/// <summary>
        /// Handles an abnormal response when sending a HTTP request.
        /// A simple rule must be followed, if you modify the request object in a way that the abnormal response can
        /// be resolved, you must return <c>true</c>.
        /// </summary>
        public async Task<bool> HandleResponseAsync(HandleUnsuccessfulResponseArgs args)
        {
            if (args.Response.StatusCode == HttpStatusCode.Unauthorized)
            {
                return await RefreshTokenAsync(args.CancellationToken).ConfigureAwait(false);
            }

            return false;
        }
示例#2
0
 public virtual async Task <bool> HandleResponseAsync(HandleUnsuccessfulResponseArgs args)
 {
     // if the func returns true try to handle this current failed try
     if (HandleUnsuccessfulResponseFunc != null && HandleUnsuccessfulResponseFunc(args.Response))
     {
         return(await HandleAsync(args.SupportsRetry, args.CurrentFailedTry, args.CancellationToken)
                .ConfigureAwait(false));
     }
     return(false);
 }
示例#3
0
        private async Task <bool> CredentialHandleResponseAsync(HandleUnsuccessfulResponseArgs args)
        {
            var effectiveCredential = GetEffectiveCredential(args.Request);

            if (effectiveCredential is IHttpUnsuccessfulResponseHandler handler)
            {
                return(await handler.HandleResponseAsync(args).ConfigureAwait(false));
            }

            return(false);
        }
示例#4
0
 public virtual async Task<bool> HandleResponseAsync(HandleUnsuccessfulResponseArgs args)
 {
     // if the func returns true try to handle this current failed try
     if (HandleUnsuccessfulResponseFunc != null && HandleUnsuccessfulResponseFunc(args.Response))
     {
         return await HandleAsync(args.SupportsRetry, args.CurrentFailedTry, args.CancellationToken)
             .ConfigureAwait(false);
     }
     return false;
 }
 public Task<bool> HandleResponseAsync(HandleUnsuccessfulResponseArgs args)
 {
     TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
     tcs.SetResult(true);
     return tcs.Task;
 }
 public Task<bool> HandleResponseAsync(HandleUnsuccessfulResponseArgs args)
 {
     ++Calls;
     TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
     tcs.SetResult(args.Response.StatusCode.Equals(HttpStatusCode.ServiceUnavailable));
     return tcs.Task;
 }
        public async Task<bool> HandleResponseAsync(HandleUnsuccessfulResponseArgs args)
        {
            // TODO(peleyal): check WWW-Authenticate header.
            if (args.Response.StatusCode == HttpStatusCode.Unauthorized)
            {
                return !Object.Equals(Token.AccessToken, AccessMethod.GetAccessToken(args.Request))
                    || await RequestAccessTokenAsync(args.CancellationToken).ConfigureAwait(false);
            }

            return false;
        }
