#region License /* * RequestStream.cs * * This code is derived from RequestStream.cs (System.Net) of Mono * (http://www.mono-project.com). * * The MIT License * * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) * Copyright (c) 2012-2015 sta.blockhead * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #endregion #region Authors /* * Authors: * - Gonzalo Paniagua Javier */ #endregion using System; using System.IO; namespace WebSocketSharp.Net { internal class RequestStream : Stream { #region Private Fields private long _bodyLeft; private byte[] _buffer; private int _count; private bool _disposed; private int _offset; private Stream _stream; #endregion #region Internal Constructors internal RequestStream (Stream stream, byte[] buffer, int offset, int count) : this (stream, buffer, offset, count, -1) { } internal RequestStream ( Stream stream, byte[] buffer, int offset, int count, long contentLength) { _stream = stream; _buffer = buffer; _offset = offset; _count = count; _bodyLeft = contentLength; } #endregion #region Public Properties public override bool CanRead { get { return true; } } public override bool CanSeek { get { return false; } } public override bool CanWrite { get { return false; } } public override long Length { get { throw new NotSupportedException (); } } public override long Position { get { throw new NotSupportedException (); } set { throw new NotSupportedException (); } } #endregion #region Private Methods // 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 offset, int count) { if (buffer == null) throw new ArgumentNullException ("buffer"); if (offset < 0) throw new ArgumentOutOfRangeException ("offset", "A negative value."); if (count < 0) throw new ArgumentOutOfRangeException ("count", "A negative value."); var len = buffer.Length; if (offset + count > len) throw new ArgumentException ( "The sum of 'offset' and 'count' is greater than 'buffer' length."); if (_bodyLeft == 0) return -1; if (_count == 0 || count == 0) return 0; if (count > _count) count = _count; if (_bodyLeft > 0 && count > _bodyLeft) count = (int) _bodyLeft; Buffer.BlockCopy (_buffer, _offset, buffer, offset, count); _offset += count; _count -= count; if (_bodyLeft > 0) _bodyLeft -= count; return count; } #endregion #region Public Methods public override IAsyncResult BeginRead ( byte[] buffer, int offset, int count, AsyncCallback callback, object state) { if (_disposed) throw new ObjectDisposedException (GetType ().ToString ()); var nread = fillFromBuffer (buffer, offset, count); if (nread > 0 || nread == -1) { var ares = new HttpStreamAsyncResult (callback, state); ares.Buffer = buffer; ares.Offset = offset; ares.Count = count; ares.SyncRead = nread > 0 ? nread : 0; ares.Complete (); return ares; } // Avoid reading past the end of the request to allow for HTTP pipelining. if (_bodyLeft >= 0 && count > _bodyLeft) count = (int) _bodyLeft; return _stream.BeginRead (buffer, offset, count, callback, state); } public override IAsyncResult BeginWrite ( byte[] buffer, int offset, int count, AsyncCallback callback, object state) { throw new NotSupportedException (); } public override void Close () { _disposed = true; } public override int EndRead (IAsyncResult asyncResult) { if (_disposed) throw new ObjectDisposedException (GetType ().ToString ()); if (asyncResult == null) throw new ArgumentNullException ("asyncResult"); if (asyncResult is HttpStreamAsyncResult) { var ares = (HttpStreamAsyncResult) asyncResult; if (!ares.IsCompleted) ares.AsyncWaitHandle.WaitOne (); return ares.SyncRead; } // Close on exception? var nread = _stream.EndRead (asyncResult); if (nread > 0 && _bodyLeft > 0) _bodyLeft -= nread; return nread; } public override void EndWrite (IAsyncResult asyncResult) { throw new NotSupportedException (); } public override void Flush () { } public override int Read (byte[] buffer, int offset, int count) { if (_disposed) throw new ObjectDisposedException (GetType ().ToString ()); // Call the fillFromBuffer method to check for buffer boundaries even when _bodyLeft 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 && _bodyLeft > 0) _bodyLeft -= 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 (); } #endregion } }