示例#1
0
        public void Execute(bool force)
        {
            if (RefreshStatus == WebCallRefreshStatus.WaitingForRefreshCallback)
            {
                if (!force)
                {
                    return;
                }
                RefreshStatus = WebCallRefreshStatus.NotRefreshing;
            }
            if (force)
            {
                this.OnUnqueued(this, new WebCallUnqueuedEventArgs());
            }
            SessionDocument sessionDocument = database.GetSessionDocument(swid);

            if (!force && sessionDocument != null && sessionDocument.ProtocolVersion != 3)
            {
                this.OnUnauthorized(this, new WebCallUnauthorizedEventArgs("UNAUTHORIZED_MIX_SESSION"));
                return;
            }
            numAttempts++;
            bodyBytes       = Encoding.UTF8.GetBytes(body);
            bodyBytes       = WebCallEncryptor.Encrypt(bodyBytes);
            wwwCall         = wwwCallFactory.Create(uri, method, bodyBytes, headers, latencyWwwCallTimeout, maxWwwCallTimeout);
            wwwCall.OnDone += HandleWwwDone;
            wwwCall.Execute();
            logger.Debug(HttpLogBuilder.BuildRequestLog(wwwCall.RequestId, uri, method, headers, body));
        }
示例#2
0
 private static void HandleGetStateSuccess(AbstractLogger logger, IEpochTime epochTime, IDatabase database, IUserDatabase userDatabase, INotificationQueue notificationQueue, GetStateResponse response, IMixWebCallFactory mixWebCallFactory, Action <GetStateResponse> successCallback, Action failureCallback)
 {
     try
     {
         if (ValidateResponse(response))
         {
             epochTime.ReferenceTime = response.Timestamp.Value;
             database.SetServerTimeOffsetMillis((long)epochTime.Offset.TotalMilliseconds);
             logger.Debug("New time offset: " + epochTime.Offset);
             successCallback(response);
             notificationQueue.LatestSequenceNumber = response.NotificationSequenceCounter.Value;
         }
         else
         {
             logger.Critical("Error validating get state response: " + JsonParser.ToJson(response));
             failureCallback();
             notificationQueue.Clear();
         }
     }
     catch (Exception ex)
     {
         logger.Critical("Unhandled exception: " + ex);
         failureCallback();
         notificationQueue.Clear();
     }
 }
