public async Task <Lease> CreateLeaseIfNotExistsAsync(string partitionId) // throws URISyntaxException, IOException, StorageException { AzureBlobLease returnLease; try { CloudBlockBlob leaseBlob = this.consumerGroupDirectory.GetBlockBlobReference(partitionId); returnLease = new AzureBlobLease(partitionId, leaseBlob); string jsonLease = JsonConvert.SerializeObject(returnLease); ProcessorEventSource.Log.AzureStorageManagerInfo( this.host.Id, partitionId, "CreateLeaseIfNotExist - leaseContainerName: " + this.leaseContainerName + " consumerGroupName: " + this.host.ConsumerGroupName); await leaseBlob.UploadTextAsync(jsonLease, null, AccessCondition.GenerateIfNoneMatchCondition("*"), null, null); } catch (StorageException se) { StorageExtendedErrorInformation extendedErrorInfo = se.RequestInformation.ExtendedErrorInformation; if (extendedErrorInfo != null && (extendedErrorInfo.ErrorCode == BlobErrorCodeStrings.BlobAlreadyExists || extendedErrorInfo.ErrorCode == BlobErrorCodeStrings.LeaseIdMissing)) // occurs when somebody else already has leased the blob { // The blob already exists. ProcessorEventSource.Log.AzureStorageManagerInfo(this.host.Id, partitionId, "Lease already exists"); returnLease = (AzureBlobLease)(await GetLeaseAsync(partitionId)); } else { Console.WriteLine("errorCode " + extendedErrorInfo.ErrorCode); Console.WriteLine("errorString " + extendedErrorInfo.ErrorMessage); ProcessorEventSource.Log.AzureStorageManagerError( this.host.Id, partitionId, "CreateLeaseIfNotExist StorageException - leaseContainerName: " + this.leaseContainerName + " consumerGroupName: " + this.host.ConsumerGroupName, se.ToString()); throw; } } return(returnLease); }
/// <summary> /// Deletes the table if it already exists. /// </summary> /// <param name="requestOptions">A <see cref="TableRequestOptions"/> object that specifies execution options, such as retry policy and timeout settings, for the operation.</param> /// <param name="operationContext">An <see cref="OperationContext"/> object for tracking the current operation.</param> /// <returns><c>true</c> if the table already existed and was deleted; otherwise, <c>false</c>.</returns> public IAsyncOperation <bool> DeleteIfExistsAsync(TableRequestOptions requestOptions, OperationContext operationContext) { requestOptions = TableRequestOptions.ApplyDefaults(requestOptions, this.ServiceClient); operationContext = operationContext ?? new OperationContext(); return(AsyncInfo.Run(async(cancellationToken) => { if (!await this.ExistsAsync(requestOptions, operationContext).AsTask(cancellationToken)) { return false; } else { try { await this.DeleteAsync(requestOptions, operationContext).AsTask(cancellationToken); return true; } catch (Exception) { if (operationContext.LastResult.HttpStatusCode == (int)HttpStatusCode.NotFound) { StorageExtendedErrorInformation extendedInfo = operationContext.LastResult.ExtendedErrorInformation; if ((extendedInfo == null) || (extendedInfo.ErrorCode == TableErrorCodeStrings.TableNotFound)) { return false; } else { throw; } } else { throw; } } } })); }
public IAsyncOperation <bool> CreateIfNotExistsAsync(FileRequestOptions options, OperationContext operationContext) { FileRequestOptions modifiedOptions = FileRequestOptions.ApplyDefaults(options, this.ServiceClient); operationContext = operationContext ?? new OperationContext(); return(AsyncInfo.Run(async(token) => { bool exists = await this.ExistsAsync(modifiedOptions, operationContext).AsTask(token); if (exists) { return false; } try { await this.CreateAsync(modifiedOptions, operationContext).AsTask(token); return true; } catch (Exception) { if (operationContext.LastResult.HttpStatusCode == (int)HttpStatusCode.Conflict) { StorageExtendedErrorInformation extendedInfo = operationContext.LastResult.ExtendedErrorInformation; if ((extendedInfo == null) || (extendedInfo.ErrorCode == FileErrorCodeStrings.ShareAlreadyExists)) { return false; } else { throw; } } else { throw; } } })); }
public virtual Task <bool> DeleteIfExistsAsync(AccessCondition accessCondition, FileRequestOptions options, OperationContext operationContext, CancellationToken cancellationToken) { FileRequestOptions modifiedOptions = FileRequestOptions.ApplyDefaults(options, this.ServiceClient); operationContext = operationContext ?? new OperationContext(); return(Task.Run(async() => { bool exists = await this.ExistsAsync(modifiedOptions, operationContext, cancellationToken); if (!exists) { return false; } try { await this.DeleteAsync(accessCondition, modifiedOptions, operationContext, cancellationToken); return true; } catch (Exception) { if (operationContext.LastResult.HttpStatusCode == (int)HttpStatusCode.NotFound) { StorageExtendedErrorInformation extendedInfo = operationContext.LastResult.ExtendedErrorInformation; if ((extendedInfo == null) || (extendedInfo.ErrorCode == FileErrorCodeStrings.ShareNotFound)) { return false; } else { throw; } } else { throw; } } }, cancellationToken)); }
/// <summary> /// Starts an asynchronous AppendBlock operation as soon as the parallel /// operation semaphore becomes available. Since parallelism is always set /// to 1 for append blobs, appendblock operations are called serially. /// </summary> /// <param name="blockData">Data to be uploaded</param> /// <param name="offset">Offset within the append blob to be used to set the append offset conditional header.</param> /// <param name="blockMD5">MD5 hash of the data to be uploaded</param> /// <returns>A task that represents the asynchronous write operation.</returns> private async Task WriteAppendBlockAsync(Stream blockData, long offset, string blockMD5) { this.noPendingWritesEvent.Increment(); await this.parallelOperationSemaphore.WaitAsync().ConfigureAwait(false); this.accessCondition.IfAppendPositionEqual = offset; int previousResultsCount = this.operationContext.RequestResults.Count; Task writeBlockTask = this.appendBlob.AppendBlockAsync(blockData, blockMD5, this.accessCondition, this.options, this.operationContext).ContinueWith(task => { if (task.Exception != null) { if (this.options.AbsorbConditionalErrorsOnRetry.Value && this.operationContext.LastResult.HttpStatusCode == (int)HttpStatusCode.PreconditionFailed) { StorageExtendedErrorInformation extendedInfo = this.operationContext.LastResult.ExtendedErrorInformation; #pragma warning disable 618 if (extendedInfo != null && (extendedInfo.ErrorCode == BlobErrorCodeStrings.InvalidAppendCondition || extendedInfo.ErrorCode == BlobErrorCodeStrings.InvalidMaxBlobSizeCondition) && (this.operationContext.RequestResults.Count - previousResultsCount > 1)) #pragma warning restore 618 { // Pre-condition failure on a retry should be ignored in a single writer scenario since the request // succeeded in the first attempt. Logger.LogWarning(this.operationContext, SR.PreconditionFailureIgnored); } else { this.lastException = task.Exception; } } else { this.lastException = task.Exception; } } this.noPendingWritesEvent.Decrement(); this.parallelOperationSemaphore.Release(); }); }
/// <summary> /// Returns the error code from storage exception, or null. /// </summary> /// <param name="exception">The storage exception.</param> /// <returns>The error code, or null.</returns> public static string GetErrorCode(this StorageException exception) { if (exception == null) { throw new ArgumentNullException("exception"); } RequestResult result = exception.RequestInformation; if (result == null) { return(null); } StorageExtendedErrorInformation extendedInformation = result.ExtendedErrorInformation; if (extendedInformation == null) { return(null); } return(extendedInformation.ErrorCode); }
private static async Task <StorageExtendedErrorInformation> ReadXmlAsync(XmlReader reader, CancellationToken cancellationToken) { StorageExtendedErrorInformation extendedErrorInformation = new StorageExtendedErrorInformation(); CommonUtility.AssertNotNull("reader", reader); extendedErrorInformation.AdditionalDetails = new Dictionary <string, string>(); await reader.ReadStartElementAsync().ConfigureAwait(continueOnCapturedContext: false); cancellationToken.ThrowIfCancellationRequested(); while (await reader.IsStartElementAsync().ConfigureAwait(continueOnCapturedContext: false)) { if (reader.IsEmptyElement) { await reader.SkipAsync().ConfigureAwait(continueOnCapturedContext: false); cancellationToken.ThrowIfCancellationRequested(); } else if (string.Compare(reader.LocalName, "Code", StringComparison.OrdinalIgnoreCase) == 0 || string.Compare(reader.LocalName, "code", StringComparison.Ordinal) == 0) { StorageExtendedErrorInformation storageExtendedErrorInformation = extendedErrorInformation; storageExtendedErrorInformation.ErrorCode = await reader.ReadElementContentAsStringAsync().ConfigureAwait(continueOnCapturedContext: false); cancellationToken.ThrowIfCancellationRequested(); } else if (string.Compare(reader.LocalName, "Message", StringComparison.OrdinalIgnoreCase) == 0 || string.Compare(reader.LocalName, "message", StringComparison.Ordinal) == 0) { StorageExtendedErrorInformation storageExtendedErrorInformation = extendedErrorInformation; storageExtendedErrorInformation.ErrorMessage = await reader.ReadElementContentAsStringAsync().ConfigureAwait(continueOnCapturedContext: false); cancellationToken.ThrowIfCancellationRequested(); } else if (string.Compare(reader.LocalName, "exceptiondetails", StringComparison.OrdinalIgnoreCase) == 0) { await reader.ReadStartElementAsync(null, null).ConfigureAwait(continueOnCapturedContext: false); cancellationToken.ThrowIfCancellationRequested(); while (await reader.IsStartElementAsync().ConfigureAwait(continueOnCapturedContext: false)) { string localName = reader.LocalName; if (!(localName == "ExceptionMessage")) { if (localName == "StackTrace") { IDictionary <string, string> additionalDetails = extendedErrorInformation.AdditionalDetails; additionalDetails.Add("StackTrace", await reader.ReadElementContentAsStringAsync("StackTrace", string.Empty).ConfigureAwait(continueOnCapturedContext: false)); cancellationToken.ThrowIfCancellationRequested(); } else { await reader.SkipAsync().ConfigureAwait(continueOnCapturedContext: false); cancellationToken.ThrowIfCancellationRequested(); } } else { IDictionary <string, string> additionalDetails = extendedErrorInformation.AdditionalDetails; additionalDetails.Add("ExceptionMessage", await reader.ReadElementContentAsStringAsync("ExceptionMessage", string.Empty).ConfigureAwait(continueOnCapturedContext: false)); cancellationToken.ThrowIfCancellationRequested(); } } await reader.ReadEndElementAsync().ConfigureAwait(continueOnCapturedContext: false); cancellationToken.ThrowIfCancellationRequested(); } else { IDictionary <string, string> additionalDetails = extendedErrorInformation.AdditionalDetails; string localName2 = reader.LocalName; additionalDetails.Add(localName2, await reader.ReadInnerXmlAsync().ConfigureAwait(continueOnCapturedContext: false)); cancellationToken.ThrowIfCancellationRequested(); } } await reader.ReadEndElementAsync().ConfigureAwait(continueOnCapturedContext: false); cancellationToken.ThrowIfCancellationRequested(); return(extendedErrorInformation); }
internal static IList <TableResult> TableBatchOperationPostProcess(IList <TableResult> result, TableBatchOperation batch, RESTCommand <IList <TableResult> > cmd, HttpWebResponse resp, OperationContext ctx) { ODataMessageReaderSettings readerSettings = new ODataMessageReaderSettings(); readerSettings.MessageQuotas = new ODataMessageQuotas() { MaxPartsPerBatch = TableConstants.TableServiceMaxResults, MaxReceivedMessageSize = TableConstants.TableServiceMaxPayload }; using (ODataMessageReader responseReader = new ODataMessageReader(new HttpResponseAdapterMessage(resp, cmd.ResponseStream), readerSettings)) { // create a reader ODataBatchReader reader = responseReader.CreateODataBatchReader(); // Initial => changesetstart if (reader.State == ODataBatchReaderState.Initial) { reader.Read(); } if (reader.State == ODataBatchReaderState.ChangesetStart) { // ChangeSetStart => Operation reader.Read(); } int index = 0; bool failError = false; bool failUnexpected = false; while (reader.State == ODataBatchReaderState.Operation) { TableOperation currentOperation = batch[index]; TableResult currentResult = new TableResult() { Result = currentOperation.Entity }; result.Add(currentResult); ODataBatchOperationResponseMessage mimePartResponseMessage = reader.CreateOperationResponseMessage(); currentResult.HttpStatusCode = mimePartResponseMessage.StatusCode; // Validate Status Code if (currentOperation.OperationType == TableOperationType.Insert) { failError = mimePartResponseMessage.StatusCode == (int)HttpStatusCode.Conflict; failUnexpected = mimePartResponseMessage.StatusCode != (int)HttpStatusCode.Created; } else if (currentOperation.OperationType == TableOperationType.Retrieve) { if (mimePartResponseMessage.StatusCode == (int)HttpStatusCode.NotFound) { index++; // Operation => next reader.Read(); continue; } failUnexpected = mimePartResponseMessage.StatusCode != (int)HttpStatusCode.OK; } else { failError = mimePartResponseMessage.StatusCode == (int)HttpStatusCode.NotFound; failUnexpected = mimePartResponseMessage.StatusCode != (int)HttpStatusCode.NoContent; } if (failError) { cmd.CurrentResult.ExtendedErrorInformation = StorageExtendedErrorInformation.ReadFromStream(mimePartResponseMessage.GetStream()); cmd.CurrentResult.HttpStatusCode = mimePartResponseMessage.StatusCode; throw new StorageException( cmd.CurrentResult, cmd.CurrentResult.ExtendedErrorInformation != null ? cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage : SR.ExtendedErrorUnavailable, null) { IsRetryable = false }; } if (failUnexpected) { cmd.CurrentResult.ExtendedErrorInformation = StorageExtendedErrorInformation.ReadFromStream(mimePartResponseMessage.GetStream()); cmd.CurrentResult.HttpStatusCode = mimePartResponseMessage.StatusCode; string indexString = Convert.ToString(index); // Attempt to extract index of failing entity from extended error info if (cmd.CurrentResult.ExtendedErrorInformation != null && !string.IsNullOrEmpty(cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage)) { string tempIndex = TableRequest.ExtractEntityIndexFromExtendedErrorInformation(cmd.CurrentResult); if (!string.IsNullOrEmpty(tempIndex)) { indexString = tempIndex; } } throw new StorageException(cmd.CurrentResult, SR.UnexpectedResponseCodeForOperation + indexString, null) { IsRetryable = true }; } // Update etag if (!string.IsNullOrEmpty(mimePartResponseMessage.GetHeader("ETag"))) { currentResult.Etag = mimePartResponseMessage.GetHeader("ETag"); if (currentOperation.Entity != null) { currentOperation.Entity.ETag = currentResult.Etag; } } // Parse Entity if needed if (currentOperation.OperationType == TableOperationType.Retrieve || currentOperation.OperationType == TableOperationType.Insert) { ReadOdataEntity(currentResult, currentOperation, mimePartResponseMessage, ctx, readerSettings); } index++; // Operation => reader.Read(); } } return(result); }
internal static async Task <IList <TableResult> > TableBatchOperationPostProcess(IList <TableResult> result, TableBatchOperation batch, RESTCommand <IList <TableResult> > cmd, HttpResponseMessage resp, OperationContext ctx, TableRequestOptions options, CancellationToken cancellationToken) { Stream responseStream = cmd.ResponseStream; StreamReader streamReader = new StreamReader(responseStream); string currentLine = await streamReader.ReadLineAsync().ConfigureAwait(false); currentLine = await streamReader.ReadLineAsync().ConfigureAwait(false); int index = 0; bool failError = false; bool failUnexpected = false; while ((currentLine != null) && !(currentLine.StartsWith(@"--batchresponse"))) //while (reader.State == ODataBatchReaderState.Operation) { while (!(currentLine.StartsWith("HTTP"))) { currentLine = await streamReader.ReadLineAsync().ConfigureAwait(false); } // The first line of the response looks like this: // HTTP/1.1 204 No Content // The HTTP status code is chars 9 - 11. int statusCode = Int32.Parse(currentLine.Substring(9, 3)); Dictionary <string, string> headers = new Dictionary <string, string>(); currentLine = await streamReader.ReadLineAsync().ConfigureAwait(false); while (!string.IsNullOrWhiteSpace(currentLine)) { // The headers all look like this: // Cache-Control: no-cache // This code below parses out the header names and values, by noting the location of the colon. int colonIndex = currentLine.IndexOf(':'); headers[currentLine.Substring(0, colonIndex)] = currentLine.Substring(colonIndex + 2); currentLine = await streamReader.ReadLineAsync().ConfigureAwait(false); } MemoryStream bodyStream = null; currentLine = await streamReader.ReadLineAsync().ConfigureAwait(false); if (statusCode != 204) { bodyStream = new MemoryStream(Encoding.UTF8.GetBytes(currentLine)); } currentLine = await streamReader.ReadLineAsync().ConfigureAwait(false); currentLine = await streamReader.ReadLineAsync().ConfigureAwait(false); TableOperation currentOperation = batch[index]; TableResult currentResult = new TableResult() { Result = currentOperation.Entity }; result.Add(currentResult); string contentType = null; if (headers.ContainsKey(Constants.ContentTypeElement)) { contentType = headers[Constants.ContentTypeElement]; } currentResult.HttpStatusCode = statusCode; // Validate Status Code if (currentOperation.OperationType == TableOperationType.Insert) { failError = statusCode == (int)HttpStatusCode.Conflict; if (currentOperation.EchoContent) { failUnexpected = statusCode != (int)HttpStatusCode.Created; } else { failUnexpected = statusCode != (int)HttpStatusCode.NoContent; } } else if (currentOperation.OperationType == TableOperationType.Retrieve) { if (statusCode == (int)HttpStatusCode.NotFound) { index++; continue; } failUnexpected = statusCode != (int)HttpStatusCode.OK; } else { failError = statusCode == (int)HttpStatusCode.NotFound; failUnexpected = statusCode != (int)HttpStatusCode.NoContent; } if (failError) { // If the parse error is null, then don't get the extended error information and the StorageException will contain SR.ExtendedErrorUnavailable message. if (cmd.ParseError != null) { cmd.CurrentResult.ExtendedErrorInformation = cmd.ParseError(bodyStream, resp, contentType); } else { cmd.CurrentResult.ExtendedErrorInformation = StorageExtendedErrorInformation.ReadFromStream(bodyStream); } cmd.CurrentResult.HttpStatusCode = statusCode; if (!string.IsNullOrEmpty(cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage)) { string msg = cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage; cmd.CurrentResult.HttpStatusMessage = msg.Substring(0, msg.IndexOf("\n")); } else { cmd.CurrentResult.HttpStatusMessage = statusCode.ToString(); } throw new StorageException( cmd.CurrentResult, cmd.CurrentResult.ExtendedErrorInformation != null ? cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage : SR.ExtendedErrorUnavailable, null) { IsRetryable = false }; } if (failUnexpected) { // If the parse error is null, then don't get the extended error information and the StorageException will contain SR.ExtendedErrorUnavailable message. if (cmd.ParseError != null) { cmd.CurrentResult.ExtendedErrorInformation = cmd.ParseError(bodyStream, resp, contentType); } cmd.CurrentResult.HttpStatusCode = statusCode; if (!string.IsNullOrEmpty(cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage)) { string msg = cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage; cmd.CurrentResult.HttpStatusMessage = msg.Substring(0, msg.IndexOf("\n")); } else { cmd.CurrentResult.HttpStatusMessage = statusCode.ToString(CultureInfo.InvariantCulture); } string indexString = Convert.ToString(index); // Attempt to extract index of failing entity from extended error info if (cmd.CurrentResult.ExtendedErrorInformation != null && !string.IsNullOrEmpty(cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage)) { string tempIndex = TableRequest.ExtractEntityIndexFromExtendedErrorInformation(cmd.CurrentResult); if (!string.IsNullOrEmpty(tempIndex)) { indexString = tempIndex; } } throw new StorageException(cmd.CurrentResult, string.Format(SR.BatchErrorInOperation, indexString), null) { IsRetryable = true }; } if ((headers.ContainsKey(Constants.HeaderConstants.EtagHeader)) && (!string.IsNullOrEmpty(headers[Constants.HeaderConstants.EtagHeader]))) { currentResult.Etag = headers[Constants.HeaderConstants.EtagHeader]; if (currentOperation.Entity != null) { currentOperation.Entity.ETag = currentResult.Etag; } } // Parse Entity if needed if (currentOperation.OperationType == TableOperationType.Retrieve || (currentOperation.OperationType == TableOperationType.Insert && currentOperation.EchoContent)) { if (headers[Constants.ContentTypeElement].Contains(Constants.JsonNoMetadataAcceptHeaderValue)) { await ReadEntityUsingJsonParserAsync(currentResult, currentOperation, bodyStream, ctx, options, cancellationToken).ConfigureAwait(false); } else { await ReadOdataEntityAsync(currentResult, currentOperation, bodyStream, ctx, options, cancellationToken).ConfigureAwait(false); } } else if (currentOperation.OperationType == TableOperationType.Insert) { currentOperation.Entity.Timestamp = ParseETagForTimestamp(currentResult.Etag); } index++; } return(result); }
internal static Exception UnableToParseTextBeforeSemicolonToInteger(StorageExtendedErrorInformation error) { return(new UnexpectedStorageResponseException(error, "Unable to parse text on first line before semicolon as integer")); }
internal static Exception ConflictExceptionMessageShouldHaveSemicolonOnFirstLine(StorageExtendedErrorInformation error) { return(new UnexpectedStorageResponseException(error, "Conflict exception message should have semicolon on first line")); }
internal static Exception ConflictExceptionMessageShouldHaveExactlyThreeLines(StorageExtendedErrorInformation error) { return(new UnexpectedStorageResponseException(error, "Conflict exception message should have exactly 3 lines")); }
internal static Exception ErrorCodeShouldBeEntityAlreadyExists(StorageExtendedErrorInformation error) { return(new UnexpectedStorageResponseException(error, "Erorr code should be indicated as 'EntityAlreadyExists' but was: " + error.ErrorCode)); }
UnexpectedStorageResponseException(StorageExtendedErrorInformation error, string details) : base("Unexpected Table Storage response. Details: " + details) { Error = error; }
internal static Task <IList <TableResult> > TableBatchOperationPostProcess(IList <TableResult> result, TableBatchOperation batch, RESTCommand <IList <TableResult> > cmd, HttpResponseMessage resp, OperationContext ctx, TableRequestOptions options, string accountName) { return(Task.Run(() => { ODataMessageReaderSettings readerSettings = new ODataMessageReaderSettings(); readerSettings.MessageQuotas = new ODataMessageQuotas() { MaxPartsPerBatch = TableConstants.TableServiceMaxResults, MaxReceivedMessageSize = TableConstants.TableServiceMaxPayload }; using (ODataMessageReader responseReader = new ODataMessageReader(new HttpResponseAdapterMessage(resp, cmd.ResponseStream), readerSettings)) { // create a reader ODataBatchReader reader = responseReader.CreateODataBatchReader(); // Initial => changesetstart if (reader.State == ODataBatchReaderState.Initial) { reader.Read(); } if (reader.State == ODataBatchReaderState.ChangesetStart) { // ChangeSetStart => Operation reader.Read(); } int index = 0; bool failError = false; bool failUnexpected = false; while (reader.State == ODataBatchReaderState.Operation) { TableOperation currentOperation = batch[index]; TableResult currentResult = new TableResult() { Result = currentOperation.Entity }; result.Add(currentResult); ODataBatchOperationResponseMessage mimePartResponseMessage = reader.CreateOperationResponseMessage(); string contentType = mimePartResponseMessage.GetHeader(Constants.ContentTypeElement); currentResult.HttpStatusCode = mimePartResponseMessage.StatusCode; // Validate Status Code if (currentOperation.OperationType == TableOperationType.Insert) { failError = mimePartResponseMessage.StatusCode == (int)HttpStatusCode.Conflict; if (currentOperation.EchoContent) { failUnexpected = mimePartResponseMessage.StatusCode != (int)HttpStatusCode.Created; } else { failUnexpected = mimePartResponseMessage.StatusCode != (int)HttpStatusCode.NoContent; } } else if (currentOperation.OperationType == TableOperationType.Retrieve) { if (mimePartResponseMessage.StatusCode == (int)HttpStatusCode.NotFound) { index++; // Operation => next reader.Read(); continue; } failUnexpected = mimePartResponseMessage.StatusCode != (int)HttpStatusCode.OK; } else { failError = mimePartResponseMessage.StatusCode == (int)HttpStatusCode.NotFound; failUnexpected = mimePartResponseMessage.StatusCode != (int)HttpStatusCode.NoContent; } if (failError) { // If the parse error is null, then don't get the extended error information and the StorageException will contain SR.ExtendedErrorUnavailable message. if (cmd.ParseError != null) { cmd.CurrentResult.ExtendedErrorInformation = cmd.ParseError(mimePartResponseMessage.GetStream(), resp, contentType); } else { cmd.CurrentResult.ExtendedErrorInformation = StorageExtendedErrorInformation.ReadFromStream(mimePartResponseMessage.GetStream()); } cmd.CurrentResult.HttpStatusCode = mimePartResponseMessage.StatusCode; throw new StorageException( cmd.CurrentResult, cmd.CurrentResult.ExtendedErrorInformation != null ? cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage : SR.ExtendedErrorUnavailable, null) { IsRetryable = false }; } if (failUnexpected) { // If the parse error is null, then don't get the extended error information and the StorageException will contain SR.ExtendedErrorUnavailable message. if (cmd.ParseError != null) { cmd.CurrentResult.ExtendedErrorInformation = cmd.ParseError(mimePartResponseMessage.GetStream(), resp, contentType); } cmd.CurrentResult.HttpStatusCode = mimePartResponseMessage.StatusCode; string indexString = Convert.ToString(index); // Attempt to extract index of failing entity from extended error info if (cmd.CurrentResult.ExtendedErrorInformation != null && !string.IsNullOrEmpty(cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage)) { string tempIndex = TableRequest.ExtractEntityIndexFromExtendedErrorInformation(cmd.CurrentResult); if (!string.IsNullOrEmpty(tempIndex)) { indexString = tempIndex; } } throw new StorageException(cmd.CurrentResult, string.Format(SR.BatchErrorInOperation, indexString), null) { IsRetryable = true }; } // Update etag if (!string.IsNullOrEmpty(mimePartResponseMessage.GetHeader("ETag"))) { currentResult.Etag = mimePartResponseMessage.GetHeader("ETag"); if (currentOperation.Entity != null) { currentOperation.Entity.ETag = currentResult.Etag; } } // Parse Entity if needed if (currentOperation.OperationType == TableOperationType.Retrieve || (currentOperation.OperationType == TableOperationType.Insert && currentOperation.EchoContent)) { if (mimePartResponseMessage.GetHeader(Constants.ContentTypeElement).Contains(Constants.JsonContentTypeHeaderValue) && mimePartResponseMessage.GetHeader(Constants.ContentTypeElement).Contains(Constants.NoMetadata)) { ReadEntityUsingJsonParser(currentResult, currentOperation, mimePartResponseMessage.GetStream(), ctx, options); } else { ReadOdataEntity(currentResult, currentOperation, mimePartResponseMessage, ctx, readerSettings, accountName); } } else if (currentOperation.OperationType == TableOperationType.Insert) { currentOperation.Entity.Timestamp = ParseETagForTimestamp(currentResult.Etag); } index++; // Operation => reader.Read(); } } return result; })); }
internal static async Task <IList <BlobBatchSubOperationResponse> > BatchPostProcessAsync(IList <BlobBatchSubOperationResponse> result, RESTCommand <IList <BlobBatchSubOperationResponse> > cmd, IList <HttpStatusCode> successfulStatusCodes, HttpResponseMessage response, OperationContext ctx, /*BlobRequestOptions options,*/ CancellationToken cancellationToken) { // TODO reimplement to use ASP.NET extensions to parse multipart content from content stream Stream responseStream = cmd.ResponseStream; StreamReader streamReader = new StreamReader(responseStream); List <BlobBatchSubOperationError> errors = new List <BlobBatchSubOperationError>(); string currentLine = await streamReader.ReadLineAsync().ConfigureAwait(false); currentLine = await streamReader.ReadLineAsync().ConfigureAwait(false); while ((currentLine != null) && !(currentLine.StartsWith(@"--batchresponse") && currentLine.EndsWith("--"))) { while ((currentLine != null) && !currentLine.StartsWith("Content-ID")) { currentLine = await streamReader.ReadLineAsync().ConfigureAwait(false); } int operationIndex = int.Parse(currentLine.Split(':')[1]); while ((currentLine != null) && !currentLine.StartsWith("HTTP")) { currentLine = await streamReader.ReadLineAsync().ConfigureAwait(false); } // The first line of the response looks like this: // HTTP/1.1 204 No Content // The HTTP status code is chars 9 - 11. var statusCode = (HttpStatusCode)int.Parse(currentLine.Substring(9, 3)); currentLine = await streamReader.ReadLineAsync().ConfigureAwait(false); if (successfulStatusCodes.Contains(statusCode)) { BlobBatchSubOperationResponse subResponse = new BlobBatchSubOperationResponse { StatusCode = statusCode, OperationIndex = operationIndex, Headers = await ParseSubRequestHeadersAsync(currentLine, streamReader).ConfigureAwait(false) }; // Can add logic to parse body here if necessary. result.Add(subResponse); } else { // ProcessExpectedStatusCodesNoException? How to share list of expected codes to avoid duplication? Change list type to HttpStatusCode. BlobBatchSubOperationError error = new BlobBatchSubOperationError { OperationIndex = operationIndex, StatusCode = statusCode }; Dictionary <string, string> headers = await ParseSubRequestHeadersAsync(currentLine, streamReader).ConfigureAwait(false); error.ErrorCode = headers[Constants.HeaderConstants.StorageErrorCodeHeader]; int contentLength = int.Parse(headers[Constants.HeaderConstants.ContentLengthHeader]); //TODO: Add check that contentLength > 0 in case we support HEAD operations. char[] errorInfoBuffer = new char[contentLength]; var position = 0; while (position < contentLength) { errorInfoBuffer[position++] = (char)streamReader.Read(); } error.ExtendedErrorInformation = await StorageExtendedErrorInformation.ReadFromStreamAsync(new MemoryStream(Encoding.UTF8.GetBytes(errorInfoBuffer))).ConfigureAwait(false); // Is this safe/performant? errors.Add(error); } currentLine = await streamReader.ReadLineAsync().ConfigureAwait(false); } if (errors.Count > 0) { BlobBatchException ex = new BlobBatchException { ErrorResponses = errors, SuccessfulResponses = result }; throw ex; } return(result); }