BindingFlags.Public needed here as Exception.HResult is now public in .NET 4.5. This...
[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
28 #if NET_2_0 && SECURITY_DEP
29
30 using System.IO;
31 using System.Net.Sockets;
32 using System.Runtime.InteropServices;
33 namespace System.Net {
34         class ChunkedInputStream : RequestStream {
35                 bool disposed;
36                 ChunkStream decoder;
37                 HttpListenerContext context;
38                 bool no_more_data;
39
40                 class ReadBufferState {
41                         public byte [] Buffer;
42                         public int Offset;
43                         public int Count;
44                         public int InitialCount;
45                         public HttpStreamAsyncResult Ares;
46                         public ReadBufferState (byte [] buffer, int offset, int count,
47                                                 HttpStreamAsyncResult ares)
48                         {
49                                 Buffer = buffer;
50                                 Offset = offset;
51                                 Count = count;
52                                 InitialCount = count;
53                                 Ares = ares;
54                         }
55                 }
56
57                 public ChunkedInputStream (HttpListenerContext context, Stream stream,
58                                                 byte [] buffer, int offset, int length)
59                                         : base (stream, buffer, offset, length)
60                 {
61                         this.context = context;
62                         WebHeaderCollection coll = (WebHeaderCollection) context.Request.Headers;
63                         decoder = new ChunkStream (coll);
64                 }
65
66                 public ChunkStream Decoder {
67                         get { return decoder; }
68                         set { decoder = value; }
69                 }
70
71                 public override int Read ([In,Out] byte [] buffer, int offset, int count)
72                 {
73                         IAsyncResult ares = BeginRead (buffer, offset, count, null, null);
74                         return EndRead (ares);
75                 }
76
77                 public override IAsyncResult BeginRead (byte [] buffer, int offset, int count,
78                                                         AsyncCallback cback, object state)
79                 {
80                         if (disposed)
81                                 throw new ObjectDisposedException (GetType ().ToString ());
82
83                         if (buffer == null)
84                                 throw new ArgumentNullException ("buffer");
85
86                         int len = buffer.Length;
87                         if (offset < 0 || offset > len)
88                                 throw new ArgumentOutOfRangeException ("offset exceeds the size of buffer");
89
90                         if (count < 0 || offset > len - count)
91                                 throw new ArgumentOutOfRangeException ("offset+size exceeds the size of buffer");
92
93                         HttpStreamAsyncResult ares = new HttpStreamAsyncResult ();
94                         ares.Callback = cback;
95                         ares.State = state;
96                         if (no_more_data) {
97                                 ares.Complete ();
98                                 return ares;
99                         }
100                         int nread = decoder.Read (buffer, offset, count);
101                         offset += nread;
102                         count -= nread;
103                         if (count == 0) {
104                                 // got all we wanted, no need to bother the decoder yet
105                                 ares.Count = nread;
106                                 ares.Complete ();
107                                 return ares;
108                         }
109                         if (!decoder.WantMore) {
110                                 no_more_data = nread == 0;
111                                 ares.Count = nread;
112                                 ares.Complete ();
113                                 return ares;
114                         }
115                         ares.Buffer = new byte [8192];
116                         ares.Offset = 0;
117                         ares.Count = 8192;
118                         ReadBufferState rb = new ReadBufferState (buffer, offset, count, ares);
119                         rb.InitialCount += nread;
120                         base.BeginRead (ares.Buffer, ares.Offset, ares.Count, OnRead, rb);
121                         return ares;
122                 }
123
124                 void OnRead (IAsyncResult base_ares)
125                 {
126                         ReadBufferState rb = (ReadBufferState) base_ares.AsyncState;
127                         HttpStreamAsyncResult ares = rb.Ares;
128                         try {
129                                 int nread = base.EndRead (base_ares);
130                                 decoder.Write (ares.Buffer, ares.Offset, nread);
131                                 nread = decoder.Read (rb.Buffer, rb.Offset, rb.Count);
132                                 rb.Offset += nread;
133                                 rb.Count -= nread;
134                                 if (rb.Count == 0 || !decoder.WantMore || nread == 0) {
135                                         no_more_data = !decoder.WantMore && nread == 0;
136                                         ares.Count = rb.InitialCount - rb.Count;
137                                         ares.Complete ();
138                                         return;
139                                 }
140                                 ares.Offset = 0;
141                                 ares.Count = Math.Min (8192, decoder.ChunkLeft + 6);
142                                 base.BeginRead (ares.Buffer, ares.Offset, ares.Count, OnRead, rb);
143                         } catch (Exception e) {
144                                 context.Connection.SendError (e.Message, 400);
145                                 ares.Complete (e);
146                         }
147                 }
148
149                 public override int EndRead (IAsyncResult ares)
150                 {
151                         if (disposed)
152                                 throw new ObjectDisposedException (GetType ().ToString ());
153
154                         HttpStreamAsyncResult my_ares = ares as HttpStreamAsyncResult;
155                         if (ares == null)
156                                 throw new ArgumentException ("Invalid IAsyncResult", "ares");
157
158                         if (!ares.IsCompleted)
159                                 ares.AsyncWaitHandle.WaitOne ();
160
161                         if (my_ares.Error != null)
162                                 throw new HttpListenerException (400, "I/O operation aborted: " + my_ares.Error.Message);
163
164                         return my_ares.Count;
165                 }
166
167                 public override void Close ()
168                 {
169                         if (!disposed) {
170                                 disposed = true;
171                                 base.Close ();
172                         }
173                 }
174         }
175 }
176 #endif