/// <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);
        }
Example #2
0
        /// <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);
            }
        }
Example #4
0
        /// <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);
        }
Example #5
0
        /// <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);
        }
Example #6
0
        /// <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();
     }
 }
Example #8
0
        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);
        }
Example #9
0
        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;
        }
Example #11
0
        /// <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);
 }