Пример #1
0
        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);
        }
Пример #4
0
        /// <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);
        }
Пример #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 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);
        }
Пример #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);
        }
Пример #7
0
        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();
        }
Пример #9
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 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);
        }
Пример #10
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);
        }
Пример #11
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);
        }
Пример #12
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);
        }
Пример #13
0
        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);
        }
Пример #14
0
        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;
        }
Пример #19
0
        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);
            }
        }
Пример #21
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);
        }
        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);
 }