internal static void StartRequestAttempt <T>(ExecutionState <T> executionState) { executionState.ExceptionRef = null; executionState.Cmd.CurrentResult = new RequestResult { StartTime = DateTime.Now }; lock (executionState.OperationContext.RequestResults) { executionState.OperationContext.RequestResults.Add(executionState.Cmd.CurrentResult); executionState.Cmd.RequestResults.Add(executionState.Cmd.CurrentResult); } RESTCommand <T> restCMD = executionState.RestCMD; if (restCMD != null) { if (!restCMD.StorageUri.ValidateLocationMode(restCMD.LocationMode)) { throw new InvalidOperationException("The Uri for the target storage location is not specified. Please consider changing the request's location mode."); } switch (restCMD.CommandLocationMode) { case CommandLocationMode.PrimaryOnly: if (restCMD.LocationMode == LocationMode.SecondaryOnly) { throw new InvalidOperationException("This operation can only be executed against the primary storage location."); } Logger.LogInformational(executionState.OperationContext, "This operation can only be executed against the primary storage location."); executionState.CurrentLocation = StorageLocation.Primary; restCMD.LocationMode = LocationMode.PrimaryOnly; break; case CommandLocationMode.SecondaryOnly: if (restCMD.LocationMode == LocationMode.PrimaryOnly) { throw new InvalidOperationException("This operation can only be executed against the secondary storage location."); } Logger.LogInformational(executionState.OperationContext, "This operation can only be executed against the secondary storage location."); executionState.CurrentLocation = StorageLocation.Secondary; restCMD.LocationMode = LocationMode.SecondaryOnly; break; } } executionState.Cmd.CurrentResult.TargetLocation = executionState.CurrentLocation; }
internal static RESTCommand <ServiceStats> GenerateCMDForGetServiceStats(CloudTableClient client, TableRequestOptions requestOptions) { RESTCommand <ServiceStats> rESTCommand = new RESTCommand <ServiceStats>(client.Credentials, client.StorageUri); RESTCommandGeneratorUtils.ApplyTableRequestOptionsToStorageCommand(requestOptions, rESTCommand); rESTCommand.HttpClient = client.HttpClient; LocationMode?locationMode = requestOptions.LocationMode; if (locationMode.GetValueOrDefault() == LocationMode.PrimaryOnly && (locationMode.HasValue ? true : false)) { throw new InvalidOperationException("GetServiceStats cannot be run with a 'PrimaryOnly' location mode."); } rESTCommand.CommandLocationMode = CommandLocationMode.PrimaryOrSecondary; rESTCommand.BuildRequest = ((RESTCommand <ServiceStats> cmd, Uri uri, UriQueryBuilder uriBuilder, HttpContent httpContent, int?timeout, OperationContext ctx) => TableRequestMessageFactory.BuildStorageRequestMessageForGetServiceStats(uri, uriBuilder, timeout, SharedKeyCanonicalizer.Instance, client.Credentials, ctx, requestOptions)); rESTCommand.ParseErrorAsync = StorageExtendedErrorInformationRestHelper.ReadExtendedErrorInfoFromStreamAsync; rESTCommand.PreProcessResponse = ((RESTCommand <ServiceStats> cmd, HttpResponseMessage resp, Exception ex, OperationContext ctx) => HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.OK, resp, null, cmd, ex)); rESTCommand.PostProcessResponseAsync = ((RESTCommand <ServiceStats> cmd, HttpResponseMessage resp, OperationContext ctx, CancellationToken token) => TableOperationHttpResponseParsers.ReadServiceStatsAsync(cmd.ResponseStream)); return(rESTCommand); }
internal static RESTCommand <TableBatchResult> GenerateCMDForTableBatchOperation(TableBatchOperation batch, CloudTableClient client, CloudTable table, TableRequestOptions requestOptions) { RESTCommand <TableBatchResult> rESTCommand = new RESTCommand <TableBatchResult>(client.Credentials, client.StorageUri); RESTCommandGeneratorUtils.ApplyTableRequestOptionsToStorageCommand(requestOptions, rESTCommand); rESTCommand.HttpClient = client.HttpClient; TableBatchResult results = new TableBatchResult(); rESTCommand.CommandLocationMode = ((!batch.ContainsWrites) ? CommandLocationMode.PrimaryOrSecondary : CommandLocationMode.PrimaryOnly); rESTCommand.CommandLocationMode = CommandLocationMode.PrimaryOnly; rESTCommand.ParseErrorAsync = StorageExtendedErrorInformationRestHelper.ReadExtendedErrorInfoFromStreamAsync; rESTCommand.BuildRequest = ((RESTCommand <TableBatchResult> cmd, Uri uri, UriQueryBuilder builder, HttpContent httpContent, int?timeout, OperationContext ctx) => TableRequestMessageFactory.BuildStorageRequestMessageForTableBatchOperation(uri, batch, SharedKeyCanonicalizer.Instance, table.Name, client.Credentials, ctx, requestOptions)); rESTCommand.PreProcessResponse = ((RESTCommand <TableBatchResult> cmd, HttpResponseMessage resp, Exception ex, OperationContext ctx) => HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.Accepted, resp?.StatusCode ?? HttpStatusCode.Unused, results, cmd, ex)); rESTCommand.PostProcessResponseAsync = ((RESTCommand <TableBatchResult> cmd, HttpResponseMessage resp, OperationContext ctx, CancellationToken token) => TableOperationHttpResponseParsers.TableBatchOperationPostProcessAsync(results, batch, cmd, resp, ctx, requestOptions, client.Credentials.AccountName, token)); rESTCommand.RecoveryAction = delegate { results.Clear(); }; return(rESTCommand); }
public ExecutionState(RESTCommand <T> cmd, IRetryPolicy policy, OperationContext operationContext) { Cmd = cmd; object retryPolicy2; if (policy == null) { IRetryPolicy retryPolicy = new NoRetry(); retryPolicy2 = retryPolicy; } else { retryPolicy2 = policy.CreateInstance(); } RetryPolicy = (IRetryPolicy)retryPolicy2; OperationContext = (operationContext ?? new OperationContext()); InitializeLocation(); if (OperationContext.StartTime == DateTimeOffset.MinValue) { OperationContext.StartTime = DateTimeOffset.Now; } }
private static void ProcessStartOfRequest <T>(ExecutionState <T> executionState, string startLogMessage, CancellationTokenSource timeoutTokenSource = null) { RESTCommand <T> restCMD = executionState.RestCMD; executionState.CurrentOperation = ExecutorOperation.BeginOperation; HttpContent arg = (restCMD.BuildContent != null) ? restCMD.BuildContent(restCMD, executionState.OperationContext) : null; Uri uri = restCMD.StorageUri.GetUri(executionState.CurrentLocation); Uri uri2 = restCMD.Credentials.TransformUri(uri); Logger.LogInformational(executionState.OperationContext, "Starting asynchronous request to {0}.", uri2); UriQueryBuilder arg2 = new UriQueryBuilder(executionState.RestCMD.Builder); executionState.Req = restCMD.BuildRequest(restCMD, uri2, arg2, arg, restCMD.ServerTimeoutInSeconds, executionState.OperationContext); ExecutionStateUtils.ApplyUserHeaders(executionState); ExecutionStateUtils.FireSendingRequest(executionState); if (executionState.OperationExpiryTime.HasValue) { timeoutTokenSource?.CancelAfter(executionState.RemainingTimeout); } else { timeoutTokenSource?.CancelAfter(int.MaxValue); } }
public static async Task <T> ExecuteAsync <T>(RESTCommand <T> cmd, IRetryPolicy policy, OperationContext operationContext, CancellationToken token) { using (ExecutionState <T> executionState = new ExecutionState <T>(cmd, policy, operationContext)) { using (CancellationTokenSource timeoutTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token)) { CancellationToken timeoutToken = timeoutTokenSource.Token; bool shouldRetry = false; TimeSpan delay = TimeSpan.Zero; HttpClient client = cmd.HttpClient ?? HttpClientFactory.Instance; do { try { executionState.Init(); ExecutionStateUtils.StartRequestAttempt(executionState); ProcessStartOfRequest(executionState, "Starting asynchronous request to {0}.", timeoutTokenSource); ExecutionStateUtils.CheckTimeout(executionState, throwOnTimeout: true); } catch (Exception ex) { Logger.LogError(executionState.OperationContext, "Exception thrown while initializing request: {0}.", ex.Message); StorageException ex2 = await ExecutionStateUtils.TranslateExceptionBasedOnParseErrorAsync(ex, executionState.Cmd.CurrentResult, executionState.Resp, executionState.Cmd.ParseErrorAsync, timeoutToken).ConfigureAwait(continueOnCapturedContext: false); ex2.IsRetryable = false; executionState.ExceptionRef = ex2; throw executionState.ExceptionRef; } try { executionState.CurrentOperation = ExecutorOperation.BeginGetResponse; Logger.LogInformational(executionState.OperationContext, "Waiting for response."); ExecutionState <T> executionState2 = executionState; executionState2.Resp = await client.SendAsync(executionState.Req, HttpCompletionOption.ResponseHeadersRead, timeoutToken).ConfigureAwait(continueOnCapturedContext: false); executionState.CurrentOperation = ExecutorOperation.EndGetResponse; if (!executionState.Resp.IsSuccessStatusCode) { executionState2 = executionState; executionState2.ExceptionRef = await StorageExceptionTranslator.PopulateStorageExceptionFromHttpResponseMessage(executionState.Resp, executionState.Cmd.CurrentResult, timeoutToken, executionState.Cmd.ParseErrorAsync).ConfigureAwait(continueOnCapturedContext: false); } Logger.LogInformational(executionState.OperationContext, "Response received. Status code = {0}, Request ID = {1}, Content-MD5 = {2}, ETag = {3}.", executionState.Cmd.CurrentResult.HttpStatusCode, executionState.Cmd.CurrentResult.ServiceRequestID, executionState.Cmd.CurrentResult.ContentMd5, executionState.Cmd.CurrentResult.Etag); ExecutionStateUtils.FireResponseReceived(executionState); if (cmd.PreProcessResponse != null) { executionState.CurrentOperation = ExecutorOperation.PreProcess; try { executionState.Result = cmd.PreProcessResponse(cmd, executionState.Resp, executionState.ExceptionRef, executionState.OperationContext); executionState.ExceptionRef = null; } catch (Exception exceptionRef) { executionState.ExceptionRef = exceptionRef; } Logger.LogInformational(executionState.OperationContext, "Response headers were processed successfully, proceeding with the rest of the operation."); } executionState.CurrentOperation = ExecutorOperation.GetResponseStream; cmd.ResponseStream = new MemoryStream(); await(await executionState.Resp.Content.ReadAsStreamAsync()).WriteToAsync(cmd.ResponseStream, executionState, timeoutTokenSource.Token); cmd.ResponseStream.Position = 0L; if (executionState.ExceptionRef != null) { executionState.CurrentOperation = ExecutorOperation.BeginDownloadResponse; Logger.LogInformational(executionState.OperationContext, "Downloading error response body."); try { executionState2 = executionState; executionState2.ExceptionRef = await StorageExceptionTranslator.TranslateExceptionWithPreBufferedStreamAsync(executionState.ExceptionRef, executionState.Cmd.CurrentResult, cmd.ResponseStream, executionState.Resp, executionState.Cmd.ParseErrorAsync, timeoutToken); throw executionState.ExceptionRef; } finally { cmd.ResponseStream.Dispose(); cmd.ResponseStream = null; } } await ProcessEndOfRequestAsync(executionState, timeoutToken).ConfigureAwait(continueOnCapturedContext: false); ExecutionStateUtils.FinishRequestAttempt(executionState); return(executionState.Result); } catch (Exception ex3) { Exception ex4 = ex3; Logger.LogWarning(executionState.OperationContext, "Exception thrown during the operation: {0}.", ex4.Message); ExecutionStateUtils.FinishRequestAttempt(executionState); if (ex4 is OperationCanceledException && executionState.OperationExpiryTime.HasValue && DateTime.Now.CompareTo(executionState.OperationExpiryTime.Value) > 0) { ex4 = new TimeoutException("The client could not finish the operation within specified timeout.", ex4); } Exception ex6 = executionState.ExceptionRef = await ExecutionStateUtils.TranslateExceptionBasedOnParseErrorAsync(ex4, executionState.Cmd.CurrentResult, executionState.Resp, executionState.Cmd.ParseErrorAsync, timeoutToken).ConfigureAwait(continueOnCapturedContext: false); Logger.LogInformational(executionState.OperationContext, "Checking if the operation should be retried. Retry count = {0}, HTTP status code = {1}, Retryable exception = {2}, Exception = {3}.", executionState.RetryCount, executionState.Cmd.CurrentResult.HttpStatusCode, ((StorageException)ex6).IsRetryable ? "yes" : "no", ex6.Message); shouldRetry = false; if (((StorageException)ex6).IsRetryable && executionState.RetryPolicy != null) { executionState.CurrentLocation = ExecutionStateUtils.GetNextLocation(executionState.CurrentLocation, cmd.LocationMode); Logger.LogInformational(executionState.OperationContext, "The next location has been set to {0}, based on the location mode.", executionState.CurrentLocation); IExtendedRetryPolicy extendedRetryPolicy = executionState.RetryPolicy as IExtendedRetryPolicy; if (extendedRetryPolicy != null) { RetryInfo retryInfo = extendedRetryPolicy.Evaluate(new RetryContext(executionState.RetryCount++, cmd.CurrentResult, executionState.CurrentLocation, cmd.LocationMode), executionState.OperationContext); if (retryInfo != null) { Logger.LogInformational(executionState.OperationContext, "The extended retry policy set the next location to {0} and updated the location mode to {1}.", retryInfo.TargetLocation, retryInfo.UpdatedLocationMode); shouldRetry = true; executionState.CurrentLocation = retryInfo.TargetLocation; cmd.LocationMode = retryInfo.UpdatedLocationMode; delay = retryInfo.RetryInterval; } } else { shouldRetry = executionState.RetryPolicy.ShouldRetry(executionState.RetryCount++, cmd.CurrentResult.HttpStatusCode, executionState.ExceptionRef, out delay, executionState.OperationContext); } if (delay < TimeSpan.Zero || delay > TableRestConstants.MaximumRetryBackoff) { delay = TableRestConstants.MaximumRetryBackoff; } } } finally { if (executionState.Resp != null) { executionState.Resp.Dispose(); executionState.Resp = null; } } if (!shouldRetry || (executionState.OperationExpiryTime.HasValue && (DateTime.Now + delay).CompareTo(executionState.OperationExpiryTime.Value) > 0)) { Logger.LogError(executionState.OperationContext, shouldRetry ? "Operation cannot be retried because the maximum execution time has been reached. Failing with {0}." : "Retry policy did not allow for a retry. Failing with {0}.", executionState.ExceptionRef.Message); throw executionState.ExceptionRef; } cmd.RecoveryAction?.Invoke(cmd, executionState.Cmd.CurrentResult.Exception, executionState.OperationContext); if (delay > TimeSpan.Zero) { await Task.Delay(delay, timeoutToken).ConfigureAwait(continueOnCapturedContext: false); } Logger.LogInformational(executionState.OperationContext, "Retrying failed operation."); ExecutionStateUtils.FireRetrying(executionState); }while (shouldRetry); throw new NotImplementedException("Unexpected internal storage client error."); } } }
internal static async Task <TableResult> TableOperationPostProcessAsync(TableResult result, TableOperation operation, RESTCommand <TableResult> cmd, HttpResponseMessage resp, OperationContext ctx, TableRequestOptions options, string accountName, CancellationToken cancellationToken) { string text = (resp.Headers.ETag != null) ? resp.Headers.ETag.ToString() : null; if (operation.OperationType != TableOperationType.Retrieve && operation.OperationType != 0) { result.Etag = text; operation.Entity.ETag = result.Etag; } else if (operation.OperationType == TableOperationType.Insert && !operation.EchoContent) { if (text != null) { result.Etag = text; operation.Entity.ETag = result.Etag; operation.Entity.Timestamp = ParseETagForTimestamp(result.Etag); } } else { MediaTypeHeaderValue contentType = resp.Content.Headers.ContentType; if (!contentType.MediaType.Equals("application/json") || !contentType.Parameters.Contains(NameValueHeaderValue.Parse("odata=nometadata"))) { await ReadOdataEntityAsync(result, operation, cmd.ResponseStream, ctx, accountName, options, cancellationToken); } else { result.Etag = text; await ReadEntityUsingJsonParserAsync(result, operation, cmd.ResponseStream, ctx, options, cancellationToken); } } return(result); }
internal static async Task <TableBatchResult> TableBatchOperationPostProcessAsync(TableBatchResult result, TableBatchOperation batch, RESTCommand <TableBatchResult> cmd, HttpResponseMessage resp, OperationContext ctx, TableRequestOptions options, string accountName, CancellationToken cancellationToken) { Stream responseStream = cmd.ResponseStream; StreamReader streamReader = new StreamReader(responseStream); await streamReader.ReadLineAsync().ConfigureAwait(continueOnCapturedContext: false); string currentLine3 = await streamReader.ReadLineAsync().ConfigureAwait(continueOnCapturedContext: false); int index = 0; bool failError = false; while (currentLine3 != null && !currentLine3.StartsWith("--batchresponse")) { while (!currentLine3.StartsWith("HTTP")) { currentLine3 = await streamReader.ReadLineAsync().ConfigureAwait(continueOnCapturedContext: false); } int statusCode = int.Parse(currentLine3.Substring(9, 3)); Dictionary <string, string> headers = new Dictionary <string, string>(); currentLine3 = await streamReader.ReadLineAsync().ConfigureAwait(continueOnCapturedContext: false); while (!string.IsNullOrWhiteSpace(currentLine3)) { int num = currentLine3.IndexOf(':'); headers[currentLine3.Substring(0, num)] = currentLine3.Substring(num + 2); currentLine3 = await streamReader.ReadLineAsync().ConfigureAwait(continueOnCapturedContext: false); } MemoryStream bodyStream = null; currentLine3 = await streamReader.ReadLineAsync().ConfigureAwait(continueOnCapturedContext: false); if (statusCode != 204) { bodyStream = new MemoryStream(Encoding.UTF8.GetBytes(currentLine3)); } await streamReader.ReadLineAsync().ConfigureAwait(continueOnCapturedContext: false); currentLine3 = await streamReader.ReadLineAsync().ConfigureAwait(continueOnCapturedContext: false); TableOperation tableOperation = batch[index]; TableResult obj = new TableResult { Result = tableOperation.Entity }; result.Add(obj); string arg = null; if (headers.ContainsKey("Content-Type")) { arg = headers["Content-Type"]; } obj.HttpStatusCode = statusCode; bool flag; if (tableOperation.OperationType == TableOperationType.Insert) { failError = (statusCode == 409); flag = ((!tableOperation.EchoContent) ? (statusCode != 204) : (statusCode != 201)); } else if (tableOperation.OperationType == TableOperationType.Retrieve) { if (statusCode == 404) { index++; continue; } flag = (statusCode != 200); } else { failError = (statusCode == 404); flag = (statusCode != 204); } if (failError) { if (cmd.ParseErrorAsync != null) { cmd.CurrentResult.ExtendedErrorInformation = cmd.ParseErrorAsync(bodyStream, resp, arg, CancellationToken.None).Result; } cmd.CurrentResult.HttpStatusCode = statusCode; if (!string.IsNullOrEmpty(cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage)) { string errorMessage = cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage; cmd.CurrentResult.HttpStatusMessage = errorMessage.Substring(0, errorMessage.IndexOf("\n", StringComparison.Ordinal)); } else { cmd.CurrentResult.HttpStatusMessage = statusCode.ToString(CultureInfo.InvariantCulture); } throw new StorageException(cmd.CurrentResult, (cmd.CurrentResult.ExtendedErrorInformation != null) ? cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage : "An unknown error has occurred, extended error information not available.", null) { IsRetryable = false }; } if (flag) { if (cmd.ParseErrorAsync != null) { cmd.CurrentResult.ExtendedErrorInformation = cmd.ParseErrorAsync(bodyStream, resp, arg, CancellationToken.None).Result; } cmd.CurrentResult.HttpStatusCode = statusCode; if (!string.IsNullOrEmpty(cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage)) { string errorMessage2 = cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage; cmd.CurrentResult.HttpStatusMessage = errorMessage2.Substring(0, errorMessage2.IndexOf("\n", StringComparison.Ordinal)); } else { cmd.CurrentResult.HttpStatusMessage = statusCode.ToString(CultureInfo.InvariantCulture); } string arg2 = Convert.ToString(index, CultureInfo.InvariantCulture); if (cmd.CurrentResult.ExtendedErrorInformation != null && !string.IsNullOrEmpty(cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage)) { string text = ExtractEntityIndexFromExtendedErrorInformation(cmd.CurrentResult); if (!string.IsNullOrEmpty(text)) { arg2 = text; } } throw new StorageException(cmd.CurrentResult, string.Format(CultureInfo.CurrentCulture, "Element {0} in the batch returned an unexpected response code.", arg2), null) { IsRetryable = true }; } if (headers.ContainsKey("ETag") && !string.IsNullOrEmpty(headers["ETag"])) { obj.Etag = headers["ETag"]; if (tableOperation.Entity != null) { tableOperation.Entity.ETag = obj.Etag; } } if (tableOperation.OperationType == TableOperationType.Retrieve || (tableOperation.OperationType == TableOperationType.Insert && tableOperation.EchoContent)) { if (!headers["Content-Type"].Contains("application/json;odata=nometadata")) { await ReadOdataEntityAsync(obj, tableOperation, bodyStream, ctx, accountName, options, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } else { await ReadEntityUsingJsonParserAsync(obj, tableOperation, bodyStream, ctx, options, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } } else if (tableOperation.OperationType == TableOperationType.Insert) { tableOperation.Entity.Timestamp = ParseETagForTimestamp(obj.Etag); } index++; } return(result); }
internal static T ProcessExpectedStatusCodeNoException <T>(HttpStatusCode[] expectedStatusCodes, HttpStatusCode actualStatusCode, T retVal, RESTCommand <T> cmd, Exception ex) { if (ex != null) { throw ex; } if (!expectedStatusCodes.Contains(actualStatusCode)) { string arg = string.Join(",", expectedStatusCodes); throw new StorageException(cmd.CurrentResult, string.Format(CultureInfo.InvariantCulture, "Unexpected response code, Expected:{0}, Received:{1}", arg, actualStatusCode.ToString()), null); } return(retVal); }
internal static T ProcessExpectedStatusCodeNoException <T>(HttpStatusCode expectedStatusCode, HttpStatusCode actualStatusCode, T retVal, RESTCommand <T> cmd, Exception ex) { if (ex != null) { throw ex; } if (actualStatusCode != expectedStatusCode) { throw new StorageException(cmd.CurrentResult, string.Format(CultureInfo.InvariantCulture, "Unexpected response code, Expected:{0}, Received:{1}", expectedStatusCode, actualStatusCode), null); } return(retVal); }
internal static T ProcessExpectedStatusCodeNoException <T>(HttpStatusCode[] expectedStatusCodes, HttpResponseMessage resp, T retVal, RESTCommand <T> cmd, Exception ex) { return(ProcessExpectedStatusCodeNoException(expectedStatusCodes, resp?.StatusCode ?? HttpStatusCode.Unused, retVal, cmd, ex)); }
internal static void ApplyTableRequestOptionsToStorageCommand <T>(TableRequestOptions options, RESTCommand <T> cmd) { if (options.LocationMode.HasValue) { cmd.LocationMode = options.LocationMode.Value; } if (options.ServerTimeout.HasValue) { cmd.ServerTimeoutInSeconds = (int)options.ServerTimeout.Value.TotalSeconds; } if (options.OperationExpiryTime.HasValue) { cmd.OperationExpiryTime = options.OperationExpiryTime; } else if (options.MaximumExecutionTime.HasValue) { cmd.OperationExpiryTime = DateTime.Now + options.MaximumExecutionTime.Value; } }