/// <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; }
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); }
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); }
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; }
/// <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); }
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; }