/// <summary> /// Returns the cached <see cref="ODataBatchOperationRequestMessage"/> for reading the content of an operation /// in a batch request. /// </summary> /// <returns>The message that can be used to read the content of the batch request operation from.</returns> protected override ODataBatchOperationResponseMessage CreateOperationResponseMessageImplementation() { string responseLine = this.batchStream.ReadFirstNonEmptyLine(); int statusCode = this.ParseResponseLine(responseLine); // Read all headers and create the response message ODataBatchOperationHeaders headers = this.batchStream.ReadHeaders(); if (this.currentContentId == null) { headers.TryGetValue(ODataConstants.ContentIdHeader, out this.currentContentId); } // In responses we don't need to use our batch URL resolver, since there are no cross referencing URLs // so use the URL resolver from the batch message instead. // We don't have correlation of changeset boundary between request and response messages in // changesets, so use null value for groupId. ODataBatchOperationResponseMessage responseMessage = BuildOperationResponseMessage( () => ODataBatchUtils.CreateBatchOperationReadStream(this.batchStream, headers, this), statusCode, headers, this.currentContentId, /*groupId*/ null); //// NOTE: Content-IDs for cross referencing are only supported in request messages; in responses //// we allow a Content-ID header but don't process it (i.e., don't add the content ID to the URL resolver). this.currentContentId = null; return(responseMessage); }
/// <summary> /// Returns the cached <see cref="ODataBatchOperationRequestMessage"/> for reading the content of an operation /// in a batch request. /// </summary> /// <returns>The message that can be used to read the content of the batch request operation from.</returns> protected override ODataBatchOperationRequestMessage CreateOperationRequestMessageImplementation() { string requestLine = this.batchStream.ReadFirstNonEmptyLine(); string httpMethod; Uri requestUri; this.ParseRequestLine(requestLine, out httpMethod, out requestUri); // Read all headers and create the request message ODataBatchOperationHeaders headers = this.batchStream.ReadHeaders(); if (this.batchStream.ChangeSetBoundary != null) { if (this.currentContentId == null) { headers.TryGetValue(ODataConstants.ContentIdHeader, out this.currentContentId); if (this.currentContentId == null) { throw new ODataException(Strings.ODataBatchOperationHeaderDictionary_KeyNotFound(ODataConstants.ContentIdHeader)); } } } else if (this.InputContext.MessageReaderSettings.Version <= ODataVersion.V4) { // For backward compatibility, don't enforce uniqueness of Content-ID // in MultipartMixed outside of a changeset this.PayloadUriConverter.Reset(); } ODataBatchOperationRequestMessage requestMessage = BuildOperationRequestMessage( () => ODataBatchUtils.CreateBatchOperationReadStream(this.batchStream, headers, this), httpMethod, requestUri, headers, this.currentContentId, this.batchStream.ChangeSetBoundary, this.dependsOnIdsTracker.GetDependsOnIds(), /*dependsOnIdsValidationRequired*/ false); if (this.currentContentId != null) { this.dependsOnIdsTracker.AddDependsOnId(this.currentContentId); } this.currentContentId = null; return(requestMessage); }
/// <summary> /// Returns the cached <see cref="ODataBatchOperationRequestMessage"/> for reading the content of an operation /// in a batch request. /// </summary> /// <returns>The message that can be used to read the content of the batch request operation from.</returns> protected override ODataBatchOperationRequestMessage CreateOperationRequestMessageImplementation() { string requestLine = this.batchStream.ReadFirstNonEmptyLine(); string httpMethod; Uri requestUri; this.ParseRequestLine(requestLine, out httpMethod, out requestUri); // Read all headers and create the request message ODataBatchOperationHeaders headers = this.batchStream.ReadHeaders(); if (this.batchStream.ChangeSetBoundary != null) { if (this.currentContentId == null) { headers.TryGetValue(ODataConstants.ContentIdHeader, out this.currentContentId); if (this.currentContentId == null) { throw new ODataException(Strings.ODataBatchOperationHeaderDictionary_KeyNotFound(ODataConstants.ContentIdHeader)); } } } ODataBatchOperationRequestMessage requestMessage = BuildOperationRequestMessage( () => ODataBatchUtils.CreateBatchOperationReadStream(this.batchStream, headers, this), httpMethod, requestUri, headers, this.currentContentId, this.batchStream.ChangeSetBoundary, this.dependsOnIdsTracker.GetDependsOnIds(), /*dependsOnIdsValidationRequired*/ false); if (this.currentContentId != null) { this.dependsOnIdsTracker.AddDependsOnId(this.currentContentId); } this.currentContentId = null; return(requestMessage); }
/// <summary> /// Reads a line (all bytes until a line resource set) from the underlying stream. /// </summary> /// <returns>Returns the string that was read from the underlying stream (not including a terminating line resource set), or null if the end of input was reached.</returns> private string ReadLine() { Debug.Assert(this.batchEncoding != null, "Batch encoding should have been established on first call to SkipToBoundary."); Debug.Assert(this.lineBuffer != null && this.lineBuffer.Length == LineBufferLength, "Line buffer should have been created."); // The number of bytes in the line buffer that make up the line. int lineBufferSize = 0; // Start with the pre-allocated line buffer array. byte[] bytesForString = this.lineBuffer; ODataBatchReaderStreamScanResult scanResult = ODataBatchReaderStreamScanResult.NoMatch; while (scanResult != ODataBatchReaderStreamScanResult.Match) { int byteCount, lineEndStartPosition, lineEndEndPosition; scanResult = this.BatchBuffer.ScanForLineEnd(out lineEndStartPosition, out lineEndEndPosition); switch (scanResult) { case ODataBatchReaderStreamScanResult.NoMatch: // Copy all the bytes in the BatchBuffer into the result byte[] and then continue byteCount = this.BatchBuffer.NumberOfBytesInBuffer; if (byteCount > 0) { // TODO: [Design] Consider security limits for data being read ODataBatchUtils.EnsureArraySize(ref bytesForString, lineBufferSize, byteCount); Buffer.BlockCopy(this.BatchBuffer.Bytes, this.BatchBuffer.CurrentReadPosition, bytesForString, lineBufferSize, byteCount); lineBufferSize += byteCount; } if (this.underlyingStreamExhausted) { if (lineBufferSize == 0) { // If there's nothing more to pull from the underlying stream, and we didn't read anything // in this invocation of ReadLine(), return null to indicate end of input. return(null); } // Nothing more to read; stop looping scanResult = ODataBatchReaderStreamScanResult.Match; this.BatchBuffer.SkipTo(this.BatchBuffer.CurrentReadPosition + byteCount); } else { this.underlyingStreamExhausted = this.BatchBuffer.RefillFrom(this.multipartMixedBatchInputContext.Stream, /*preserveFrom*/ ODataBatchReaderStreamBuffer.BufferLength); } break; case ODataBatchReaderStreamScanResult.PartialMatch: // We found the start of a line end in the buffer but could not verify whether we saw all of it. // This can happen if a line end is represented as \r\n and we found \r at the very last position in the buffer. // In this case we copy the bytes into the result byte[] and continue at the start of the line end; this will guarantee // that the next scan will find the full line end, not find any additional bytes and then skip the full line end. // It is safe to copy the string right here because we will also accept \r as a line end; we are just not sure whether there // will be a subsequent \n. // This can also happen if the last byte in the stream is \r. byteCount = lineEndStartPosition - this.BatchBuffer.CurrentReadPosition; if (byteCount > 0) { ODataBatchUtils.EnsureArraySize(ref bytesForString, lineBufferSize, byteCount); Buffer.BlockCopy(this.BatchBuffer.Bytes, this.BatchBuffer.CurrentReadPosition, bytesForString, lineBufferSize, byteCount); lineBufferSize += byteCount; } if (this.underlyingStreamExhausted) { // Nothing more to read; stop looping scanResult = ODataBatchReaderStreamScanResult.Match; this.BatchBuffer.SkipTo(lineEndStartPosition + 1); } else { this.underlyingStreamExhausted = this.BatchBuffer.RefillFrom(this.multipartMixedBatchInputContext.Stream, /*preserveFrom*/ lineEndStartPosition); } break; case ODataBatchReaderStreamScanResult.Match: // We found a line end in the buffer Debug.Assert(lineEndStartPosition >= this.BatchBuffer.CurrentReadPosition, "Line end must be at or after current position."); Debug.Assert(lineEndEndPosition < this.BatchBuffer.CurrentReadPosition + this.BatchBuffer.NumberOfBytesInBuffer, "Line end must finish withing buffer range."); byteCount = lineEndStartPosition - this.BatchBuffer.CurrentReadPosition; if (byteCount > 0) { ODataBatchUtils.EnsureArraySize(ref bytesForString, lineBufferSize, byteCount); Buffer.BlockCopy(this.BatchBuffer.Bytes, this.BatchBuffer.CurrentReadPosition, bytesForString, lineBufferSize, byteCount); lineBufferSize += byteCount; } this.BatchBuffer.SkipTo(lineEndEndPosition + 1); break; default: throw new ODataException(Strings.General_InternalError(InternalErrorCodes.ODataBatchReaderStream_ReadLine)); } } Debug.Assert(bytesForString != null, "bytesForString != null"); return(this.CurrentEncoding.GetString(bytesForString, 0, lineBufferSize)); }