using System; using System.IO; using System.Runtime.InteropServices; namespace EmbedIO.Net.Internal { internal class RequestStream : Stream { private readonly Stream _stream; private readonly byte[] _buffer; private int _offset; private int _length; private long _remainingBody; internal RequestStream(Stream stream, byte[] buffer, int offset, int length, long contentLength = -1) { _stream = stream; _buffer = buffer; _offset = offset; _length = length; _remainingBody = contentLength; } public override bool CanRead => true; public override bool CanSeek => false; public override bool CanWrite => false; public override long Length => throw new NotSupportedException(); public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); } public override void Flush() { } public override int Read([In, Out] byte[] buffer, int offset, int count) { // Call FillFromBuffer to check for buffer boundaries even when remaining_body is 0 var nread = FillFromBuffer(buffer, offset, count); if (nread == -1) { // No more bytes available (Content-Length) return 0; } if (nread > 0) { return nread; } nread = _stream.Read(buffer, offset, count); if (nread > 0 && _remainingBody > 0) { _remainingBody -= nread; } return nread; } public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); public override void SetLength(long value) => throw new NotSupportedException(); public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); // Returns 0 if we can keep reading from the base stream, // > 0 if we read something from the buffer. // -1 if we had a content length set and we finished reading that many bytes. private int FillFromBuffer(byte[] buffer, int off, int count) { if (buffer == null) { throw new ArgumentNullException(nameof(buffer)); } if (off < 0) { throw new ArgumentOutOfRangeException(nameof(off), "< 0"); } if (count < 0) { throw new ArgumentOutOfRangeException(nameof(count), "< 0"); } var len = buffer.Length; if (off > len) { throw new ArgumentException("destination offset is beyond array size"); } if (off > len - count) { throw new ArgumentException("Reading would overrun buffer"); } if (_remainingBody == 0) { return -1; } if (_length == 0) { return 0; } var size = Math.Min(_length, count); if (_remainingBody > 0) { size = (int) Math.Min(size, _remainingBody); } if (_offset > _buffer.Length - size) { size = Math.Min(size, _buffer.Length - _offset); } if (size == 0) { return 0; } Buffer.BlockCopy(_buffer, _offset, buffer, off, size); _offset += size; _length -= size; if (_remainingBody > 0) { _remainingBody -= size; } return size; } } }