示例#3
0
        public IInternalSession Create(string swid)
        {
            byte[] localStorageKey = keychain.LocalStorageKey;
            IDocumentCollection <AlertDocument>            documentCollection  = GetDocumentCollection <AlertDocument>(swid, "Alerts", databaseDirectoryCreator, localStorageKey, documentCollectionFactory);
            IDocumentCollection <FriendDocument>           documentCollection2 = GetDocumentCollection <FriendDocument>(swid, "Friends", databaseDirectoryCreator, localStorageKey, documentCollectionFactory);
            IDocumentCollection <FriendInvitationDocument> documentCollection3 = GetDocumentCollection <FriendInvitationDocument>(swid, "FriendInvitations", databaseDirectoryCreator, localStorageKey, documentCollectionFactory);
            IDocumentCollection <UserDocument>             documentCollection4 = GetDocumentCollection <UserDocument>(swid, "Users", databaseDirectoryCreator, localStorageKey, documentCollectionFactory);

            databaseCorruptionHandler.Add(documentCollection4);
            string       dirPath      = BuildDocCollectionPath(databaseDirectoryCreator, swid);
            UserDatabase userDatabase = new UserDatabase(documentCollection, documentCollection2, documentCollection3, documentCollection4, localStorageKey, dirPath, epochTime, documentCollectionFactory, databaseCorruptionHandler, coroutineManager);

            database.ClearServerTimeOffsetMillis();
            epochTime.OffsetMilliseconds = database.GetServerTimeOffsetMillis() ?? 0;
            logger.Debug("Initial time offset: " + epochTime.Offset);
            SessionDocument sessionDocument = database.GetSessionDocument(swid);

            keychain.PushNotificationKey = sessionDocument.CurrentSymmetricEncryptionKey;
            IWebCallEncryptor      webCallEncryptor      = webCallEncryptorFactory.Create(sessionDocument.CurrentSymmetricEncryptionKey, sessionDocument.SessionId);
            IGuestControllerClient guestControllerClient = guestControllerClientFactory.Create(swid);
            ISessionRefresher      sessionRefresher      = sessionRefresherFactory.Create(mixSessionStarter, guestControllerClient);
            IMixWebCallFactory     mixWebCallFactory     = mixWebCallFactoryFactory.Create(webCallEncryptor, swid, sessionDocument.GuestControllerAccessToken, sessionRefresher);

            guestControllerClient.OnAccessTokenChanged += delegate(object sender, AbstractGuestControllerAccessTokenChangedEventArgs e)
            {
                mixWebCallFactory.GuestControllerAccessToken = e.GuestControllerAccessToken;
            };
            AssetLoader             assetLoader           = new AssetLoader(logger, wwwCallFactory);
            IList <IInternalFriend> friends               = CreateFriends(userDatabase);
            AgeBandType             ageBandType           = AgeBandTypeConverter.Convert(sessionDocument.AgeBand);
            DateTime               lastRefreshTime        = epochTime.FromSeconds(sessionDocument.LastProfileRefreshTime);
            RegistrationProfile    registrationProfile    = new RegistrationProfile(logger, sessionDocument.DisplayNameText, sessionDocument.ProposedDisplayName, sessionDocument.ProposedDisplayNameStatus, sessionDocument.FirstName, sessionDocument.AccountStatus, lastRefreshTime, sessionDocument.CountryCode);
            GetStateResponseParser getStateResponseParser = new GetStateResponseParser(logger);
            NotificationPoller     notificationPoller     = new NotificationPoller(logger, mixWebCallFactory, notificationQueue, pollCountdownStopwatch, getStateResponseParser, epochTime, random, database, swid);
            DisplayName            displayName            = new DisplayName(sessionDocument.DisplayNameText);
            LocalUser              localUser              = new LocalUser(logger, displayName, swid, friends, ageBandType, database, userDatabase, registrationProfile, mixWebCallFactory, guestControllerClient, notificationQueue, encryptor, epochTime);
            Session session = new Session(logger, localUser, sessionDocument.GuestControllerAccessToken, sessionDocument.PushNotificationToken != null, notificationPoller, coroutineManager, database, userDatabase, guestControllerClient, mixWebCallFactory, epochTime, databaseCorruptionHandler, sessionStatus, keychain, getStateResponseParser, clientVersion, notificationQueue);

            try
            {
                NotificationHandler.Handle(notificationDispatcher, userDatabase, localUser, epochTime);
                notificationQueue.LatestSequenceNumber = sessionDocument.LatestNotificationSequenceNumber;
                IEnumerable <IncomingFriendInvitation> incomingFriendInvitations = GetIncomingFriendInvitations(userDatabase, localUser);
                foreach (IncomingFriendInvitation item in incomingFriendInvitations)
                {
                    localUser.AddIncomingFriendInvitation(item);
                }
                IEnumerable <OutgoingFriendInvitation> outgoingFriendInvitations = GetOutgoingFriendInvitations(userDatabase, localUser);
                foreach (OutgoingFriendInvitation item2 in outgoingFriendInvitations)
                {
                    localUser.AddOutgoingFriendInvitation(item2);
                }
            }
            catch (Exception)
            {
                session.Dispose();
                throw;
            }
            return(session);
        }
