New test.
[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                         int nread = decoder.Read (buffer, offset, count);
98                         offset += nread;
99                         count -= nread;
100                         if (count == 0) {
101                                 // got all we wanted, no need to bother the decoder yet
102                                 ares.Count = nread;
103                                 ares.Complete ();
104                                 return ares;
105                         }
106                         if (!decoder.WantMore) {
107                                 no_more_data = nread == 0;
108                                 ares.Count = nread;
109                                 ares.Complete ();
110                                 return ares;
111                         }
112                         ares.Buffer = new byte [8192];
113                         ares.Offset = 0;
114                         ares.Count = 8192;
115                         ReadBufferState rb = new ReadBufferState (buffer, offset, count, ares);
116                         rb.InitialCount += nread;
117                         base.BeginRead (ares.Buffer, ares.Offset, ares.Count, OnRead, rb);
118                         return ares;
119                 }
120
121                 void OnRead (IAsyncResult base_ares)
122                 {
123                         ReadBufferState rb = (ReadBufferState) base_ares.AsyncState;
124                         HttpStreamAsyncResult ares = rb.Ares;
125                         try {
126                                 int nread = base.EndRead (base_ares);
127                                 decoder.Write (ares.Buffer, ares.Offset, nread);
128                                 nread = decoder.Read (rb.Buffer, rb.Offset, rb.Count);
129                                 rb.Offset += nread;
130                                 rb.Count -= nread;
131                                 if (rb.Count == 0 || !decoder.WantMore || nread == 0) {
132                                         no_more_data = !decoder.WantMore && nread == 0;
133                                         ares.Count = rb.InitialCount - rb.Count;
134                                         ares.Complete ();
135                                         return;
136                                 }
137                                 ares.Offset = 0;
138                                 ares.Count = Math.Min (8192, decoder.ChunkLeft + 6);
139                                 base.BeginRead (ares.Buffer, ares.Offset, ares.Count, OnRead, rb);
140                         } catch (Exception e) {
141                                 context.Connection.SendError (e.Message, 400);
142                                 ares.Complete (e);
143                         }
144                 }
145
146                 public override int EndRead (IAsyncResult ares)
147                 {
148                         if (disposed)
149                                 throw new ObjectDisposedException (GetType ().ToString ());
150
151                         HttpStreamAsyncResult my_ares = ares as HttpStreamAsyncResult;
152                         if (ares == null)
153                                 throw new ArgumentException ("Invalid IAsyncResult", "ares");
154
155                         if (!ares.IsCompleted)
156                                 ares.AsyncWaitHandle.WaitOne ();
157
158                         if (my_ares.Error != null)
159                                 throw new HttpListenerException (400, "I/O operation aborted.");
160
161                         return my_ares.Count;
162                 }
163
164                 public override void Close ()
165                 {
166                         if (!disposed) {
167                                 disposed = true;
168                                 base.Close ();
169                         }
170                 }
171         }
172 }
173 #endif