c2314f9530040d4558c13f364f9dfcae98423a26
[mono.git] / mcs / class / System.Runtime.Remoting / MonoHttp / ChunkStream.cs
1 #define EMBEDDED_IN_1_0
2
3 //
4 // System.Net.ChunkStream
5 //
6 // Authors:
7 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 //
9 // (C) 2003 Ximian, Inc (http://www.ximian.com)
10 //
11
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System.Collections;
34 using System.Globalization;
35 using System.IO;
36 using System.Text;
37
38 using System; using System.Net; namespace MonoHttp
39 {
40         class ChunkStream
41         {
42                 enum State {
43                         None,
44                         Body,
45                         BodyFinished,
46                         Trailer
47                 }
48
49                 class Chunk {
50                         public byte [] Bytes;
51                         public int Offset;
52
53                         public Chunk (byte [] chunk)
54                         {
55                                 this.Bytes = chunk;
56                         }
57
58                         public int Read (byte [] buffer, int offset, int size)
59                         {
60                                 int nread = (size > Bytes.Length - Offset) ? Bytes.Length - Offset : size;
61                                 Buffer.BlockCopy (Bytes, Offset, buffer, offset, nread);
62                                 Offset += nread;
63                                 return nread;
64                         }
65                 }
66
67                 internal WebHeaderCollection headers;
68                 int chunkSize;
69                 int chunkRead;
70                 State state;
71                 //byte [] waitBuffer;
72                 StringBuilder saved;
73                 bool sawCR;
74                 bool gotit;
75                 int trailerState;
76                 ArrayList chunks;
77                 
78                 public ChunkStream (byte [] buffer, int offset, int size, WebHeaderCollection headers)
79                                         : this (headers)
80                 {
81                         Write (buffer, offset, size);
82                 }
83
84                 public ChunkStream (WebHeaderCollection headers)
85                 {
86                         this.headers = headers;
87                         saved = new StringBuilder ();
88                         chunks = new ArrayList ();
89                         chunkSize = -1;
90                 }
91
92                 public void ResetBuffer ()
93                 {
94                         chunkSize = -1;
95                         chunkRead = 0;
96                         chunks.Clear ();
97                 }
98                 
99                 public void WriteAndReadBack (byte [] buffer, int offset, int size, ref int read)
100                 {
101                         if (offset + read > 0)
102                                 Write (buffer, offset, offset+read);
103                         read = Read (buffer, offset, size);
104                 }
105
106                 public int Read (byte [] buffer, int offset, int size)
107                 {
108                         return ReadFromChunks (buffer, offset, size);
109                 }
110
111                 int ReadFromChunks (byte [] buffer, int offset, int size)
112                 {
113                         int count = chunks.Count;
114                         int nread = 0;
115                         for (int i = 0; i < count; i++) {
116                                 Chunk chunk = (Chunk) chunks [i];
117                                 if (chunk == null)
118                                         continue;
119
120                                 if (chunk.Offset == chunk.Bytes.Length) {
121                                         chunks [i] = null;
122                                         continue;
123                                 }
124                                 
125                                 nread += chunk.Read (buffer, offset + nread, size - nread);
126                                 if (nread == size)
127                                         break;
128                         }
129
130                         return nread;
131                 }
132                 
133                 public void Write (byte [] buffer, int offset, int size)
134                 {
135                         InternalWrite (buffer, ref offset, size);
136                 }
137                 
138                 void InternalWrite (byte [] buffer, ref int offset, int size)
139                 {
140                         if (state == State.None) {
141                                 state = GetChunkSize (buffer, ref offset, size);
142                                 if (state == State.None)
143                                         return;
144                                 
145                                 saved.Length = 0;
146                                 sawCR = false;
147                                 gotit = false;
148                         }
149                         
150                         if (state == State.Body && offset < size) {
151                                 state = ReadBody (buffer, ref offset, size);
152                                 if (state == State.Body)
153                                         return;
154                         }
155                         
156                         if (state == State.BodyFinished && offset < size) {
157                                 state = ReadCRLF (buffer, ref offset, size);
158                                 if (state == State.BodyFinished)
159                                         return;
160
161                                 sawCR = false;
162                         }
163                         
164                         if (state == State.Trailer && offset < size) {
165                                 state = ReadTrailer (buffer, ref offset, size);
166                                 if (state == State.Trailer)
167                                         return;
168
169                                 saved.Length = 0;
170                                 sawCR = false;
171                                 gotit = false;
172                         }
173
174                         if (offset < size)
175                                 InternalWrite (buffer, ref offset, size);
176                 }
177
178                 public bool WantMore {
179                         get { return (chunkRead != chunkSize || chunkSize != 0 || state != State.None); }
180                 }
181
182                 public int ChunkLeft {
183                         get { return chunkSize - chunkRead; }
184                 }
185                 
186                 State ReadBody (byte [] buffer, ref int offset, int size)
187                 {
188                         if (chunkSize == 0)
189                                 return State.BodyFinished;
190
191                         int diff = size - offset;
192                         if (diff + chunkRead > chunkSize)
193                                 diff = chunkSize - chunkRead;
194
195                         byte [] chunk = new byte [diff];
196                         Buffer.BlockCopy (buffer, offset, chunk, 0, diff);
197                         chunks.Add (new Chunk (chunk));
198                         offset += diff;
199                         chunkRead += diff;
200                         return (chunkRead == chunkSize) ? State.BodyFinished : State.Body;
201                                 
202                 }
203                 
204                 State GetChunkSize (byte [] buffer, ref int offset, int size)
205                 {
206                         char c = '\0';
207                         while (offset < size) {
208                                 c = (char) buffer [offset++];
209                                 if (c == '\r') {
210                                         if (sawCR)
211                                                 ThrowProtocolViolation ("2 CR found");
212
213                                         sawCR = true;
214                                         continue;
215                                 }
216                                 
217                                 if (sawCR && c == '\n')
218                                         break;
219
220                                 if (c == ' ')
221                                         gotit = true;
222
223                                 if (!gotit)
224                                         saved.Append (c);
225
226                                 if (saved.Length > 20)
227                                         ThrowProtocolViolation ("chunk size too long.");
228                         }
229
230                         if (!sawCR || c != '\n') {
231                                 if (offset < size)
232                                         ThrowProtocolViolation ("Missing \\n");
233
234                                 try {
235                                         if (saved.Length > 0)
236                                                 chunkSize = Int32.Parse (saved.ToString (), NumberStyles.HexNumber);
237                                 } catch (Exception) {
238                                         ThrowProtocolViolation ("Cannot parse chunk size.");
239                                 }
240
241                                 return State.None;
242                         }
243
244                         chunkRead = 0;
245                         try {
246                                 chunkSize = Int32.Parse (saved.ToString (), NumberStyles.HexNumber);
247                         } catch (Exception) {
248                                 ThrowProtocolViolation ("Cannot parse chunk size.");
249                         }
250                         
251                         if (chunkSize == 0) {
252                                 trailerState = 2;
253                                 return State.Trailer;
254                         }
255
256                         return State.Body;
257                 }
258
259                 State ReadCRLF (byte [] buffer, ref int offset, int size)
260                 {
261                         if (!sawCR) {
262                                 if ((char) buffer [offset++] != '\r')
263                                         ThrowProtocolViolation ("Expecting \\r");
264
265                                 sawCR = true;
266                                 if (offset == size)
267                                         return State.BodyFinished;
268                         }
269                         
270                         if (sawCR && (char) buffer [offset++] != '\n')
271                                 ThrowProtocolViolation ("Expecting \\n");
272
273                         return State.None;
274                 }
275
276                 State ReadTrailer (byte [] buffer, ref int offset, int size)
277                 {
278                         char c = '\0';
279
280                         // short path
281                         if (trailerState == 2 && (char) buffer [offset] == '\r' && saved.Length == 0) {
282                                 offset++;
283                                 if (offset < size && (char) buffer [offset] == '\n') {
284                                         offset++;
285                                         return State.None;
286                                 }
287                                 offset--;
288                         }
289                         
290                         int st = trailerState;
291                         string stString = "\r\n\r";
292                         while (offset < size && st < 4) {
293                                 c = (char) buffer [offset++];
294                                 if ((st == 0 || st == 2) && c == '\r') {
295                                         st++;
296                                         continue;
297                                 }
298
299                                 if ((st == 1 || st == 3) && c == '\n') {
300                                         st++;
301                                         continue;
302                                 }
303
304                                 if (st > 0) {
305                                         saved.Append (stString.Substring (0, saved.Length == 0? st-2: st));
306                                         st = 0;
307                                 }
308                         }
309
310                         if (st < 4) {
311                                 trailerState = st;
312                                 if (offset <  size)
313                                         ThrowProtocolViolation ("Error reading trailer.");
314
315                                 return State.Trailer;
316                         }
317
318                         StringReader reader = new StringReader (saved.ToString ());
319                         string line;
320                         while ((line = reader.ReadLine ()) != null && line != "")
321                                 headers.Add (line);
322
323                         return State.None;
324                 }
325
326                 static void ThrowProtocolViolation (string message)
327                 {
328                         WebException we = new WebException (message, null, WebExceptionStatus.ServerProtocolViolation, null);
329                         throw we;
330                 }
331         }
332 }
333