示例#4
0
        public static void Resume(AbstractLogger logger, IGetStateResponseParser getStateResponseParser, IEpochTime epochTime, string clientVersion, INotificationQueue notificationQueue, IMixWebCallFactory mixWebCallFactory, IInternalLocalUser localUser, IDatabase database, IUserDatabase userDatabase, INotificationPoller notificationPoller, Action <IResumeSessionResult> callback)
        {
            epochTime.OffsetMilliseconds = database.GetServerTimeOffsetMillis() ?? 0;
            logger.Debug("Initial time offset: " + epochTime.Offset);
            SessionDocument sessionDocument      = database.GetSessionDocument(localUser.Swid);
            long            lastNotificationTime = sessionDocument.LastNotificationTime;

            StateGetter.GetState(logger, epochTime, clientVersion, database, userDatabase, notificationQueue, mixWebCallFactory, localUser.Swid, lastNotificationTime, delegate(GetStateResponse response)
            {
                HandleGetStateSuccess(logger, getStateResponseParser, response, mixWebCallFactory, localUser, userDatabase, notificationPoller, callback);
            }, delegate
            {
                callback(new ResumeSessionResult(success: false));
            });
        }
示例#5
0
 public static void Search(AbstractLogger logger, IMixWebCallFactory mixWebCallFactory, string displayName, IUserDatabase userDatabase, Action <IInternalUnidentifiedUser> successCallback, Action failureCallback)
 {
     try
     {
         DisplayNameSearchRequest displayNameSearchRequest = new DisplayNameSearchRequest();
         displayNameSearchRequest.DisplayName = displayName;
         DisplayNameSearchRequest request = displayNameSearchRequest;
         IWebCall <DisplayNameSearchRequest, DisplayNameSearchResponse> webCall = mixWebCallFactory.SearchDisplaynamePost(request);
         webCall.OnResponse += delegate(object sender, WebCallEventArgs <DisplayNameSearchResponse> e)
         {
             DisplayNameSearchResponse response = e.Response;
             if (ValidateResponse(response))
             {
                 userDatabase.InsertUserDocument(new UserDocument
                 {
                     DisplayName = response.DisplayName,
                     FirstName   = response.FirstName,
                     Swid        = null,
                     HashedSwid  = null
                 });
                 IInternalUnidentifiedUser obj = RemoteUserFactory.CreateUnidentifiedUser(response.DisplayName, response.FirstName, userDatabase);
                 successCallback(obj);
             }
             else
             {
                 logger.Critical("Failed to validate display name search response: " + JsonParser.ToJson(response));
                 failureCallback();
             }
         };
         webCall.OnError += delegate(object sender, WebCallErrorEventArgs e)
         {
             logger.Debug("Failed to find user: "******"Unhandled exception: " + arg);
         failureCallback();
     }
 }
示例#6
0
        private static void HandleResult(WwwCallResult result, string log, byte[] bytes, Uri uri, StringBuilder timeoutLogs, int numAttempts, Action <LoadAssetResult> callback, Action <Uri, Action <LoadAssetResult>, StringBuilder, int> retry, AbstractLogger logger)
        {
            switch (result)
            {
            case WwwCallResult.Success:
                logger.Debug(log);
                callback(new LoadAssetResult(success: true, bytes));
                break;

            case WwwCallResult.TooManyTimeoutsError:
                logger.Critical("Too many timeouts: " + uri.AbsoluteUri + "\nPrevious logs:\n" + timeoutLogs);
                callback(new LoadAssetResult(success: false, null));
                break;

            case WwwCallResult.TooManyServerErrors:
                logger.Critical("Too many server errors:\n" + log);
                callback(new LoadAssetResult(success: false, null));
                break;

            case WwwCallResult.RetryTimeout:
                logger.Error(log);
                timeoutLogs.Append(log);
                timeoutLogs.Append("\n\n");
                retry(uri, callback, timeoutLogs, numAttempts + 1);
                break;

            case WwwCallResult.RetryServerError:
                logger.Error(log);
                retry(uri, callback, timeoutLogs, numAttempts + 1);
                break;

            case WwwCallResult.OfflineError:
                logger.Error(log);
                callback(new LoadAssetResult(success: false, null));
                break;

            case WwwCallResult.ClientError:
                logger.Critical(log);
                callback(new LoadAssetResult(success: false, null));
                break;
            }
        }
示例#7
0
        private void Load(Uri uri, Action <LoadAssetResult> callback, StringBuilder timeoutLogs, int numAttempts)
        {
            IWwwCall wwwCall = wwwCallFactory.Create(uri, HttpMethod.GET, null, emptyHeaders, 10000L, 30000L);

            logger.Debug(BuildRequestLog(uri, wwwCall.RequestId));
            EventHandler <WwwDoneEventArgs> handleOnDone = null;

            handleOnDone = delegate
            {
                wwwCall.OnDone -= handleOnDone;
                Dictionary <string, string> responseHeaders = wwwCall.ResponseHeaders;
                uint          statusCode   = wwwCall.StatusCode;
                WwwCallResult result       = GetResult(wwwCall.Error, statusCode, numAttempts);
                string        log          = BuildResponseLog(uri, result, responseHeaders, statusCode, wwwCall);
                byte[]        responseBody = wwwCall.ResponseBody;
                HandleResult(result, log, responseBody, uri, timeoutLogs, numAttempts, callback, Load, logger);
                wwwCall.Dispose();
            };
            wwwCall.OnDone += handleOnDone;
            wwwCall.Execute();
        }
        public static IInternalPushNotification Receive(AbstractLogger logger, IEncryptor encryptor, IDatabase database, string swid, IDictionary notification)
        {
            SessionDocument sessionDocument = database.GetSessionDocument(swid);

            byte[]      currentSymmetricEncryptionKey = sessionDocument.CurrentSymmetricEncryptionKey;
            string      optionalString = GetOptionalString(notification, "payload");
            IDictionary dictionary;

            if (optionalString == null)
            {
                dictionary = notification;
            }
            else
            {
                byte[] payloadEncryptedBytes;
                try
                {
                    payloadEncryptedBytes = Convert.FromBase64String(optionalString);
                }
                catch (Exception ex)
                {
                    throw new ArgumentException("Couldn't deserialize push notification: " + ex);
                }
                try
                {
                    dictionary = DecryptPayload(encryptor, currentSymmetricEncryptionKey, payloadEncryptedBytes);
                }
                catch (Exception ex)
                {
                    byte[] previousSymmetricEncryptionKey = sessionDocument.PreviousSymmetricEncryptionKey;
                    if (previousSymmetricEncryptionKey == null)
                    {
                        throw new ArgumentException("Couldn't decrypt push notification: " + ex);
                    }
                    try
                    {
                        dictionary = DecryptPayload(encryptor, previousSymmetricEncryptionKey, payloadEncryptedBytes);
                    }
                    catch (Exception ex2)
                    {
                        throw new ArgumentException(string.Concat("Couldn't decrypt push notification: ", ex, "\n", ex2));
                    }
                }
            }
            StringBuilder stringBuilder = new StringBuilder("Received push notification with payload:\n");

            foreach (DictionaryEntry item in dictionary)
            {
                stringBuilder.Append(item.Key);
                stringBuilder.Append(" = ");
                stringBuilder.Append(item.Value);
                stringBuilder.Append('\n');
            }
            logger.Debug(stringBuilder.ToString());
            string optionalString2 = GetOptionalString(dictionary, "type");

            if (optionalString2 == null)
            {
                throw new ArgumentException("Push notification doesn't have a type");
            }
            string optionalString3        = GetOptionalString(dictionary, "notifications_available");
            bool   notificationsAvailable = optionalString3 == null || optionalString3 == "true";

            switch (optionalString2)
            {
            case "FRIENDSHIP_INVITATION_MESSAGE":
            {
                string optionalString5 = GetOptionalString(dictionary, "friendshipInvitationId");
                return(new FriendshipInvitationReceivedPushNotification(notificationsAvailable, optionalString5));
            }

            case "FRIENDSHIP_MESSAGE":
            {
                string optionalString4 = GetOptionalString(dictionary, "friendId");
                return(new FriendshipAddedPushNotification(notificationsAvailable, optionalString4));
            }

            case "BROADCAST":
                return(new BroadcastPushNotification(notificationsAvailable));

            case "ADD_ALERT":
                return(new AlertAddedPushNotification(notificationsAvailable));

            case "CLEAR_ALERT":
                return(new AlertsClearedPushNotification(notificationsAvailable));

            case "REMOVE_FRIENDSHIP_INVITATION":
                return(new FriendshipInvitationRemovedPushNotification(notificationsAvailable));

            case "REMOVE_FRIENDSHIP_TRUST":
                return(new UntrustedPushNotification(notificationsAvailable));

            default:
                return(new GenericPushNotification(notificationsAvailable));
            }
        }
    private void ExecuteNextCall()
    {
        if (isExecuting || queue.Count == 0)
        {
            return;
        }
        QueueItem item = queue[0];
        Dictionary <string, string> dictionary = new Dictionary <string, string>();

        dictionary.Add("Content-Type", "application/json");
        dictionary.Add("Accept", "application/json");
        dictionary.Add("Cache-Control", "no-cache,no-store,must-revalidate");
        Dictionary <string, string> dictionary2 = dictionary;

        if (spoofedIpAddress != null)
        {
            dictionary2["X-Forwarded-For"] = spoofedIpAddress;
        }
        switch (item.AuthType)
        {
        case GuestControllerAuthenticationType.ApiKey:
        {
            string guestControllerApiKey = database.GetGuestControllerApiKey();
            if (guestControllerApiKey == null)
            {
                GetApiKeyImmediately(delegate(GuestControllerResult <GuestControllerWebCallResponse> r)
                    {
                        if (!r.Success)
                        {
                            logger.Critical("Getting API key failed");
                            queue.Remove(item);
                            CallCallbackWithFailureResult(item, null, null);
                        }
                        ExecuteNextCall();
                    });
                return;
            }
            dictionary2["Authorization"] = "APIKEY " + guestControllerApiKey;
            break;
        }

        case GuestControllerAuthenticationType.AccessToken:
        {
            SessionDocument sessionDocument = database.GetSessionDocument(swid);
            if (sessionDocument == null)
            {
                logger.Critical("Guest Controller web call requires access token, but no session document found");
                queue.Remove(item);
                CallCallbackWithFailureResult(item, null, null);
                ExecuteNextCall();
                return;
            }
            string guestControllerAccessToken = sessionDocument.GuestControllerAccessToken;
            if (guestControllerAccessToken == null)
            {
                logger.Critical("Guest Controller web call requires access token, session document doesn't have one");
                queue.Remove(item);
                CallCallbackWithFailureResult(item, null, null);
                ExecuteNextCall();
                return;
            }
            dictionary2["Authorization"] = "BEARER " + guestControllerAccessToken;
            break;
        }
        }
        item.NumAttempts++;
        isExecuting = true;
        IWwwCall wwwCall = wwwCallFactory.Create(item.Uri, item.HttpMethod, item.RequestBody, dictionary2, 60000L, 70000L);

        logger.Debug(HttpLogBuilder.BuildRequestLog(wwwCall.RequestId, item.Uri, item.HttpMethod, dictionary2, item.RequestBodyJson));
        wwwCalls.Add(wwwCall);
        wwwCall.OnDone += delegate
        {
            isExecuting = false;
            Dictionary <string, string> responseHeaders = new Dictionary <string, string>(wwwCall.ResponseHeaders);
            string error                            = wwwCall.Error;
            int    requestId                        = wwwCall.RequestId;
            long   timeToStartUpload                = wwwCall.TimeToStartUpload;
            long   timeToFinishUpload               = wwwCall.TimeToFinishUpload;
            float  percentUploaded                  = wwwCall.PercentUploaded;
            long   timeToStartDownload              = wwwCall.TimeToStartDownload;
            long   timeToFinishDownload             = wwwCall.TimeToFinishDownload;
            float  percentDownloaded                = wwwCall.PercentDownloaded;
            string timeoutReason                    = wwwCall.TimeoutReason;
            long   timeoutTime                      = wwwCall.TimeoutTime;
            uint   statusCode                       = wwwCall.StatusCode;
            string responseText                     = (wwwCall.ResponseBody == null) ? string.Empty : stringEncoding.GetString(wwwCall.ResponseBody);
            GuestControllerWebCallResponse response = item.ResponseParser(responseText);
            if (response == null)
            {
                CallCallbackWithFailureResult(item, null, responseHeaders);
            }
            else
            {
                wwwCalls.Remove(wwwCall);
                wwwCall.Dispose();
                if (!string.IsNullOrEmpty(error))
                {
                    string text = error.ToLower();
                    if (text == "couldn't connect to host" || text.Contains("timedout") || text.Contains("timed out"))
                    {
                        string text2 = HttpLogBuilder.BuildTimeoutLog(requestId, item.Uri, item.HttpMethod, responseHeaders, responseText, timeToStartUpload, timeToFinishUpload, percentUploaded, timeToStartDownload, timeToFinishDownload, percentDownloaded, timeoutReason, timeoutTime);
                        item.TimeoutLogs.Append(text2);
                        item.TimeoutLogs.Append("\n\n");
                        logger.Error(text2);
                        if (item.NumAttempts > 3)
                        {
                            logger.Critical("Too many timeouts: " + item.Uri.AbsoluteUri + "\nPrevious logs:\n" + item.TimeoutLogs);
                            queue.Remove(item);
                            CallCallbackWithFailureResult(item, response, responseHeaders);
                        }
                        ExecuteNextCall();
                        return;
                    }
                }
                if (statusCode >= 500 && statusCode <= 599)
                {
                    string text2 = HttpLogBuilder.BuildResponseLog(requestId, item.Uri, item.HttpMethod, responseHeaders, responseText, statusCode);
                    if (item.NumAttempts > 1)
                    {
                        logger.Critical(text2);
                        logger.Critical("Too many server errors");
                        queue.Remove(item);
                        CallCallbackWithFailureResult(item, response, responseHeaders);
                    }
                    else
                    {
                        logger.Error(text2);
                    }
                    ExecuteNextCall();
                }
                else if (HasErrorCode(response, "API_KEY_INVALID"))
                {
                    logger.Error(HttpLogBuilder.BuildResponseLog(requestId, item.Uri, item.HttpMethod, responseHeaders, responseText, statusCode));
                    if (item.RefreshedApiKey)
                    {
                        logger.Critical("Invalid API key. Already got new token once. Giving up.");
                        queue.Remove(item);
                        CallCallbackWithFailureResult(item, response, responseHeaders);
                        ExecuteNextCall();
                    }
                    else
                    {
                        logger.Error("Invalid API key. Getting new API key...");
                        GetApiKeyImmediately(delegate(GuestControllerResult <GuestControllerWebCallResponse> r)
                        {
                            if (!r.Success)
                            {
                                logger.Critical(HttpLogBuilder.BuildResponseLog(requestId, item.Uri, item.HttpMethod, responseHeaders, responseText, statusCode));
                                logger.Critical("Getting new API key failed.");
                                queue.Remove(item);
                                CallCallbackWithFailureResult(item, response, responseHeaders);
                            }
                            ExecuteNextCall();
                        });
                    }
                }
                else if (HasErrorCode(response, "RATE_LIMITED"))
                {
                    logger.Error(HttpLogBuilder.BuildResponseLog(requestId, item.Uri, item.HttpMethod, responseHeaders, responseText, statusCode));
                    if (item.RefreshedApiKey)
                    {
                        logger.Critical("Couldn't get new API key. Already got new API key once. Giving up.");
                        queue.Remove(item);
                        CallCallbackWithFailureResult(item, response, responseHeaders);
                        ExecuteNextCall();
                    }
                    else
                    {
                        logger.Error("Rate limited. Getting new API key...");
                        GetApiKeyImmediately(delegate(GuestControllerResult <GuestControllerWebCallResponse> r)
                        {
                            if (!r.Success || HasErrorCode(r.Response, "RATE_LIMITED"))
                            {
                                logger.Critical(HttpLogBuilder.BuildResponseLog(requestId, item.Uri, item.HttpMethod, responseHeaders, responseText, statusCode));
                                logger.Critical("Getting new API key failed.");
                                queue.Remove(item);
                                CallCallbackWithFailureResult(item, response, responseHeaders);
                            }
                            ExecuteNextCall();
                        });
                    }
                }
                else if (HasErrorCode(response, "ACCESS_TOKEN_NOT_FOUND", "AUTHORIZATION_INVALID_OR_EXPIRED_TOKEN"))
                {
                    logger.Error(HttpLogBuilder.BuildResponseLog(requestId, item.Uri, item.HttpMethod, responseHeaders, responseText, statusCode));
                    if (item.RefreshedAccessToken)
                    {
                        logger.Critical("Invalid access token. Already tried to refresh. Giving up.");
                        queue.Remove(item);
                        CallCallbackWithFailureResult(item, response, responseHeaders);
                        ExecuteNextCall();
                    }
                    else
                    {
                        logger.Error("Invalid access token. Refreshing...");
                        RefreshImmediately(delegate(GuestControllerResult <RefreshResponse> r)
                        {
                            if (!r.Success)
                            {
                                logger.Critical(HttpLogBuilder.BuildResponseLog(requestId, item.Uri, item.HttpMethod, responseHeaders, responseText, statusCode));
                                logger.Critical("Refreshing access token failed");
                                queue.Remove(item);
                                CallCallbackWithFailureResult(item, response, responseHeaders);
                            }
                            ExecuteNextCall();
                        });
                    }
                }
                else if (statusCode >= 401 && statusCode <= 499)
                {
                    logger.Critical(HttpLogBuilder.BuildResponseLog(requestId, item.Uri, item.HttpMethod, responseHeaders, responseText, statusCode));
                    queue.Remove(item);
                    CallCallbackWithFailureResult(item, response, responseHeaders);
                    ExecuteNextCall();
                }
                else if (statusCode == 400 || (statusCode >= 200 && statusCode <= 299))
                {
                    logger.Debug(HttpLogBuilder.BuildResponseLog(requestId, item.Uri, item.HttpMethod, responseHeaders, responseText, statusCode));
                    queue.Remove(item);
                    item.Callback(new GuestControllerResult <GuestControllerWebCallResponse>(success: true, response, responseHeaders));
                    ExecuteNextCall();
                }
                else
                {
                    if (!string.IsNullOrEmpty(error))
                    {
                        string text = error.ToLower();
                        if (text.Contains("connection appears to be offline") || text.Contains("connection was lost") || text.Contains("network is unreachable"))
                        {
                            logger.Critical(HttpLogBuilder.BuildResponseLog(requestId, item.Uri, item.HttpMethod, responseHeaders, responseText, statusCode));
                            logger.Critical("Offline error = " + error);
                        }
                        else
                        {
                            logger.Critical(HttpLogBuilder.BuildResponseLog(requestId, item.Uri, item.HttpMethod, responseHeaders, responseText, statusCode));
                            logger.Critical("Other web call error = " + error);
                        }
                    }
                    queue.Remove(item);
                    CallCallbackWithFailureResult(item, response, responseHeaders);
                    ExecuteNextCall();
                }
            }
        };
        wwwCall.Execute();
    }