This should fix #76928. This fix incorporates ideas from a patch
[mono.git] / mcs / class / System / System.Net / ChunkedInputStream.cs
1 //
2 // System.Net.ChunkedInputStream
3 //
4 // Authors:
5 //      Gonzalo Paniagua Javier (gonzalo@novell.com)
6 //
7 // Copyright (c) 2005 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 #if NET_2_0
28 using System.Net.Sockets;
29 using System.Runtime.InteropServices;
30 namespace System.Net {
31         class ChunkedInputStream : RequestStream {
32                 bool disposed;
33                 ChunkStream decoder;
34                 HttpListenerContext context;
35                 bool no_more_data;
36
37                 class ReadBufferState {
38                         public byte [] Buffer;
39                         public int Offset;
40                         public int Count;
41                         public int InitialCount;
42                         public HttpStreamAsyncResult Ares;
43                         public ReadBufferState (byte [] buffer, int offset, int count,
44                                                 HttpStreamAsyncResult ares)
45                         {
46                                 Buffer = buffer;
47                                 Offset = offset;
48                                 Count = count;
49                                 InitialCount = count;
50                                 Ares = ares;
51                         }
52                 }
53
54                 public ChunkedInputStream (HttpListenerContext context, Socket sock,
55                                                 byte [] buffer, int offset, int length)
56                                         : base (sock, buffer, offset, length)
57                 {
58                         this.context = context;
59                         WebHeaderCollection coll = (WebHeaderCollection) context.Request.Headers;
60                         decoder = new ChunkStream (coll);
61                 }
62
63                 public ChunkStream Decoder {
64                         get { return decoder; }
65                         set { decoder = value; }
66                 }
67
68                 public override int Read ([In,Out] byte [] buffer, int offset, int count)
69                 {
70                         IAsyncResult ares = BeginRead (buffer, offset, count, null, null);
71                         return EndRead (ares);
72                 }
73
74                 public override IAsyncResult BeginRead (byte [] buffer, int offset, int count,
75                                                         AsyncCallback cback, object state)
76                 {
77                         if (disposed)
78                                 throw new ObjectDisposedException (GetType ().ToString ());
79
80                         if (buffer == null)
81                                 throw new ArgumentNullException ("buffer");
82
83                         int len = buffer.Length;
84                         if (offset < 0 || offset > len)
85                                 throw new ArgumentOutOfRangeException ("offset exceeds the size of buffer");
86
87                         if (count < 0 || offset > len - count)
88                                 throw new ArgumentOutOfRangeException ("offset+size exceeds the size of buffer");
89
90                         HttpStreamAsyncResult ares = new HttpStreamAsyncResult ();
91                         ares.Callback = cback;
92                         ares.State = state;
93                         if (no_more_data) {
94                                 ares.Complete ();
95                                 return ares;
96                         }
97                         ares.Buffer = new byte [8192];
98                         ares.Offset = 0;
99                         ares.Count = 8192;
100                         ReadBufferState rb = new ReadBufferState (buffer, offset, count, ares);
101                         base.BeginRead (ares.Buffer, ares.Offset, ares.Count, OnRead, rb);
102                         return ares;
103                 }
104
105                 void OnRead (IAsyncResult base_ares)
106                 {
107                         ReadBufferState rb = (ReadBufferState) base_ares.AsyncState;
108                         HttpStreamAsyncResult ares = rb.Ares;
109                         try {
110                                 int nread = base.EndRead (base_ares);
111                                 decoder.Write (ares.Buffer, ares.Offset, nread);
112                                 nread = decoder.Read (rb.Buffer, rb.Offset, rb.Count);
113                                 rb.Offset += nread;
114                                 rb.Count -= nread;
115                                 if (rb.Count == 0 || !decoder.WantMore) {
116                                         no_more_data = !decoder.WantMore;
117                                         ares.Count = rb.InitialCount - rb.Count;
118                                         ares.Complete ();
119                                         return;
120                                 }
121                                 ares.Offset = 0;
122                                 ares.Count = Math.Min (8192, decoder.ChunkLeft + 6);
123                                 base.BeginRead (ares.Buffer, ares.Offset, ares.Count, OnRead, rb);
124                         } catch (Exception e) {
125                                 context.Connection.SendError (e.Message, 400);
126                                 ares.Complete (e);
127                         }
128                 }
129
130                 public override int EndRead (IAsyncResult ares)
131                 {
132                         if (disposed)
133                                 throw new ObjectDisposedException (GetType ().ToString ());
134
135                         HttpStreamAsyncResult my_ares = ares as HttpStreamAsyncResult;
136                         if (ares == null)
137                                 throw new ArgumentException ("Invalid IAsyncResult", "ares");
138
139                         if (!ares.IsCompleted)
140                                 ares.AsyncWaitHandle.WaitOne ();
141
142                         if (my_ares.Error != null)
143                                 throw new HttpListenerException (400, "I/O operation aborted.");
144
145                         return my_ares.Count;
146                 }
147
148                 public override void Close ()
149                 {
150                         if (!disposed) {
151                                 disposed = true;
152                                 base.Close ();
153                         }
154                 }
155         }
156 }
157 #endif