// 
 | 
// ChunkedInputStream.cs 
 | 
//    Copied from System.Net.ChunkedInputStream 
 | 
// 
 | 
// Authors: 
 | 
//    Gonzalo Paniagua Javier (gonzalo@novell.com) 
 | 
// 
 | 
// Copyright (c) 2005 Novell, Inc (http://www.novell.com) 
 | 
// 
 | 
// 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. 
 | 
  
 | 
using System; 
 | 
using System.IO; 
 | 
using System.Net; 
 | 
using System.Net.Sockets; 
 | 
using System.Runtime.InteropServices; 
 | 
  
 | 
namespace WebSocketSharp.Net { 
 | 
  
 | 
    class ChunkedInputStream : RequestStream 
 | 
    { 
 | 
        HttpListenerContext context; 
 | 
        ChunkStream         decoder; 
 | 
        bool                disposed; 
 | 
        bool                no_more_data; 
 | 
  
 | 
        class ReadBufferState { 
 | 
  
 | 
            public HttpStreamAsyncResult Ares; 
 | 
            public byte []               Buffer; 
 | 
            public int                   Count; 
 | 
            public int                   InitialCount; 
 | 
            public int                   Offset; 
 | 
  
 | 
            public ReadBufferState ( 
 | 
                byte [] buffer, int offset, int count, HttpStreamAsyncResult ares) 
 | 
            { 
 | 
                Buffer = buffer; 
 | 
                Offset = offset; 
 | 
                Count = count; 
 | 
                InitialCount = count; 
 | 
                Ares = ares; 
 | 
            } 
 | 
        } 
 | 
  
 | 
        public ChunkedInputStream ( 
 | 
            HttpListenerContext context, Stream stream, byte [] buffer, int offset, int length) 
 | 
            : base (stream, buffer, offset, length) 
 | 
        { 
 | 
            this.context = context; 
 | 
            WebHeaderCollection coll = (WebHeaderCollection) context.Request.Headers; 
 | 
            decoder = new ChunkStream (coll); 
 | 
        } 
 | 
  
 | 
        public ChunkStream Decoder { 
 | 
            get { return decoder; } 
 | 
            set { decoder = value; } 
 | 
        } 
 | 
  
 | 
        void OnRead (IAsyncResult base_ares) 
 | 
        { 
 | 
            ReadBufferState rb = (ReadBufferState) base_ares.AsyncState; 
 | 
            HttpStreamAsyncResult ares = rb.Ares; 
 | 
            try { 
 | 
                int nread = base.EndRead (base_ares); 
 | 
                decoder.Write (ares.Buffer, ares.Offset, nread); 
 | 
                nread = decoder.Read (rb.Buffer, rb.Offset, rb.Count); 
 | 
                rb.Offset += nread; 
 | 
                rb.Count -= nread; 
 | 
                if (rb.Count == 0 || !decoder.WantMore || nread == 0) { 
 | 
                    no_more_data = !decoder.WantMore && nread == 0; 
 | 
                    ares.Count = rb.InitialCount - rb.Count; 
 | 
                    ares.Complete (); 
 | 
                    return; 
 | 
                } 
 | 
                ares.Offset = 0; 
 | 
                ares.Count = Math.Min (8192, decoder.ChunkLeft + 6); 
 | 
                base.BeginRead (ares.Buffer, ares.Offset, ares.Count, OnRead, rb); 
 | 
            } catch (Exception e) { 
 | 
                context.Connection.SendError (e.Message, 400); 
 | 
                ares.Complete (e); 
 | 
            } 
 | 
        } 
 | 
  
 | 
        public override IAsyncResult BeginRead ( 
 | 
            byte [] buffer, int offset, int count, AsyncCallback cback, object state) 
 | 
        { 
 | 
            if (disposed) 
 | 
                throw new ObjectDisposedException (GetType ().ToString ()); 
 | 
  
 | 
            if (buffer == null) 
 | 
                throw new ArgumentNullException ("buffer"); 
 | 
  
 | 
            int len = buffer.Length; 
 | 
            if (offset < 0 || offset > len) 
 | 
                throw new ArgumentOutOfRangeException ("offset exceeds the size of buffer"); 
 | 
  
 | 
            if (count < 0 || offset > len - count) 
 | 
                throw new ArgumentOutOfRangeException ("offset+size exceeds the size of buffer"); 
 | 
  
 | 
            HttpStreamAsyncResult ares = new HttpStreamAsyncResult (); 
 | 
            ares.Callback = cback; 
 | 
            ares.State = state; 
 | 
            if (no_more_data) { 
 | 
                ares.Complete (); 
 | 
                return ares; 
 | 
            } 
 | 
            int nread = decoder.Read (buffer, offset, count); 
 | 
            offset += nread; 
 | 
            count -= nread; 
 | 
            if (count == 0) { 
 | 
                // got all we wanted, no need to bother the decoder yet 
 | 
                ares.Count = nread; 
 | 
                ares.Complete (); 
 | 
                return ares; 
 | 
            } 
 | 
            if (!decoder.WantMore) { 
 | 
                no_more_data = nread == 0; 
 | 
                ares.Count = nread; 
 | 
                ares.Complete (); 
 | 
                return ares; 
 | 
            } 
 | 
            ares.Buffer = new byte [8192]; 
 | 
            ares.Offset = 0; 
 | 
            ares.Count = 8192; 
 | 
            ReadBufferState rb = new ReadBufferState (buffer, offset, count, ares); 
 | 
            rb.InitialCount += nread; 
 | 
            base.BeginRead (ares.Buffer, ares.Offset, ares.Count, OnRead, rb); 
 | 
            return ares; 
 | 
        } 
 | 
  
 | 
        public override void Close () 
 | 
        { 
 | 
            if (!disposed) { 
 | 
                disposed = true; 
 | 
                base.Close (); 
 | 
            } 
 | 
        } 
 | 
  
 | 
        public override int EndRead (IAsyncResult ares) 
 | 
        { 
 | 
            if (disposed) 
 | 
                throw new ObjectDisposedException (GetType ().ToString ()); 
 | 
  
 | 
            HttpStreamAsyncResult my_ares = ares as HttpStreamAsyncResult; 
 | 
            if (ares == null) 
 | 
                throw new ArgumentException ("Invalid IAsyncResult", "ares"); 
 | 
  
 | 
            if (!ares.IsCompleted) 
 | 
                ares.AsyncWaitHandle.WaitOne (); 
 | 
  
 | 
            if (my_ares.Error != null) 
 | 
                throw new HttpListenerException (400, "I/O operation aborted."); 
 | 
  
 | 
            return my_ares.Count; 
 | 
        } 
 | 
  
 | 
        public override int Read ([In,Out] byte [] buffer, int offset, int count) 
 | 
        { 
 | 
            IAsyncResult ares = BeginRead (buffer, offset, count, null, null); 
 | 
            return EndRead (ares); 
 | 
        } 
 | 
    } 
 | 
} 
 |