Beispiel #1
0
        /// <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);
                }
            }
        }
Beispiel #3
0
        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);
     }
 }
Beispiel #5
0
        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;
                    }
                }
            }
        }
Beispiel #8
0
        /// <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);
        }
Beispiel #10
0
        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;
     }
 }
Beispiel #12
0
        /// <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();
                        }
                    }
                }
            }
        }