[System]: WebRequest.GetSystemProxy(): Return custom proxy for monodroid.
[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                 internal WebHeaderCollection headers;
66                 int chunkSize;
67                 int chunkRead;
68                 State state;
69                 //byte [] waitBuffer;
70                 StringBuilder saved;
71                 bool sawCR;
72                 bool gotit;
73                 int trailerState;
74                 ArrayList chunks;
75                 
76                 public ChunkStream (byte [] buffer, int offset, int size, WebHeaderCollection headers)
77                                         : this (headers)
78                 {
79                         Write (buffer, offset, size);
80                 }
81
82                 public ChunkStream (WebHeaderCollection headers)
83                 {
84                         this.headers = headers;
85                         saved = new StringBuilder ();
86                         chunks = new ArrayList ();
87                         chunkSize = -1;
88                 }
89
90                 public void ResetBuffer ()
91                 {
92                         chunkSize = -1;
93                         chunkRead = 0;
94                         chunks.Clear ();
95                 }
96                 
97                 public void WriteAndReadBack (byte [] buffer, int offset, int size, ref int read)
98                 {
99                         if (offset + read > 0)
100                                 Write (buffer, offset, offset+read);
101                         read = Read (buffer, offset, size);
102                 }
103
104                 public int Read (byte [] buffer, int offset, int size)
105                 {
106                         return ReadFromChunks (buffer, offset, size);
107                 }
108
109                 int ReadFromChunks (byte [] buffer, int offset, int size)
110                 {
111                         int count = chunks.Count;
112                         int nread = 0;
113                         for (int i = 0; i < count; i++) {
114                                 Chunk chunk = (Chunk) chunks [i];
115                                 if (chunk == null)
116                                         continue;
117
118                                 if (chunk.Offset == chunk.Bytes.Length) {
119                                         chunks [i] = null;
120                                         continue;
121                                 }
122                                 
123                                 nread += chunk.Read (buffer, offset + nread, size - nread);
124                                 if (nread == size)
125                                         break;
126                         }
127
128                         return nread;
129                 }
130                 
131                 public void Write (byte [] buffer, int offset, int size)
132                 {
133                         if (offset < size)
134                                 InternalWrite (buffer, ref offset, size);
135                 }
136                 
137                 void InternalWrite (byte [] buffer, ref int offset, int size)
138                 {
139                         if (state == State.None) {
140                                 state = GetChunkSize (buffer, ref offset, size);
141                                 if (state == State.None)
142                                         return;
143                                 
144                                 saved.Length = 0;
145                                 sawCR = false;
146                                 gotit = false;
147                         }
148                         
149                         if (state == State.Body && offset < size) {
150                                 state = ReadBody (buffer, ref offset, size);
151                                 if (state == State.Body)
152                                         return;
153                         }
154                         
155                         if (state == State.BodyFinished && offset < size) {
156                                 state = ReadCRLF (buffer, ref offset, size);
157                                 if (state == State.BodyFinished)
158                                         return;
159
160                                 sawCR = false;
161                         }
162                         
163                         if (state == State.Trailer && offset < size) {
164                                 state = ReadTrailer (buffer, ref offset, size);
165                                 if (state == State.Trailer)
166                                         return;
167
168                                 saved.Length = 0;
169                                 sawCR = false;
170                                 gotit = false;
171                         }
172
173                         if (offset < size)
174                                 InternalWrite (buffer, ref offset, size);
175                 }
176
177                 public bool WantMore {
178                         get { return (chunkRead != chunkSize || chunkSize != 0 || state != State.None); }
179                 }
180
181                 public bool DataAvailable {
182                         get {
183                                 int count = chunks.Count;
184                                 for (int i = 0; i < count; i++) {
185                                         Chunk ch = (Chunk) chunks [i];
186                                         if (ch == null || ch.Bytes == null)
187                                                 continue;
188                                         if (ch.Bytes.Length > 0 && ch.Offset < ch.Bytes.Length)
189                                                 return (state != State.Body);
190                                 }
191                                 return false;
192                         }
193                 }
194
195                 public int ChunkLeft {
196                         get { return chunkSize - chunkRead; }
197                 }
198                 
199                 State ReadBody (byte [] buffer, ref int offset, int size)
200                 {
201                         if (chunkSize == 0)
202                                 return State.BodyFinished;
203
204                         int diff = size - offset;
205                         if (diff + chunkRead > chunkSize)
206                                 diff = chunkSize - chunkRead;
207
208                         byte [] chunk = new byte [diff];
209                         Buffer.BlockCopy (buffer, offset, chunk, 0, diff);
210                         chunks.Add (new Chunk (chunk));
211                         offset += diff;
212                         chunkRead += diff;
213                         return (chunkRead == chunkSize) ? State.BodyFinished : State.Body;
214                                 
215                 }
216                 
217                 State GetChunkSize (byte [] buffer, ref int offset, int size)
218                 {
219                         chunkRead = 0;
220                         chunkSize = 0;
221                         char c = '\0';
222                         while (offset < size) {
223                                 c = (char) buffer [offset++];
224                                 if (c == '\r') {
225                                         if (sawCR)
226                                                 ThrowProtocolViolation ("2 CR found");
227
228                                         sawCR = true;
229                                         continue;
230                                 }
231                                 
232                                 if (sawCR && c == '\n')
233                                         break;
234
235                                 if (c == ' ')
236                                         gotit = true;
237
238                                 if (!gotit)
239                                         saved.Append (c);
240
241                                 if (saved.Length > 20)
242                                         ThrowProtocolViolation ("chunk size too long.");
243                         }
244
245                         if (!sawCR || c != '\n') {
246                                 if (offset < size)
247                                         ThrowProtocolViolation ("Missing \\n");
248
249                                 try {
250                                         if (saved.Length > 0) {
251                                                 chunkSize = Int32.Parse (RemoveChunkExtension (saved.ToString ()), NumberStyles.HexNumber);
252                                         }
253                                 } catch (Exception) {
254                                         ThrowProtocolViolation ("Cannot parse chunk size.");
255                                 }
256
257                                 return State.None;
258                         }
259
260                         chunkRead = 0;
261                         try {
262                                 chunkSize = Int32.Parse (RemoveChunkExtension (saved.ToString ()), NumberStyles.HexNumber);
263                         } catch (Exception) {
264                                 ThrowProtocolViolation ("Cannot parse chunk size.");
265                         }
266
267                         if (chunkSize == 0) {
268                                 trailerState = 2;
269                                 return State.Trailer;
270                         }
271
272                         return State.Body;
273                 }
274
275                 static string RemoveChunkExtension (string input)
276                 {
277                         int idx = input.IndexOf (';');
278                         if (idx == -1)
279                                 return input;
280                         return input.Substring (0, idx);
281                 }
282
283                 State ReadCRLF (byte [] buffer, ref int offset, int size)
284                 {
285                         if (!sawCR) {
286                                 if ((char) buffer [offset++] != '\r')
287                                         ThrowProtocolViolation ("Expecting \\r");
288
289                                 sawCR = true;
290                                 if (offset == size)
291                                         return State.BodyFinished;
292                         }
293                         
294                         if (sawCR && (char) buffer [offset++] != '\n')
295                                 ThrowProtocolViolation ("Expecting \\n");
296
297                         return State.None;
298                 }
299
300                 State ReadTrailer (byte [] buffer, ref int offset, int size)
301                 {
302                         char c = '\0';
303
304                         // short path
305                         if (trailerState == 2 && (char) buffer [offset] == '\r' && saved.Length == 0) {
306                                 offset++;
307                                 if (offset < size && (char) buffer [offset] == '\n') {
308                                         offset++;
309                                         return State.None;
310                                 }
311                                 offset--;
312                         }
313                         
314                         int st = trailerState;
315                         string stString = "\r\n\r";
316                         while (offset < size && st < 4) {
317                                 c = (char) buffer [offset++];
318                                 if ((st == 0 || st == 2) && c == '\r') {
319                                         st++;
320                                         continue;
321                                 }
322
323                                 if ((st == 1 || st == 3) && c == '\n') {
324                                         st++;
325                                         continue;
326                                 }
327
328                                 if (st > 0) {
329                                         saved.Append (stString.Substring (0, saved.Length == 0? st-2: st));
330                                         st = 0;
331                                         if (saved.Length > 4196)
332                                                 ThrowProtocolViolation ("Error reading trailer (too long).");
333                                 }
334                         }
335
336                         if (st < 4) {
337                                 trailerState = st;
338                                 if (offset < size)
339                                         ThrowProtocolViolation ("Error reading trailer.");
340
341                                 return State.Trailer;
342                         }
343
344                         StringReader reader = new StringReader (saved.ToString ());
345                         string line;
346                         while ((line = reader.ReadLine ()) != null && line != "")
347                                 headers.Add (line);
348
349                         return State.None;
350                 }
351
352                 static void ThrowProtocolViolation (string message)
353                 {
354                         WebException we = new WebException (message, null, WebExceptionStatus.ServerProtocolViolation, null);
355                         throw we;
356                 }
357         }
358 }
359