private async Task <bool> HandleUnsuccessfulResponse <TResult>(ChoosenNode choosenNode, JsonOperationContext context, RavenCommand <TResult> command, HttpResponseMessage response, string url) { switch (response.StatusCode) { case HttpStatusCode.NotFound: command.SetResponse(null); return(true); case HttpStatusCode.Unauthorized: case HttpStatusCode.PreconditionFailed: if (string.IsNullOrEmpty(choosenNode.Node.ApiKey)) { throw new UnauthorizedAccessException( $"Got unauthorized response exception for {url}. Please specify an API Key."); } if (++command.AuthenticationRetries > 1) { throw new UnauthorizedAccessException( $"Got unauthorized response exception for {url} after trying to authenticate using ApiKey."); } var oauthSource = response.Headers.GetFirstValue("OAuth-Source"); #if DEBUG && FIDDLER // Make sure to avoid a cross DNS security issue, when running with Fiddler if (string.IsNullOrEmpty(oauthSource) == false) { oauthSource = oauthSource.Replace("localhost:", "localhost.fiddler:"); } #endif await HandleUnauthorized(oauthSource, choosenNode.Node, context).ConfigureAwait(false); await ExecuteAsync(choosenNode, context, command).ConfigureAwait(false); return(true); case HttpStatusCode.Forbidden: throw new UnauthorizedAccessException( $"Forbidan access to {url}. Make sure you're using the correct ApiKey."); case HttpStatusCode.BadGateway: case HttpStatusCode.ServiceUnavailable: await HandleServerDown(choosenNode, context, command, null); break; case HttpStatusCode.Conflict: // TODO: Conflict resolution // current implementation is temporary object message; using (var stream = await response.Content.ReadAsStreamAsync()) { var blittableJsonReaderObject = await context.ReadForMemoryAsync(stream, "PutResult"); blittableJsonReaderObject.TryGetMember("Message", out message); } throw new ConcurrencyException(message.ToString()); default: await ThrowServerError(context, response); break; } return(false); }
public async Task ExecuteAsync <TResult>(ChoosenNode choosenNode, JsonOperationContext context, RavenCommand <TResult> command, CancellationToken token = default(CancellationToken)) { string url; var request = CreateRequest(choosenNode.Node, command, out url); long cachedEtag; BlittableJsonReaderObject cachedValue; HttpCache.ReleaseCacheItem cachedItem; using (cachedItem = GetFromCache(context, command, request, url, out cachedEtag, out cachedValue)) { if (cachedEtag != 0) { var aggresiveCacheOptions = AggressiveCaching.Value; if (aggresiveCacheOptions != null && cachedItem.Age < aggresiveCacheOptions.Duration) { command.SetResponse(cachedValue); return; } request.Headers.IfNoneMatch.Add(new EntityTagHeaderValue("\"" + cachedEtag + "\"")); } var sp = Stopwatch.StartNew(); HttpResponseMessage response; try { response = await _httpClient.SendAsync(request, token).ConfigureAwait(false); sp.Stop(); } catch (HttpRequestException e) // server down, network down { sp.Stop(); await HandleServerDown(choosenNode, context, command, e); return; } finally { var requestTimeInMilliseconds = sp.ElapsedMilliseconds; choosenNode.Node.UpdateRequestTime(requestTimeInMilliseconds); if (choosenNode.SkippedNodes != null) { foreach (var skippedNode in choosenNode.SkippedNodes) { skippedNode.DecreaseRate(requestTimeInMilliseconds); } } } if (response.StatusCode == HttpStatusCode.NotModified) { cachedItem.NotModified(); command.SetResponse(cachedValue); return; } if (response.IsSuccessStatusCode == false) { if (await HandleUnsuccessfulResponse(choosenNode, context, command, response, url)) { return; } } if (response.Content.Headers.ContentLength.HasValue && response.Content.Headers.ContentLength == 0) { return; } await command.ProcessResponse(context, _cache, response, url); } }