private async Task <ODataResponse> ReadResponse(ODataBatchReader odataReader) { var batch = new List <ODataResponse>(); while (odataReader.Read()) { switch (odataReader.State) { case ODataBatchReaderState.ChangesetStart: break; case ODataBatchReaderState.Operation: var operationMessage = odataReader.CreateOperationResponseMessage(); if (operationMessage.StatusCode == (int)HttpStatusCode.NoContent) { batch.Add(ODataResponse.FromStatusCode(operationMessage.StatusCode)); } else if (operationMessage.StatusCode >= (int)HttpStatusCode.BadRequest) { batch.Add(ODataResponse.FromStatusCode( operationMessage.StatusCode, #if SILVERLIGHT operationMessage.GetStream())); } #else await operationMessage.GetStreamAsync().ConfigureAwait(false))); #endif else { batch.Add(await GetResponseAsync(operationMessage).ConfigureAwait(false)); } break;
private async Task <ODataResponse> ReadResponse(ODataBatchReader odataReader) { var batch = new List <ODataResponse>(); while (odataReader.Read()) { switch (odataReader.State) { case ODataBatchReaderState.ChangesetStart: break; case ODataBatchReaderState.Operation: var operationMessage = odataReader.CreateOperationResponseMessage(); if (operationMessage.StatusCode == (int)HttpStatusCode.NoContent) { batch.Add(ODataResponse.FromStatusCode(operationMessage.StatusCode)); } else { batch.Add(await GetResponseAsync(operationMessage)); } break; case ODataBatchReaderState.ChangesetEnd: break; } } return(ODataResponse.FromBatch(batch)); }
private void ClientReadSingletonBatchResponse(byte[] responsePayload, string batchContentType) { IODataResponseMessage responseMessage = new InMemoryMessage() { Stream = new MemoryStream(responsePayload) }; responseMessage.SetHeader("Content-Type", batchContentType); using (ODataMessageReader messageReader = new ODataMessageReader(responseMessage, new ODataMessageReaderSettings(), this.userModel)) { 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 (ODataMessageReader innerMessageReader = new ODataMessageReader(operationMessage, new ODataMessageReaderSettings(), this.userModel)) { ODataReader reader = innerMessageReader.CreateODataResourceReader(); while (reader.Read()) { if (reader.State == ODataReaderState.ResourceEnd) { ODataResource entry = reader.Item as ODataResource; Assert.Equal(10, entry.Properties.Single(p => p.Name == "WebId").Value); Assert.Equal("WebSingleton", entry.Properties.Single(p => p.Name == "Name").Value); } } } // The only two messages with HTTP-200 response codes are the two GET requests with content id value of null. // Verify that: for multipart batch the content id of the response is matching that of the request; // for Json batch the content id of the response is not null. Assert.True( (batchReader is ODataJsonLightBatchReader && operationMessage.ContentId != null) || (batchReader is ODataMultipartMixedBatchReader && operationMessage.ContentId == null)); } break; } } } }
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; } } } }
private async Task <ODataResponse> ReadResponse(ODataBatchReader odataReader) { var batch = new List <ODataResponse>(); while (odataReader.Read()) { switch (odataReader.State) { case ODataBatchReaderState.ChangesetStart: break; case ODataBatchReaderState.Operation: var operationMessage = odataReader.CreateOperationResponseMessage(); if (operationMessage.StatusCode == (int)HttpStatusCode.NoContent) { batch.Add(ODataResponse.FromStatusCode(TypeCache, operationMessage.StatusCode, operationMessage.Headers)); } else if (operationMessage.StatusCode >= (int)HttpStatusCode.BadRequest) { batch.Add(ODataResponse.FromStatusCode(TypeCache, operationMessage.StatusCode, operationMessage.Headers, await operationMessage.GetStreamAsync().ConfigureAwait(false), _session.Settings.WebRequestExceptionMessageSource)); } else { batch.Add(await GetResponseAsync(operationMessage).ConfigureAwait(false)); } break; case ODataBatchReaderState.ChangesetEnd: break; } } return(ODataResponse.FromBatch(TypeCache, batch)); }
private Exception ProcessCurrentOperationResponse(ODataBatchReader batchReader) { MemoryStream stream2; Version version; IODataResponseMessage message = batchReader.CreateOperationResponseMessage(); Stream input = message.GetStream(); if (input == null) { System.Data.Services.Client.Error.ThrowBatchExpectedResponse(InternalError.NullResponseStream); } try { stream2 = new MemoryStream(); WebUtil.CopyStream(input, stream2, ref this.streamCopyBuffer); stream2.Position = 0L; } finally { input.Dispose(); } this.currentOperationResponse = new CurrentOperationResponse((HttpStatusCode)message.StatusCode, message.Headers, stream2); return(BaseSaveResult.HandleResponse(base.RequestInfo, this.currentOperationResponse.StatusCode, this.currentOperationResponse.GetHeader("DataServiceVersion"), () => this.currentOperationResponse.ContentStream, false, out version)); }
/// <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); }
private async Task<ODataResponse> ReadResponse(ODataBatchReader odataReader) { var batch = new List<ODataResponse>(); while (odataReader.Read()) { switch (odataReader.State) { case ODataBatchReaderState.ChangesetStart: break; case ODataBatchReaderState.Operation: var operationMessage = odataReader.CreateOperationResponseMessage(); if (operationMessage.StatusCode == (int)HttpStatusCode.NoContent) batch.Add(ODataResponse.FromStatusCode(operationMessage.StatusCode)); else batch.Add(await GetResponseAsync(operationMessage)); break; case ODataBatchReaderState.ChangesetEnd: break; } } return ODataResponse.FromBatch(batch); }
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 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); }
private Exception ProcessCurrentOperationResponse(ODataBatchReader batchReader) { MemoryStream stream2; Version version; IODataResponseMessage message = batchReader.CreateOperationResponseMessage(); Stream input = message.GetStream(); if (input == null) { System.Data.Services.Client.Error.ThrowBatchExpectedResponse(InternalError.NullResponseStream); } try { stream2 = new MemoryStream(); WebUtil.CopyStream(input, stream2, ref this.streamCopyBuffer); stream2.Position = 0L; } finally { input.Dispose(); } this.currentOperationResponse = new CurrentOperationResponse((HttpStatusCode) message.StatusCode, message.Headers, stream2); return BaseSaveResult.HandleResponse(base.RequestInfo, this.currentOperationResponse.StatusCode, this.currentOperationResponse.GetHeader("DataServiceVersion"), () => this.currentOperationResponse.ContentStream, false, out version); }