protected override void DoExecute(IBeaconSendingContext context) { var statusResponse = ExecuteStatusRequest(context); if (context.IsShutdownRequested) { // shutdown was requested -> abort init with failure // transition to shutdown state is handled by base class context.InitCompleted(false); } else if (BeaconSendingResponseUtil.IsSuccessfulResponse(statusResponse)) { // success -> continue with capture on/capture off context.HandleStatusResponse(statusResponse); if (context.IsCaptureOn) { context.NextState = new BeaconSendingCaptureOnState(); } else { context.NextState = new BeaconSendingCaptureOffState(); } context.InitCompleted(true); } }
private static void HandleStatusResponse(IBeaconSendingContext context, StatusResponse statusResponse) { if (statusResponse != null) { // handle status response, even if it's erroneous // if it's an erroneous response capturing is disabled context.HandleStatusResponse(statusResponse); } // if initial time sync failed before if (BeaconSendingResponseUtil.IsTooManyRequestsResponse(statusResponse)) { // received "too many requests" response // in this case stay in capture off state and use the retry-after delay for sleeping context.NextState = new BeaconSendingCaptureOffState(statusResponse.GetRetryAfterInMilliseconds()); } else if (context.IsTimeSyncSupported && !context.IsTimeSynced) { // if initial time sync failed before, then retry initial time sync context.NextState = new BeaconSendingTimeSyncState(true); } else if (BeaconSendingResponseUtil.IsSuccessfulResponse(statusResponse) && context.IsCaptureOn) { // capturing is re-enabled again, but only if we received a response from the server context.NextState = new BeaconSendingCaptureOnState(); } }
/// <summary> /// Execute status requests, until a successful response was received or shutdown was requested. /// </summary> /// <param name="context">The state's context</param> /// <returns>The last received status response, which might be erroneous if shutdown has been requested.</returns> private StatusResponse ExecuteStatusRequest(IBeaconSendingContext context) { StatusResponse statusResponse; while (true) { var currentTimestamp = context.CurrentTimestamp; context.LastOpenSessionBeaconSendTime = currentTimestamp; context.LastStatusCheckTime = currentTimestamp; statusResponse = BeaconSendingRequestUtil.SendStatusRequest(context, MAX_INITIAL_STATUS_REQUEST_RETRIES, INITIAL_RETRY_SLEEP_TIME_MILLISECONDS); if (context.IsShutdownRequested || BeaconSendingResponseUtil.IsSuccessfulResponse(statusResponse)) { // shutdown was requested or a successful status response was received break; } var sleepTime = REINIT_DELAY_MILLISECONDS[reinitializeDelayIndex]; if (BeaconSendingResponseUtil.IsTooManyRequestsResponse(statusResponse)) { // in case of too many requests the server might send us a retry-after sleepTime = statusResponse.GetRetryAfterInMilliseconds(); // also temporarily disable capturing to avoid further server overloading context.DisableCapture(); } // status request needs to be sent again after some delay context.Sleep(sleepTime); reinitializeDelayIndex = Math.Min(reinitializeDelayIndex + 1, REINIT_DELAY_MILLISECONDS.Length - 1); // ensure no out of bounds } return(statusResponse); }
/// <summary> /// Check if the send interval (configured by server) has expired and start to send open sessions if it has expired. /// </summary> /// <param name="context">The state's context</param> /// <returns>The last status response received.</returns> private IStatusResponse SendOpenSessions(IBeaconSendingContext context) { IStatusResponse statusResponse = null; var currentTimestamp = context.CurrentTimestamp; if (currentTimestamp <= context.LastOpenSessionBeaconSendTime + context.SendInterval) { return(null); // some time left until open sessions need to be sent } var openSessions = context.GetAllOpenAndConfiguredSessions(); foreach (var session in openSessions) { if (session.IsDataSendingAllowed) { statusResponse = session.SendBeacon(context.HttpClientProvider, context); if (BeaconSendingResponseUtil.IsTooManyRequestsResponse(statusResponse)) { // server is currently overloaded, return immediately break; } } else { session.ClearCapturedData(); } } // update open session send timestamp context.LastOpenSessionBeaconSendTime = currentTimestamp; return(statusResponse); }
/// <summary> /// Send a status request to the server and try to get the status response. /// </summary> /// <param name="context">Used to retrieve the <see cref="IHTTPClient"/> and for delaying methods.</param> /// <param name="numRetries">The number of retries (total number of tries = numRetries + 1)</param> /// <param name="initialRetryDelayInMillis">The initial delay which is doubled between one unsuccessful attempt and the next retry.</param> /// <returns> A status response or <code>null</code> if shutdown was requested or number of retries was reached.</returns> internal static StatusResponse SendStatusRequest(IBeaconSendingContext context, int numRetries, int initialRetryDelayInMillis) { StatusResponse statusResponse = null; var sleepTimeInMillis = initialRetryDelayInMillis; var retry = 0; while (true) { statusResponse = context.GetHTTPClient().SendStatusRequest(); if (BeaconSendingResponseUtil.IsSuccessfulResponse(statusResponse) || BeaconSendingResponseUtil.IsTooManyRequestsResponse(statusResponse) || // is handled by the states retry >= numRetries || context.IsShutdownRequested) { break; } // if no (valid) status response was received -> sleep and double the delay for each retry context.Sleep(sleepTimeInMillis); sleepTimeInMillis *= 2; retry++; } return(statusResponse); }
/// <summary> /// Send new session requests for all sessions where we currently don't have a multiplicity configuration. /// </summary> /// <param name="context">The state context.</param> /// <returns>The last status response received.</returns> private IStatusResponse SendNewSessionRequests(IBeaconSendingContext context) { IStatusResponse statusResponse = null; var notConfiguredSessions = context.GetAllNotConfiguredSessions(); foreach (var session in notConfiguredSessions) { if (!session.CanSendNewSessionRequest) { // already exceeded the maximum number of session requests, disable any further data collecting session.DisableCapture(); continue; } statusResponse = context.GetHttpClient().SendNewSessionRequest(context); if (BeaconSendingResponseUtil.IsSuccessfulResponse(statusResponse)) { var updatedAttributes = context.UpdateFrom(statusResponse); var newConfiguration = ServerConfiguration.From(updatedAttributes); session.UpdateServerConfiguration(newConfiguration); } else if (BeaconSendingResponseUtil.IsTooManyRequestsResponse(statusResponse)) { // server is currently overloaded, return immediately break; } else { // any other unsuccessful response session.DecreaseNumRemainingSessionRequests(); } } return(statusResponse); }
private void HandleErroneousTimeSyncRequest(IBeaconSendingContext context, TimeSyncResponse response) { // if this is the initial sync try, we have to initialize the time provider // in every other case we keep the previous setting if (IsInitialTimeSync) { context.InitializeTimeSync(0, context.IsTimeSyncSupported); } if (BeaconSendingResponseUtil.IsTooManyRequestsResponse(response)) { // server is currently overloaded, change to CaptureOff state temporarily context.NextState = new BeaconSendingCaptureOffState(response.GetRetryAfterInMilliseconds()); } else if (context.IsTimeSyncSupported) { // in case of time sync failure when it's supported, go to capture off state context.NextState = new BeaconSendingCaptureOffState(); } else { // otherwise set the next state based on the configuration SetNextState(context); } }
protected override void DoExecute(IBeaconSendingContext context) { // first get all sessions that do not have any multiplicity explicitly set foreach (var session in context.GetAllNotConfiguredSessions()) { session.EnableCapture(); } // end all open sessions -> will be flushed afterwards foreach (var session in context.GetAllOpenAndConfiguredSessions()) { session.End(false); } // flush already finished (and previously ended) sessions var tooManyRequestsReceived = false; foreach (var finishedSession in context.GetAllFinishedAndConfiguredSessions()) { if (!tooManyRequestsReceived && finishedSession.IsDataSendingAllowed) { var statusResponse = finishedSession.SendBeacon(context.HttpClientProvider, context); if (BeaconSendingResponseUtil.IsTooManyRequestsResponse(statusResponse)) { tooManyRequestsReceived = true; } } finishedSession.ClearCapturedData(); context.RemoveSession(finishedSession); } // make last state transition to terminal state context.NextState = new BeaconSendingTerminalState(); }
/// <summary> /// Send all sessions which have been finished previously. /// </summary> /// <param name="context">The state's context</param> /// <returns>The last status response received.</returns> private IStatusResponse SendFinishedSessions(IBeaconSendingContext context) { IStatusResponse statusResponse = null; // check if there's finished Sessions to be sent -> immediately send beacon(s) of finished Sessions var finishedSessions = context.GetAllFinishedAndConfiguredSessions(); foreach (var session in finishedSessions) { if (session.IsDataSendingAllowed) { statusResponse = session.SendBeacon(context.HttpClientProvider, context); if (!BeaconSendingResponseUtil.IsSuccessfulResponse(statusResponse)) { // something went wrong, if (BeaconSendingResponseUtil.IsTooManyRequestsResponse(statusResponse) || !session.IsEmpty) { break; // sending did not work, break out for now and retry it later } } } // session was sent - so remove it from beacon cache context.RemoveSession(session); session.ClearCapturedData(); } return(statusResponse); }
/// <summary> /// Execute status requests, until a successful response was received or shutdown was requested. /// </summary> /// <param name="context">The state's context</param> /// <returns>The last received status response, which might be erroneous if shutdown has been requested.</returns> private IStatusResponse ExecuteStatusRequest(IBeaconSendingContext context) { IStatusResponse statusResponse; while (true) { var currentTimestamp = context.CurrentTimestamp; context.LastOpenSessionBeaconSendTime = currentTimestamp; context.LastStatusCheckTime = currentTimestamp; statusResponse = BeaconSendingRequestUtil.SendStatusRequest(context, MaxInitialStatusRequestRetries, InitialRetrySleepTimeMilliseconds); if (context.IsShutdownRequested || BeaconSendingResponseUtil.IsSuccessfulResponse(statusResponse)) { // shutdown was requested or a successful status response was received break; } var sleepTime = ReInitDelayMilliseconds[reinitializeDelayIndex]; if (BeaconSendingResponseUtil.IsTooManyRequestsResponse(statusResponse)) { // in case of too many requests the server might send us a retry-after sleepTime = statusResponse.GetRetryAfterInMilliseconds(); // also temporarily disable capturing to avoid further server overloading context.DisableCaptureAndClear(); } // status request needs to be sent again after some delay context.Sleep(sleepTime); reinitializeDelayIndex = Math.Min(reinitializeDelayIndex + 1, ReInitDelayMilliseconds.Length - 1); // ensure no out of bounds } return(statusResponse); }
private TimeSyncRequestsResponse ExecuteTimeSyncRequests(IBeaconSendingContext context) { var response = new TimeSyncRequestsResponse(); var retry = 0; var sleepTimeInMillis = INITIAL_RETRY_SLEEP_TIME_MILLISECONDS; // no check for shutdown here, time sync has to be completed while (response.timeSyncOffsets.Count < TIME_SYNC_REQUESTS && !context.IsShutdownRequested) { // doExecute time-sync request and take timestamps var requestSendTime = context.CurrentTimestamp; var timeSyncResponse = context.GetHTTPClient().SendTimeSyncRequest(); var responseReceiveTime = context.CurrentTimestamp; if (BeaconSendingResponseUtil.IsSuccessfulResponse(timeSyncResponse)) { var requestReceiveTime = timeSyncResponse.RequestReceiveTime; var responseSendTime = timeSyncResponse.ResponseSendTime; // check both timestamps for being > 0 if ((requestReceiveTime > 0) && (responseSendTime > 0)) { // if yes -> continue time-sync var offset = ((requestReceiveTime - requestSendTime) + (responseSendTime - responseReceiveTime)) / 2; response.timeSyncOffsets.Add(offset); retry = 0; // on successful response reset the retry count & initial sleep time sleepTimeInMillis = INITIAL_RETRY_SLEEP_TIME_MILLISECONDS; } else { // if no -> stop time sync, it's not supported context.DisableTimeSyncSupport(); break; } } else if (retry >= TIME_SYNC_RETRY_COUNT) { // retry limit reached break; } else if (BeaconSendingResponseUtil.IsTooManyRequestsResponse(timeSyncResponse)) { // special handling for too many requests // clear all time sync offsets captured so far and store the response, which is handled later response.timeSyncOffsets.Clear(); response.response = timeSyncResponse; break; } else { context.Sleep(sleepTimeInMillis); sleepTimeInMillis *= 2; retry++; } } return(response); }
public void IsSuccessfulResponseReturnsTrueIfResponseIsNotErroneous() { // given var successfulResponse = new StatusResponse(Substitute.For <ILogger>(), string.Empty, Response.HttpOk, new Dictionary <string, List <string> >()); // when, then Assert.That(BeaconSendingResponseUtil.IsSuccessfulResponse(successfulResponse), Is.True); }
public void IsTooManyRequestsResponseReturnsTrueIfResponseCodeIndicatesTooManyRequests() { // given var erroneousResponse = new StatusResponse(Substitute.For <ILogger>(), string.Empty, Response.HttpTooManyRequests, new Dictionary <string, List <string> >()); // when, then Assert.That(BeaconSendingResponseUtil.IsTooManyRequestsResponse(erroneousResponse), Is.True); }
public void IsTooManyRequestsResponseReturnsFalseIfResponseCodeIsNotEqualToTooManyRequestsCode() { // given var erroneousResponse = new StatusResponse(Substitute.For <ILogger>(), string.Empty, Response.HttpBadRequest, new Dictionary <string, List <string> >()); // when, then Assert.That(BeaconSendingResponseUtil.IsTooManyRequestsResponse(erroneousResponse), Is.False); }
protected override void DoExecute(IBeaconSendingContext context) { // every two hours a time sync shall be performed if (BeaconSendingTimeSyncState.IsTimeSyncRequired(context)) { // transition to time sync state. if cature on is still true after time sync we will end up in the CaputerOnState again context.NextState = new BeaconSendingTimeSyncState(); return; } context.Sleep(); // send new session request for all sessions that are new var newSessionsResponse = SendNewSessionRequests(context); if (BeaconSendingResponseUtil.IsTooManyRequestsResponse(newSessionsResponse)) { // server is currently overloaded, temporarily switch to capture off context.NextState = new BeaconSendingCaptureOffState(newSessionsResponse.GetRetryAfterInMilliseconds()); return; } // send all finished sessions var finishedSessionsResponse = SendFinishedSessions(context); if (BeaconSendingResponseUtil.IsTooManyRequestsResponse(finishedSessionsResponse)) { // server is currently overloaded, temporarily switch to capture off context.NextState = new BeaconSendingCaptureOffState(finishedSessionsResponse.GetRetryAfterInMilliseconds()); return; } // check if we need to send open sessions & do it if necessary var openSessionsResponse = SendOpenSessions(context); if (BeaconSendingResponseUtil.IsTooManyRequestsResponse(openSessionsResponse)) { // server is currently overloaded, temporarily switch to capture off context.NextState = new BeaconSendingCaptureOffState(openSessionsResponse.GetRetryAfterInMilliseconds()); return; } // collect the last status response var lastStatusResponse = newSessionsResponse; if (openSessionsResponse != null) { lastStatusResponse = openSessionsResponse; } else if (finishedSessionsResponse != null) { lastStatusResponse = finishedSessionsResponse; } // check if send interval spent -> send current beacon(s) of open Sessions HandleStatusResponse(context, lastStatusResponse); }
public void IsTooManyRequestsResponseReturnsFalseIfResponseCodeIsNotEqualToTooManyRequestsCode() { // given var response = Substitute.For <IStatusResponse>(); response.ResponseCode.Returns(StatusResponse.HttpBadRequest); // when var obtained = BeaconSendingResponseUtil.IsTooManyRequestsResponse(response); // then Assert.That(obtained, Is.False); _ = response.Received(1).ResponseCode; }
public void IsTooManyRequestsResponseReturnsTrueIfResponseCodeIndicatesTooManyRequests() { // given var response = Substitute.For <IStatusResponse>(); response.ResponseCode.Returns(StatusResponse.HttpTooManyRequests); // when var obtained = BeaconSendingResponseUtil.IsTooManyRequestsResponse(response); // then Assert.That(obtained, Is.True); _ = response.Received(1).ResponseCode; }
public void IsSuccessfulResponseReturnsTrueIfResponseIsNotErroneous() { // given var response = Substitute.For <IStatusResponse>(); response.IsErroneousResponse.Returns(false); // when var obtained = BeaconSendingResponseUtil.IsSuccessfulResponse(response); // then Assert.That(obtained, Is.True); _ = response.Received(1).IsErroneousResponse; }
protected override void DoExecute(IBeaconSendingContext context) { context.Sleep(); // send new session request for all sessions that are new var newSessionsResponse = SendNewSessionRequests(context); if (BeaconSendingResponseUtil.IsTooManyRequestsResponse(newSessionsResponse)) { // server is currently overloaded, temporarily switch to capture off context.NextState = new BeaconSendingCaptureOffState(newSessionsResponse.GetRetryAfterInMilliseconds()); return; } // send all finished sessions var finishedSessionsResponse = SendFinishedSessions(context); if (BeaconSendingResponseUtil.IsTooManyRequestsResponse(finishedSessionsResponse)) { // server is currently overloaded, temporarily switch to capture off context.NextState = new BeaconSendingCaptureOffState(finishedSessionsResponse.GetRetryAfterInMilliseconds()); return; } // check if we need to send open sessions & do it if necessary var openSessionsResponse = SendOpenSessions(context); if (BeaconSendingResponseUtil.IsTooManyRequestsResponse(openSessionsResponse)) { // server is currently overloaded, temporarily switch to capture off context.NextState = new BeaconSendingCaptureOffState(openSessionsResponse.GetRetryAfterInMilliseconds()); return; } // collect the last status response var lastStatusResponse = newSessionsResponse; if (openSessionsResponse != null) { lastStatusResponse = openSessionsResponse; } else if (finishedSessionsResponse != null) { lastStatusResponse = finishedSessionsResponse; } // check if send interval spent -> send current beacon(s) of open Sessions HandleStatusResponse(context, lastStatusResponse); }
protected override void DoExecute(IBeaconSendingContext context) { StatusResponse statusResponse = ExecuteStatusRequest(context); if (context.IsShutdownRequested) { // shutdown was requested -> abort init with failure // transition to shutdown state is handled by base class context.InitCompleted(false); } else if (BeaconSendingResponseUtil.IsSuccessfulResponse(statusResponse)) { // success -> continue with time sync context.HandleStatusResponse(statusResponse); context.NextState = new BeaconSendingTimeSyncState(true); } }
/// <summary> /// Send new session requests for all sessions where we currently don't have a multiplicity configuration. /// </summary> /// <param name="context">The state context.</param> /// <returns>The last status response received.</returns> private StatusResponse SendNewSessionRequests(IBeaconSendingContext context) { StatusResponse statusResponse = null; var newSessions = context.NewSessions; foreach (var newSession in newSessions) { if (!newSession.CanSendNewSessionRequest) { // already exceeded the maximum number of session requests, disable any further data collecting var currentConfiguration = newSession.BeaconConfiguration; var newConfiguration = new BeaconConfiguration(0, currentConfiguration.DataCollectionLevel, currentConfiguration.CrashReportingLevel); newSession.UpdateBeaconConfiguration(newConfiguration); continue; } statusResponse = context.GetHTTPClient().SendNewSessionRequest(); if (BeaconSendingResponseUtil.IsSuccessfulResponse(statusResponse)) { var currentConfiguration = newSession.BeaconConfiguration; var newConfiguration = new BeaconConfiguration(statusResponse.Multiplicity, currentConfiguration.DataCollectionLevel, currentConfiguration.CrashReportingLevel); newSession.UpdateBeaconConfiguration(newConfiguration); } else if (BeaconSendingResponseUtil.IsTooManyRequestsResponse(statusResponse)) { // server is currently overloaded, return immediately break; } else { // any other unsuccessful response newSession.DecreaseNumNewSessionRequests(); } } return(statusResponse); }
protected override void DoExecute(IBeaconSendingContext context) { // first get all sessions that do not have any multiplicity explicitely set context.NewSessions.ForEach(newSession => { var currentConfiguration = newSession.BeaconConfiguration; newSession.UpdateBeaconConfiguration( new BeaconConfiguration(1, currentConfiguration.DataCollectionLevel, currentConfiguration.CrashReportingLevel)); }); // end all open sessions -> will be flushed afterwards context.OpenAndConfiguredSessions.ForEach(openSession => { openSession.End(); }); // flush already finished (and previously ended) sessions var tooManyRequestsReceived = false; context.FinishedAndConfiguredSessions.ForEach(finishedSession => { if (!tooManyRequestsReceived && finishedSession.IsDataSendingAllowed) { var statusResponse = finishedSession.SendBeacon(context.HTTPClientProvider); if (BeaconSendingResponseUtil.IsTooManyRequestsResponse(statusResponse)) { tooManyRequestsReceived = true; } } finishedSession.ClearCapturedData(); context.RemoveSession(finishedSession); }); // make last state transition to terminal state context.NextState = new BeaconSendingTerminalState(); }
public void IsTooManyRequestsResponseReturnsFalseIfResponseIsNull() { // when, then Assert.That(BeaconSendingResponseUtil.IsTooManyRequestsResponse(null), Is.False); }
public void IsSuccessfulResponseReturnsFalseIfResponseIsNull() { // when, then Assert.That(BeaconSendingResponseUtil.IsSuccessfulResponse(null), Is.False); }