示例#8
0
        /// <summary>
        /// The main logic of sending a request to the server. This send method adds the User-Agent header to a request
        /// with <see cref="ApplicationName"/> and the library version. It also calls interceptors before each attempt,
        /// and unsuccessful response handler or exception handlers when abnormal response or exception occurred.
        /// </summary>
        protected override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request,
                                                                      CancellationToken cancellationToken)
        {
            var    loggable         = IsLoggingEnabled && InstanceLogger.IsDebugEnabled;
            string loggingRequestId = "";

            if (loggable)
            {
                loggingRequestId = Interlocked.Increment(ref _loggingRequestId).ToString("X8");
            }

            int triesRemaining    = NumTries;
            int redirectRemaining = NumRedirects;

            Exception lastException = null;

            // Set User-Agent header.
            var userAgent = (ApplicationName == null ? "" : ApplicationName + " ") + UserAgentSuffix;

            // TODO: setting the User-Agent won't work on Silverlight. We may need to create a special callback here to
            // set it correctly.
            request.Headers.Add("User-Agent", userAgent);
            var apiClientHeader = GoogleApiClientHeader;

            if (apiClientHeader != null)
            {
                request.Headers.Add("x-goog-api-client", apiClientHeader);
            }

            HttpResponseMessage response = null;

            do // While (triesRemaining > 0)
            {
                response?.Dispose();
                response      = null;
                lastException = null;

                // Check after previous response (if any) has been disposed of.
                cancellationToken.ThrowIfCancellationRequested();

                // We keep a local list of the interceptors, since we can't call await inside lock.
                List <IHttpExecuteInterceptor> interceptors;
                lock (executeInterceptorsLock)
                {
                    interceptors = executeInterceptors.ToList();
                }
                if (request.Properties.TryGetValue(ExecuteInterceptorKey, out var interceptorsValue) &&
                    interceptorsValue is List <IHttpExecuteInterceptor> perCallinterceptors)
                {
                    interceptors.AddRange(perCallinterceptors);
                }

                // Intercept the request.
                foreach (var interceptor in interceptors)
                {
                    await interceptor.InterceptAsync(request, cancellationToken).ConfigureAwait(false);
                }

                await CredentialInterceptAsync(request, cancellationToken).ConfigureAwait(false);

                if (loggable)
                {
                    if ((LogEvents & LogEventType.RequestUri) != 0)
                    {
                        InstanceLogger.Debug("Request[{0}] (triesRemaining={1}) URI: '{2}'", loggingRequestId, triesRemaining, request.RequestUri);
                    }
                    if ((LogEvents & LogEventType.RequestHeaders) != 0)
                    {
                        LogHeaders($"Request[{loggingRequestId}] Headers:", request.Headers, request.Content?.Headers);
                    }
                    if ((LogEvents & LogEventType.RequestBody) != 0)
                    {
                        await LogBody($"Request[{loggingRequestId}] Body: '{{0}}'", request.Content).ConfigureAwait(false);
                    }
                }
                try
                {
                    // Send the request!
                    response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
                }
                catch (Exception ex)
                {
                    lastException = ex;
                }

                // Decrease the number of retries.
                if (response == null || ((int)response.StatusCode >= 400 || (int)response.StatusCode < 200))
                {
                    triesRemaining--;
                }

                // Exception was thrown, try to handle it.
                if (response == null)
                {
                    var exceptionHandled = false;

                    // We keep a local list of the handlers, since we can't call await inside lock.
                    List <IHttpExceptionHandler> handlers;
                    lock (exceptionHandlersLock)
                    {
                        handlers = exceptionHandlers.ToList();
                    }
                    if (request.Properties.TryGetValue(ExceptionHandlerKey, out var handlersValue) &&
                        handlersValue is List <IHttpExceptionHandler> perCallHandlers)
                    {
                        handlers.AddRange(perCallHandlers);
                    }

                    // Try to handle the exception with each handler.
                    foreach (var handler in handlers)
                    {
                        exceptionHandled |= await handler.HandleExceptionAsync(new HandleExceptionArgs
                        {
                            Request           = request,
                            Exception         = lastException,
                            TotalTries        = NumTries,
                            CurrentFailedTry  = NumTries - triesRemaining,
                            CancellationToken = cancellationToken
                        }).ConfigureAwait(false);
                    }

                    if (!exceptionHandled)
                    {
                        InstanceLogger.Error(lastException,
                                             "Response[{0}] Exception was thrown while executing a HTTP request and it wasn't handled", loggingRequestId);
                        throw lastException;
                    }
                    else if (loggable && (LogEvents & LogEventType.ResponseAbnormal) != 0)
                    {
                        InstanceLogger.Debug("Response[{0}] Exception {1} was thrown, but it was handled by an exception handler",
                                             loggingRequestId, lastException.Message);
                    }
                }
                else
                {
                    if (loggable)
                    {
                        if ((LogEvents & LogEventType.ResponseStatus) != 0)
                        {
                            InstanceLogger.Debug("Response[{0}] Response status: {1} '{2}'", loggingRequestId, response.StatusCode, response.ReasonPhrase);
                        }
                        if ((LogEvents & LogEventType.ResponseHeaders) != 0)
                        {
                            LogHeaders($"Response[{loggingRequestId}] Headers:", response.Headers, response.Content?.Headers);
                        }
                        if ((LogEvents & LogEventType.ResponseBody) != 0)
                        {
                            await LogBody($"Response[{loggingRequestId}] Body: '{{0}}'", response.Content).ConfigureAwait(false);
                        }
                    }
                    if (response.IsSuccessStatusCode)
                    {
                        // No need to retry, the response was successful.
                        triesRemaining = 0;
                    }
                    else
                    {
                        bool errorHandled = false;

                        // We keep a local list of the handlers, since we can't call await inside lock.
                        List <IHttpUnsuccessfulResponseHandler> handlers;
                        lock (unsuccessfulResponseHandlersLock)
                        {
                            handlers = unsuccessfulResponseHandlers.ToList();
                        }
                        if (request.Properties.TryGetValue(UnsuccessfulResponseHandlerKey, out var handlersValue) &&
                            handlersValue is List <IHttpUnsuccessfulResponseHandler> perCallHandlers)
                        {
                            handlers.AddRange(perCallHandlers);
                        }

                        var handlerArgs = new HandleUnsuccessfulResponseArgs
                        {
                            Request           = request,
                            Response          = response,
                            TotalTries        = NumTries,
                            CurrentFailedTry  = NumTries - triesRemaining,
                            CancellationToken = cancellationToken
                        };

                        // Try to handle the abnormal HTTP response with each handler.
                        foreach (var handler in handlers)
                        {
                            try
                            {
                                errorHandled |= await handler.HandleResponseAsync(handlerArgs).ConfigureAwait(false);
                            }
                            catch when(DisposeAndReturnFalse(response))
                            {
                            }

                            bool DisposeAndReturnFalse(IDisposable disposable)
                            {
                                disposable.Dispose();
                                return(false);
                            }
                        }

                        errorHandled |= await CredentialHandleResponseAsync(handlerArgs).ConfigureAwait(false);

                        if (!errorHandled)
                        {
                            if (FollowRedirect && HandleRedirect(response))
                            {
                                if (redirectRemaining-- == 0)
                                {
                                    triesRemaining = 0;
                                }

                                errorHandled = true;
                                if (loggable && (LogEvents & LogEventType.ResponseAbnormal) != 0)
                                {
                                    InstanceLogger.Debug("Response[{0}] Redirect response was handled successfully. Redirect to {1}",
                                                         loggingRequestId, response.Headers.Location);
                                }
                            }
                            else
                            {
                                if (loggable && (LogEvents & LogEventType.ResponseAbnormal) != 0)
                                {
                                    InstanceLogger.Debug("Response[{0}] An abnormal response wasn't handled. Status code is {1}",
                                                         loggingRequestId, response.StatusCode);
                                }

                                // No need to retry, because no handler handled the abnormal response.
                                triesRemaining = 0;
                            }
                        }
                        else if (loggable && (LogEvents & LogEventType.ResponseAbnormal) != 0)
                        {
                            InstanceLogger.Debug("Response[{0}] An abnormal response was handled by an unsuccessful response handler. " +
                                                 "Status Code is {1}", loggingRequestId, response.StatusCode);
                        }
                    }
                }
            } while (triesRemaining > 0); // Not a successful status code but it was handled.

            // If the response is null, we should throw the last exception.
            if (response == null)
            {
                InstanceLogger.Error(lastException, "Request[{0}] Exception was thrown while executing a HTTP request", loggingRequestId);
                throw lastException;
            }
            else if (!response.IsSuccessStatusCode && loggable && (LogEvents & LogEventType.ResponseAbnormal) != 0)
            {
                InstanceLogger.Debug("Response[{0}] Abnormal response is being returned. Status Code is {1}", loggingRequestId, response.StatusCode);
            }

            return(response);
        }
示例#9
0
 public virtual bool HandleResponse(HandleUnsuccessfulResponseArgs args)
 {
     // if the func returns true try to handle this current failed try
     return(HandleUnsuccessfulResponseFunc != null && HandleUnsuccessfulResponseFunc(args.Response) &&
            Handle(args.SupportsRetry, args.CurrentFailedTry, args.CancellationToken));
 }
        /// <summary>
        /// Decorates unsuccessful responses, returns true if the response gets modified.
        /// See IHttpUnsuccessfulResponseHandler for more information. 
        /// </summary>
        public async Task<bool> HandleResponseAsync(HandleUnsuccessfulResponseArgs args)
        {
            // If the response was unauthorized, request a new access token so that the original
            // request can be retried.
            // TODO(peleyal): check WWW-Authenticate header.
            if (args.Response.StatusCode == HttpStatusCode.Unauthorized)
            {
                bool tokensEqual = false;
                if (Token != null)
                {
                    tokensEqual = Object.Equals(
                        Token.AccessToken, AccessMethod.GetAccessToken(args.Request));
                }
                return !tokensEqual
                    || await RequestAccessTokenAsync(args.CancellationToken).ConfigureAwait(false);
            }

            return false;
        }