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