2004-01-27 Nick Drochak <ndrochak@ieee.org>
[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 using System.Globalization;
11 using System.IO;
12 using System.Text;
13
14 namespace System.Net
15 {
16         class ChunkStream
17         {
18                 enum State {
19                         None,
20                         Body,
21                         BodyFinished,
22                         Trailer
23                 }
24
25                 MemoryStream ms;
26                 WebHeaderCollection headers;
27                 int chunkSize;
28                 int chunkRead;
29                 State state;
30                 //byte [] waitBuffer;
31                 StringBuilder saved;
32                 bool sawCR;
33                 bool gotit;
34                 long readPosition;
35                 
36                 public ChunkStream (byte [] buffer, int offset, int size, WebHeaderCollection headers)
37                 {
38                         this.headers = headers;
39                         ms = new MemoryStream ();
40                         saved = new StringBuilder ();
41                         chunkSize = -1;
42                         if (offset < size)
43                                 Write (buffer, offset, size);
44                 }
45
46                 public void ResetBuffer ()
47                 {
48                         ms.SetLength (0);
49                         readPosition = 0;
50                         chunkSize = -1;
51                         chunkRead = 0;
52                 }
53                 
54                 public void WriteAndReadBack (byte [] buffer, int offset, int size, ref int read)
55                 {
56                         Write (buffer, offset, offset+read);
57                         read = Read (buffer, offset, size);
58                 }
59
60                 public int Read (byte [] buffer, int offset, int size)
61                 {
62                         ms.Position = readPosition;
63                         int r = ms.Read (buffer, offset, size);
64                         readPosition += r;
65                         ms.Position = ms.Length;
66                         return r;
67                 }
68
69                 public void Write (byte [] buffer, int offset, int size)
70                 {
71                         InternalWrite (buffer, ref offset, size);
72                 }
73                 
74                 void InternalWrite (byte [] buffer, ref int offset, int size)
75                 {
76                         if (state == State.None) {
77                                 state = GetChunkSize (buffer, ref offset, size);
78                                 if (state == State.None)
79                                         return;
80                                 
81                                 saved.Length = 0;
82                                 sawCR = false;
83                                 gotit = false;
84                         }
85                         
86                         if (state == State.Body && offset < size) {
87                                 state = ReadBody (buffer, ref offset, size);
88                                 if (state == State.Body)
89                                         return;
90                         }
91                         
92                         if (state == State.BodyFinished && offset < size) {
93                                 state = ReadCRLF (buffer, ref offset, size);
94                                 if (state == State.BodyFinished)
95                                         return;
96
97                                 sawCR = false;
98                         }
99                         
100                         if (state == State.Trailer && offset < size) {
101                                 state = ReadTrailer (buffer, ref offset, size);
102                                 if (state == State.Trailer)
103                                         return;
104
105                                 saved.Length = 0;
106                                 sawCR = false;
107                                 gotit = false;
108                         }
109
110                         if (offset < size)
111                                 InternalWrite (buffer, ref offset, size);
112                 }
113
114                 public bool WantMore {
115                         get { return (chunkRead != chunkSize || chunkSize != 0); }
116                 }
117                 
118                 public bool EOF {
119                         get { return (Available == 0); }
120                 }
121                 
122                 public int Available {
123                         get { return (int) (ms.Length - readPosition); }
124                 }
125                 
126                 State ReadBody (byte [] buffer, ref int offset, int size)
127                 {
128                         if (chunkSize == 0)
129                                 return State.BodyFinished;
130
131                         int diff = size - offset;
132                         if (diff + chunkRead > chunkSize)
133                                 diff = chunkSize - chunkRead;
134
135                         ms.Write (buffer, offset, diff);
136                         offset += diff;
137                         chunkRead += diff;
138                         return (chunkRead == chunkSize) ? State.BodyFinished : State.Body;
139                                 
140                 }
141                 
142                 State GetChunkSize (byte [] buffer, ref int offset, int size)
143                 {
144                         char c = '\0';
145                         while (offset < size) {
146                                 c = (char) buffer [offset++];
147                                 if (c == '\r') {
148                                         if (sawCR)
149                                                 throw new ProtocolViolationException ("2 CR found");
150
151                                         sawCR = true;
152                                         continue;
153                                 }
154                                 
155                                 if (sawCR && c == '\n')
156                                         break;
157
158                                 if (c == ' ')
159                                         gotit = true;
160
161                                 if (!gotit)
162                                         saved.Append (c);
163                         }
164
165                         if (!sawCR || c != '\n')
166                                 return State.None;
167
168                         chunkRead = 0;
169                         chunkSize = Int32.Parse (saved.ToString (), NumberStyles.HexNumber);
170                         if (chunkSize == 0)
171                                 return State.Trailer;
172
173                         return State.Body;
174                 }
175
176                 State ReadCRLF (byte [] buffer, ref int offset, int size)
177                 {
178                         if (!sawCR) {
179                                 if ((char) buffer [offset++] != '\r')
180                                         throw new ProtocolViolationException ("Expecting \\r");
181
182                                 sawCR = true;
183                                 if (offset == size)
184                                         return State.BodyFinished;
185                         }
186                         
187                         if ((char) buffer [offset++] != '\n')
188                                 throw new ProtocolViolationException ("Expecting \\n");
189
190                         return State.None;
191                 }
192
193                 State ReadTrailer (byte [] buffer, ref int offset, int size)
194                 {
195                         char c = '\0';
196
197                         // short path
198                         if ((char) buffer [offset] == '\r') {
199                                 offset++;
200                                 if ((char) buffer [offset] == '\n') {
201                                         offset++;
202                                         return State.None;
203                                 }
204                                 offset--;
205                         }
206                         
207                         int st = 0;
208                         string stString = "\r\n\r";
209                         while (offset < size && st < 4) {
210                                 c = (char) buffer [offset++];
211                                 if ((st == 0 || st == 2) && c == '\r') {
212                                         st++;
213                                         continue;
214                                 }
215
216                                 if ((st == 1 || st == 3) && c == '\n') {
217                                         st++;
218                                         continue;
219                                 }
220
221                                 if (st > 0) {
222                                         saved.Append (stString.Substring (0, st));
223                                         st = 0;
224                                 }
225                         }
226
227                         if (st < 4)
228                                 return State.Trailer;
229
230                         StringReader reader = new StringReader (saved.ToString ());
231                         string line;
232                         while ((line = reader.ReadLine ()) != null && line != "")
233                                 headers.Add (line);
234
235                         return State.None;
236                 }
237         }
238 }
239