/// <summary> /// Writes a single OData batch response. /// </summary> /// <param name="writer">The <see cref="ODataBatchWriter"/>.</param> /// <param name="response">The response message.</param> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param> /// <returns>A task object representing writing the given batch response using the given writer.</returns> public static async Task WriteMessageAsync(ODataBatchWriter writer, HttpResponseMessage response, CancellationToken cancellationToken) { if (writer == null) { throw Error.ArgumentNull("writer"); } if (response == null) { throw Error.ArgumentNull("response"); } ODataBatchOperationResponseMessage batchResponse = writer.CreateOperationResponseMessage(); batchResponse.StatusCode = (int)response.StatusCode; foreach (KeyValuePair <string, IEnumerable <string> > header in response.Headers) { batchResponse.SetHeader(header.Key, String.Join(",", header.Value)); } if (response.Content != null) { foreach (KeyValuePair <string, IEnumerable <string> > header in response.Content.Headers) { batchResponse.SetHeader(header.Key, String.Join(",", header.Value)); } using (Stream stream = batchResponse.GetStream()) { cancellationToken.ThrowIfCancellationRequested(); await response.Content.CopyToAsync(stream); } } }
/// <summary> /// Writes a single OData batch response. /// </summary> /// <param name="writer">The <see cref="ODataBatchWriter"/>.</param> /// <param name="context">The message context.</param> public static async Task WriteMessageAsync(ODataBatchWriter writer, HttpContext context) { if (writer == null) { throw Error.ArgumentNull("writer"); } if (context == null) { throw Error.ArgumentNull("context"); } string contentId = (context.Request != null) ? context.Request.GetODataContentId() : String.Empty; ODataBatchOperationResponseMessage batchResponse = writer.CreateOperationResponseMessage(contentId); batchResponse.StatusCode = context.Response.StatusCode; foreach (KeyValuePair <string, StringValues> header in context.Response.Headers) { batchResponse.SetHeader(header.Key, String.Join(",", header.Value.ToArray())); } if (context.Response.Body != null && context.Response.Body.Length != 0) { using (Stream stream = batchResponse.GetStream()) { context.RequestAborted.ThrowIfCancellationRequested(); context.Response.Body.Seek(0L, SeekOrigin.Begin); await context.Response.Body.CopyToAsync(stream); } } }
private static BatchWriterStatesTestSetupResult GetOperationStream(object message, WriterTestConfiguration testConfiguration) { BatchWriterStatesTestSetupResult result = new BatchWriterStatesTestSetupResult { Message = message }; ODataBatchOperationRequestMessage requestMessage = message as ODataBatchOperationRequestMessage; if (requestMessage != null) { if (testConfiguration.Synchronous) { result.MessageStream = requestMessage.GetStream(); return(result); } else { // TODO: 191417: Enable async Tests on Phone and Silverlight when Product Supports them #if SILVERLIGHT || WINDOWS_PHONE throw new TaupoNotSupportedException("This test is not supported in aSynchronous mode in Silverlight or Phone"); #else var t = requestMessage.GetStreamAsync(); t.Wait(); result.MessageStream = t.Result; return(result); #endif } } ODataBatchOperationResponseMessage responseMessage = message as ODataBatchOperationResponseMessage; if (responseMessage != null) { if (testConfiguration.Synchronous) { result.MessageStream = responseMessage.GetStream(); return(result); } else { // TODO: Enable async Tests on Phone and Silverlight when Product Supports them #if SILVERLIGHT || WINDOWS_PHONE throw new TaupoNotSupportedException("This test is not supported in aSynchronous mode in Silverlight or Phone"); #else var t = responseMessage.GetStreamAsync(); t.Wait(); result.MessageStream = t.Result; return(result); #endif } } return(null); }
private static Stream GetMessageStream(ODataBatchOperationResponseMessage responseMessage, WriterTestConfiguration testConfiguration) { if (testConfiguration.Synchronous) { return(responseMessage.GetStream()); } else { var t = responseMessage.GetStreamAsync(); t.Wait(); return(t.Result); } }
private void WriteOperation(ODataBatchWriter writer, OeOperationMessage operation) { ODataBatchOperationResponseMessage operationMessage = writer.CreateOperationResponseMessage(operation.ContentId); operationMessage.SetHeader("Location", operation.RequestUrl.AbsoluteUri); operationMessage.SetHeader(ODataConstants.ContentTypeHeader, operation.ContentType); operationMessage.StatusCode = (int)operation.StatusCode; if (operation.StatusCode != HttpStatusCode.NoContent) { using (Stream stream = operationMessage.GetStream()) WriteEntity(stream, operation.EntityItem); } }
private static BatchWriterStatesTestSetupResult GetOperationStream(object message, WriterTestConfiguration testConfiguration) { BatchWriterStatesTestSetupResult result = new BatchWriterStatesTestSetupResult { Message = message }; ODataBatchOperationRequestMessage requestMessage = message as ODataBatchOperationRequestMessage; if (requestMessage != null) { if (testConfiguration.Synchronous) { result.MessageStream = requestMessage.GetStream(); return(result); } else { var t = requestMessage.GetStreamAsync(); t.Wait(); result.MessageStream = t.Result; return(result); } } ODataBatchOperationResponseMessage responseMessage = message as ODataBatchOperationResponseMessage; if (responseMessage != null) { if (testConfiguration.Synchronous) { result.MessageStream = responseMessage.GetStream(); return(result); } else { var t = responseMessage.GetStreamAsync(); t.Wait(); result.MessageStream = t.Result; return(result); } } return(null); }
private void ClientReadBatchResponse(byte[] responsePayload, BodyContentType bodyContentType) { IODataResponseMessage responseMessage = new InMemoryMessage() { Stream = new MemoryStream(responsePayload) }; responseMessage.SetHeader(ODataConstants.ContentTypeHeader, batchContentTypeApplicationJson); using (ODataMessageReader messageReader = new ODataMessageReader(responseMessage, new ODataMessageReaderSettings(), null)) { ODataBatchReader batchReader = messageReader.CreateODataBatchReader(); while (batchReader.Read()) { switch (batchReader.State) { case ODataBatchReaderState.Operation: // Encountered an operation (either top-level or in a change set) ODataBatchOperationResponseMessage operationMessage = batchReader.CreateOperationResponseMessage(); if (operationMessage.StatusCode == 200) { using (Stream operationMessageBody = operationMessage.GetStream()) { // Verify the bytes in the response body. byte[] sampleBytes = bodyContentType == BodyContentType.Textual ? Encoding.UTF8.GetBytes("\"" + this.textualSampleStringB + "\"") : this.binarySampleBytesB; Assert.Equal(operationMessageBody.Length, sampleBytes.Length); foreach (byte samplebyte in sampleBytes) { Assert.Equal(samplebyte, operationMessageBody.ReadByte()); } } } else { Assert.True(201 == operationMessage.StatusCode); } break; } } } }
/// <summary> /// Processed the operation response reported by the batch reader. /// This is a side-effecting method that is tied deeply to how it is used in the batch processing pipeline. /// </summary> /// <param name="batchReader">The batch reader to get the operation response from.</param> /// <param name="isChangesetOperation">True if the current operation is inside a changeset (implying CUD, not query)</param> /// <returns>An exception if the operation response is an error response, null for success response.</returns> private Exception ProcessCurrentOperationResponse(ODataBatchReader batchReader, bool isChangesetOperation) { Debug.Assert(batchReader != null, "batchReader != null"); Debug.Assert(batchReader.State == ODataBatchReaderState.Operation, "This method requires the batch reader to be on an operation."); ODataBatchOperationResponseMessage operationResponseMessage = batchReader.CreateOperationResponseMessage(); Descriptor descriptor = null; if (isChangesetOperation) { // We need to peek at the content-Id before handing the response to the user, so we can expose the Descriptor them. // We're OK with this exception to our general rule of not using them before ReceivingResponse event is fired. this.entryIndex = this.ValidateContentID(operationResponseMessage.ContentId); descriptor = this.ChangedEntries[entryIndex]; } // If we hit en error inside a batch, we will never expose a descriptor since we don't know which one to return. // The descriptor we fetched above based on the content-ID is bogus because the server returns an errounous content-id when // it hits an error inside batch. if (!WebUtil.SuccessStatusCode((HttpStatusCode)operationResponseMessage.StatusCode)) { descriptor = null; } this.RequestInfo.Context.FireReceivingResponseEvent(new ReceivingResponseEventArgs(operationResponseMessage, descriptor, true)); // We need to know if the content of the operation response is empty or not. // We also need to cache the entire content, since in case of GET response the response itself will be parsed // lazily and so it can happen that we will move the batch reader after this operation before we actually read // the content of the operation. Stream originalOperationResponseContentStream = operationResponseMessage.GetStream(); if (originalOperationResponseContentStream == null) { Error.ThrowBatchExpectedResponse(InternalError.NullResponseStream); } MemoryStream operationResponseContentStream; try { operationResponseContentStream = new MemoryStream(); WebUtil.CopyStream(originalOperationResponseContentStream, operationResponseContentStream, ref this.streamCopyBuffer); operationResponseContentStream.Position = 0; } finally { originalOperationResponseContentStream.Dispose(); } this.currentOperationResponse = new CurrentOperationResponse( (HttpStatusCode)operationResponseMessage.StatusCode, operationResponseMessage.Headers, operationResponseContentStream); Version responseVersion; string headerName = XmlConstants.HttpODataVersion; return(BaseSaveResult.HandleResponse( this.RequestInfo, this.currentOperationResponse.StatusCode, this.currentOperationResponse.Headers.GetHeader(headerName), () => this.currentOperationResponse.ContentStream, false, out responseVersion)); }
internal static IList <TableResult> TableBatchOperationPostProcess(IList <TableResult> result, TableBatchOperation batch, RESTCommand <IList <TableResult> > cmd, HttpWebResponse resp, OperationContext ctx, TableRequestOptions options, string accountName) { 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); } cmd.CurrentResult.HttpStatusCode = mimePartResponseMessage.StatusCode; if (!string.IsNullOrEmpty(cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage)) { string msg = cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage; cmd.CurrentResult.HttpStatusMessage = msg.Substring(0, msg.IndexOf("\n", StringComparison.Ordinal)); } else { cmd.CurrentResult.HttpStatusMessage = mimePartResponseMessage.StatusCode.ToString(CultureInfo.InvariantCulture); } 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; if (!string.IsNullOrEmpty(cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage)) { string msg = cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage; cmd.CurrentResult.HttpStatusMessage = msg.Substring(0, msg.IndexOf("\n", StringComparison.Ordinal)); } else { cmd.CurrentResult.HttpStatusMessage = mimePartResponseMessage.StatusCode.ToString(CultureInfo.InvariantCulture); } string indexString = Convert.ToString(index, CultureInfo.InvariantCulture); // 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(CultureInfo.CurrentCulture, SR.BatchErrorInOperation, indexString), null) { IsRetryable = true }; } // Update etag if (!string.IsNullOrEmpty(mimePartResponseMessage.GetHeader(Constants.HeaderConstants.EtagHeader))) { currentResult.Etag = mimePartResponseMessage.GetHeader(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 (mimePartResponseMessage.GetHeader(Constants.ContentTypeElement).Contains(Constants.JsonNoMetadataAcceptHeaderValue)) { ReadEntityUsingJsonParser(currentResult, currentOperation, mimePartResponseMessage.GetStream(), ctx, options); } else { ReadOdataEntity(currentResult, currentOperation, mimePartResponseMessage, ctx, readerSettings, accountName, options); } } else if (currentOperation.OperationType == TableOperationType.Insert) { currentOperation.Entity.Timestamp = ParseETagForTimestamp(currentResult.Etag); } index++; // Operation => reader.Read(); } } return(result); }
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); }
private static Stream GetMessageStream(ODataBatchOperationResponseMessage responseMessage, WriterTestConfiguration testConfiguration) { if (testConfiguration.Synchronous) { return responseMessage.GetStream(); } else { var t = responseMessage.GetStreamAsync(); t.Wait(); return t.Result; } }
/// <summary> /// Writes a single OData batch response. /// </summary> /// <param name="writer">The <see cref="ODataBatchWriter"/>.</param> /// <param name="context">The message context.</param> /// <param name="asyncWriter">Whether or not the writer is in async mode. </param> public static async Task WriteMessageAsync(ODataBatchWriter writer, HttpContext context, bool asyncWriter) { if (writer == null) { throw Error.ArgumentNull("writer"); } if (context == null) { throw Error.ArgumentNull("context"); } string contentId = (context.Request != null) ? context.Request.GetODataContentId() : String.Empty; ODataBatchOperationResponseMessage batchResponse = asyncWriter ? await writer.CreateOperationResponseMessageAsync(contentId) : writer.CreateOperationResponseMessage(contentId); batchResponse.StatusCode = context.Response.StatusCode; foreach (KeyValuePair <string, StringValues> header in context.Response.Headers) { batchResponse.SetHeader(header.Key, String.Join(",", header.Value.ToArray())); } if (context.Response.Body != null && context.Response.Body.Length != 0) { using (Stream stream = asyncWriter ? await batchResponse.GetStreamAsync() : batchResponse.GetStream()) { context.RequestAborted.ThrowIfCancellationRequested(); context.Response.Body.Seek(0L, SeekOrigin.Begin); await context.Response.Body.CopyToAsync(stream); // Close and release the stream for the individual response ODataBatchStream batchStream = context.Response.Body as ODataBatchStream; if (batchStream != null) { if (asyncWriter) { await batchStream.InternalDisposeAsync(); } else { batchStream.InternalDispose(); } } } } }