private void StoreBody(Stream output, long contentLength) { // argument checks Debug.Assert(output != null); Debug.Assert(output.CanWrite); Debug.Assert(0 <= contentLength); // state checks HeaderBuffer headerBuffer = this.headerBuffer; Debug.Assert(headerBuffer != null); // write body bytes in the header buffer to the output int bodyBytesInHeaderBufferLength = headerBuffer.Limit - headerBuffer.Next; if (bodyBytesInHeaderBufferLength <= contentLength) { WriteTo(headerBuffer, output, headerBuffer.Next, bodyBytesInHeaderBufferLength); } else { // there is data of the next message bodyBytesInHeaderBufferLength = (int)contentLength; WriteTo(headerBuffer, output, headerBuffer.Next, bodyBytesInHeaderBufferLength); headerBuffer.SetPrefetchedBytes(headerBuffer.Next + bodyBytesInHeaderBufferLength); } // write rest of body bytes to the output // the memoryBlock is used as just intermediate buffer instead of storing media byte[] memoryBlock = EnsureMemoryBlockAllocated(); long remaining = contentLength - bodyBytesInHeaderBufferLength; while (0 < remaining) { long count = Math.Min(remaining, memoryBlock.Length); int readCount = ReadBytes(memoryBlock, 0, (int)count); Debug.Assert(0 < readCount); // ReadBytes() throws an exception on end of stream output.Write(memoryBlock, 0, readCount); remaining -= readCount; } return; }
public void SkipBody(long contentLength) { // argument checks if (contentLength < 0) { throw new ArgumentOutOfRangeException(nameof(contentLength)); } // state checks HeaderBuffer headerBuffer = this.headerBuffer; if (headerBuffer == null) { throw new InvalidOperationException(); } if (this.bodyLength != 0) { throw new InvalidOperationException("This buffer has already handled a message body."); } Debug.Assert(this.bodyStream == null); // skip the body storing its bytes // The media to store body depends on its length and the current margin. Stream bodyStream = null; try { // Note that the some body bytes may be read into the header buffer. // Unread bytes in the header buffer at this point are body bytes. // That is, range [headerBuffer.Next - headerBuffer.Limit). int bodyBytesInHeaderBufferLength = headerBuffer.Limit - headerBuffer.Next; long restLen = contentLength - bodyBytesInHeaderBufferLength; if (restLen <= headerBuffer.Margin) { // The body is to be stored in the rest of header buffer. (tiny body) // read body bytes into the rest of the header buffer if (0 <= restLen) { Debug.Assert(restLen <= int.MaxValue); FillBuffer(headerBuffer, (int)restLen); Debug.Assert(contentLength == headerBuffer.Limit - headerBuffer.Next); Debug.Assert(this.MemoryBlock == null); } else { // there is data of the next message Debug.Assert(contentLength <= int.MaxValue); int offset = headerBuffer.Next + (int)contentLength; headerBuffer.SetPrefetchedBytes(offset); } } else { byte[] memoryBlock = EnsureMemoryBlockAllocated(); if (contentLength <= memoryBlock.Length) { // The body is to be stored in a memory block. (small body) // copy body bytes in the header buffer to this buffer CopyFrom(headerBuffer, headerBuffer.Next, bodyBytesInHeaderBufferLength); // read rest of body bytes Debug.Assert(restLen <= int.MaxValue); FillBuffer((int)restLen); Debug.Assert(contentLength == (this.Limit - this.Next)); } else { // The body is to be stored in a stream. // determine which medium is used to store body, memory or file if (contentLength <= BodyStreamThreshold) { // use memory stream (medium body) Debug.Assert(contentLength <= int.MaxValue); bodyStream = new MemoryStream((int)contentLength); } else { // use temp file stream (large body) bodyStream = Util.CreateTempFileStream(); } StoreBody(bodyStream, contentLength); } } // update state this.bodyLength = contentLength; this.bodyStream = bodyStream; } catch { DisposableUtil.DisposeSuppressingErrors(bodyStream); throw; } return; }