/// <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> /// 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); }
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 capture on/capture off context.HandleStatusResponse(statusResponse); if (context.IsCaptureOn) { context.NextState = new BeaconSendingCaptureOnState(); } else { context.NextState = new BeaconSendingCaptureOffState(); } context.InitCompleted(true); } }
/// <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 StatusResponse SendFinishedSessions(IBeaconSendingContext context) { StatusResponse statusResponse = null; // check if there's finished Sessions to be sent -> immediately send beacon(s) of finished Sessions var finishedSessions = context.FinishedAndConfiguredSessions; foreach (var session in finishedSessions) { if (session.IsDataSendingAllowed) { statusResponse = session.SendBeacon(context.HTTPClientProvider); 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> /// 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 IStatusResponse SendStatusRequest(IBeaconSendingContext context, int numRetries, int initialRetryDelayInMillis) { IStatusResponse statusResponse; var sleepTimeInMillis = initialRetryDelayInMillis; var retry = 0; while (true) { statusResponse = context.GetHttpClient().SendStatusRequest(context); 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 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(); } }
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 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; }
/// <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); }
public void IsSuccessfulResponseReturnsFalseIfResponseIsNull() { // when, then Assert.That(BeaconSendingResponseUtil.IsSuccessfulResponse(null), Is.False); }