Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(271)

Side by Side Diff: runtime/bin/http_parser.dart

Issue 9834008: Add error handling to the HTTP library (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 // Global constants. 5 // Global constants.
6 class _Const { 6 class _Const {
7 // Bytes for "HTTP/1.0". 7 // Bytes for "HTTP/1.0".
8 static final HTTP10 = const [72, 84, 84, 80, 47, 49, 46, 48]; 8 static final HTTP10 = const [72, 84, 84, 80, 47, 49, 46, 48];
9 // Bytes for "HTTP/1.1". 9 // Bytes for "HTTP/1.1".
10 static final HTTP11 = const [72, 84, 84, 80, 47, 49, 46, 49]; 10 static final HTTP11 = const [72, 84, 84, 80, 47, 49, 46, 49];
11 11
12 static final END_CHUNKED = const [0x30, 13, 10, 13, 10]; 12 static final END_CHUNKED = const [0x30, 13, 10, 13, 10];
13
14 // Bytes for '()<>@,;:\\"/[]?={} \t'.
15 static final SEPARATORS = const [40, 41, 60, 62, 64, 44, 59, 58, 92, 34, 47,
16 91, 93, 63, 61, 123, 125, 32, 9];
17
18 // Bytes for '()<>@,;:\\"/[]?={} \t\r\n'.
19 static final SEPARATORS_AND_CR_LF = const [40, 41, 60, 62, 64, 44, 59, 58, 92,
20 34, 47, 91, 93, 63, 61, 123, 125,
21 32, 9, 13, 10];
13 } 22 }
14 23
15 24
16 // Frequently used character codes. 25 // Frequently used character codes.
17 class _CharCode { 26 class _CharCode {
18 static final int HT = 9; 27 static final int HT = 9;
19 static final int LF = 10; 28 static final int LF = 10;
20 static final int CR = 13; 29 static final int CR = 13;
21 static final int SP = 32; 30 static final int SP = 32;
22 static final int COLON = 58; 31 static final int COLON = 58;
(...skipping 20 matching lines...) Expand all
43 static final int HEADER_VALUE_FOLD_OR_END = 14; 52 static final int HEADER_VALUE_FOLD_OR_END = 14;
44 static final int HEADER_ENDING = 15; 53 static final int HEADER_ENDING = 15;
45 static final int CHUNK_SIZE_STARTING_CR = 16; 54 static final int CHUNK_SIZE_STARTING_CR = 16;
46 static final int CHUNK_SIZE_STARTING_LF = 17; 55 static final int CHUNK_SIZE_STARTING_LF = 17;
47 static final int CHUNK_SIZE = 18; 56 static final int CHUNK_SIZE = 18;
48 static final int CHUNK_SIZE_EXTENSION = 19; 57 static final int CHUNK_SIZE_EXTENSION = 19;
49 static final int CHUNK_SIZE_ENDING = 20; 58 static final int CHUNK_SIZE_ENDING = 20;
50 static final int CHUNKED_BODY_DONE_CR = 21; 59 static final int CHUNKED_BODY_DONE_CR = 21;
51 static final int CHUNKED_BODY_DONE_LF = 22; 60 static final int CHUNKED_BODY_DONE_LF = 22;
52 static final int BODY = 23; 61 static final int BODY = 23;
62 static final int FAILURE = 24;
53 } 63 }
54 64
55 65
56 // States of the HTTP parser state machine. 66 // States of the HTTP parser state machine.
57 class _MessageType { 67 class _MessageType {
58 static final int UNDETERMINED = 0; 68 static final int UNDETERMINED = 0;
59 static final int REQUEST = 1; 69 static final int REQUEST = 1;
60 static final int RESPONSE = 0; 70 static final int RESPONSE = 0;
61 } 71 }
62 72
63 73
64 /** 74 /**
65 * HTTP parser which parses the HTTP stream as data is supplied 75 * HTTP parser which parses the HTTP stream as data is supplied
66 * through the writeList method. As the data is parsed the events 76 * through the [:writeList:] and [:connectionClosed:] methods. As the
67 * RequestStart 77 * data is parsed the following callbacks are called:
68 * ResponseStart 78 *
69 * UriReceived 79 * [:requestStart:]
70 * HeaderReceived 80 * [:responseStart:]
71 * HeadersComplete 81 * [:headerReceived:]
72 * DataReceived 82 * [:headersComplete:]
73 * DataEnd 83 * [:dataReceived:]
74 * are generated. 84 * [:dataEnd:]
75 * Currently only HTTP requests with Content-Length header are supported. 85 * [:error:]
86 *
87 * If an HTTP parser error occours it is possible to get an exception
88 * thrown from the [:writeList:] and [:connectionClosed:] methods if
89 * the error callback is not set.
90 *
91 * For keep-alive connections where the underlying connection stays
92 * open the [:connectionClosed:] method would not be called.
76 */ 93 */
77 class _HttpParser { 94 class _HttpParser {
78 _HttpParser() { 95 _HttpParser() {
79 _reset(); 96 _reset();
80 } 97 }
81 98
82 // From RFC 2616. 99 // From RFC 2616.
83 // generic-message = start-line 100 // generic-message = start-line
84 // *(message-header CRLF) 101 // *(message-header CRLF)
85 // CRLF 102 // CRLF
86 // [ message-body ] 103 // [ message-body ]
87 // start-line = Request-Line | Status-Line 104 // start-line = Request-Line | Status-Line
88 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF 105 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
89 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF 106 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
90 // message-header = field-name ":" [ field-value ] 107 // message-header = field-name ":" [ field-value ]
91 int writeList(List<int> buffer, int offset, int count) { 108 int writeList(List<int> buffer, int offset, int count) {
92 int index = offset; 109 int index = offset;
93 int lastIndex = offset + count; 110 int lastIndex = offset + count;
94 while ((index < lastIndex) && !_failure) { 111 try {
95 int byte = buffer[index]; 112 while ((index < lastIndex) && _state != _State.FAILURE) {
96 switch (_state) { 113 int byte = buffer[index];
97 case _State.START: 114 switch (_state) {
98 if (byte == _Const.HTTP11[0]) { 115 case _State.START:
99 // Start parsing HTTP method. 116 if (byte == _Const.HTTP11[0]) {
100 _httpVersionIndex = 1; 117 // Start parsing HTTP method.
101 _state = _State.METHOD_OR_HTTP_VERSION; 118 _httpVersionIndex = 1;
102 } else { 119 _state = _State.METHOD_OR_HTTP_VERSION;
103 // Start parsing method. 120 } else {
104 _method_or_status_code.addCharCode(byte); 121 // Start parsing method.
105 _state = _State.REQUEST_LINE_METHOD; 122 if (_Const.SEPARATORS_AND_CR_LF.indexOf(byte) != -1) {
106 } 123 throw new HttpParserException("Invalid request method");
107 break; 124 }
125 _method_or_status_code.addCharCode(byte);
126 _state = _State.REQUEST_LINE_METHOD;
127 }
128 break;
108 129
109 case _State.METHOD_OR_HTTP_VERSION: 130 case _State.METHOD_OR_HTTP_VERSION:
110 if (_httpVersionIndex < _Const.HTTP11.length && 131 if (_httpVersionIndex < _Const.HTTP11.length &&
111 byte == _Const.HTTP11[_httpVersionIndex]) { 132 byte == _Const.HTTP11[_httpVersionIndex]) {
112 // Continue parsing HTTP version. 133 // Continue parsing HTTP version.
113 _httpVersionIndex++; 134 _httpVersionIndex++;
114 } else if (_httpVersionIndex == _Const.HTTP11.length && 135 } else if (_httpVersionIndex == _Const.HTTP11.length &&
115 byte == _CharCode.SP) { 136 byte == _CharCode.SP) {
116 // HTTP version parsed. 137 // HTTP version parsed.
117 _state = _State.RESPONSE_LINE_STATUS_CODE; 138 _state = _State.RESPONSE_LINE_STATUS_CODE;
118 } else { 139 } else {
119 // Did not parse HTTP version. Expect method instead. 140 // Did not parse HTTP version. Expect method instead.
120 for (int i = 0; i < _httpVersionIndex; i++) { 141 for (int i = 0; i < _httpVersionIndex; i++) {
121 _method_or_status_code.addCharCode(_Const.HTTP11[i]); 142 _method_or_status_code.addCharCode(_Const.HTTP11[i]);
143 }
144 _state = _State.REQUEST_LINE_URI;
122 } 145 }
123 _state = _State.REQUEST_LINE_URI; 146 break;
124 }
125 break;
126 147
127 case _State.REQUEST_LINE_METHOD: 148 case _State.REQUEST_LINE_METHOD:
128 if (byte == _CharCode.SP) { 149 if (byte == _CharCode.SP) {
129 _state = _State.REQUEST_LINE_URI; 150 _state = _State.REQUEST_LINE_URI;
130 } else {
131 _method_or_status_code.addCharCode(byte);
132 }
133 break;
134
135 case _State.REQUEST_LINE_URI:
136 if (byte == _CharCode.SP) {
137 _state = _State.REQUEST_LINE_HTTP_VERSION;
138 _httpVersionIndex = 0;
139 } else {
140 _uri_or_reason_phrase.addCharCode(byte);
141 }
142 break;
143
144 case _State.REQUEST_LINE_HTTP_VERSION:
145 if (_httpVersionIndex < _Const.HTTP11.length) {
146 _expect(byte, _Const.HTTP11[_httpVersionIndex]);
147 _httpVersionIndex++;
148 } else {
149 _expect(byte, _CharCode.CR);
150 _state = _State.REQUEST_LINE_ENDING;
151 }
152 break;
153
154 case _State.REQUEST_LINE_ENDING:
155 _expect(byte, _CharCode.LF);
156 _messageType = _MessageType.REQUEST;
157 if (requestStart != null) {
158 requestStart(_method_or_status_code.toString(),
159 _uri_or_reason_phrase.toString());
160 }
161 _method_or_status_code.clear();
162 _uri_or_reason_phrase.clear();
163 _state = _State.HEADER_START;
164 break;
165
166 case _State.RESPONSE_LINE_STATUS_CODE:
167 if (byte == _CharCode.SP) {
168 _state = _State.RESPONSE_LINE_REASON_PHRASE;
169 } else {
170 if (byte < 0x30 && 0x39 < byte) {
171 if (error != null) error("Failed to parse HTTP");
172 _failure = true;
173 } else { 151 } else {
152 if (_Const.SEPARATORS_AND_CR_LF.indexOf(byte) != -1) {
153 throw new HttpParserException("Invalid request method");
154 }
174 _method_or_status_code.addCharCode(byte); 155 _method_or_status_code.addCharCode(byte);
175 } 156 }
176 } 157 break;
177 break;
178 158
179 case _State.RESPONSE_LINE_REASON_PHRASE: 159 case _State.REQUEST_LINE_URI:
180 if (byte == _CharCode.CR) { 160 if (byte == _CharCode.SP) {
181 _state = _State.RESPONSE_LINE_ENDING; 161 _state = _State.REQUEST_LINE_HTTP_VERSION;
182 } else { 162 _httpVersionIndex = 0;
183 _uri_or_reason_phrase.addCharCode(byte); 163 } else {
184 } 164 if (byte == _CharCode.CR || byte == _CharCode.LF) {
185 break; 165 throw new HttpParserException("Invalid request URI");
166 }
167 _uri_or_reason_phrase.addCharCode(byte);
168 }
169 break;
186 170
187 case _State.RESPONSE_LINE_ENDING: 171 case _State.REQUEST_LINE_HTTP_VERSION:
188 _expect(byte, _CharCode.LF); 172 if (_httpVersionIndex < _Const.HTTP11.length) {
189 _messageType == _MessageType.RESPONSE; 173 _expect(byte, _Const.HTTP11[_httpVersionIndex]);
190 if (responseStart != null) { 174 _httpVersionIndex++;
175 } else {
176 _expect(byte, _CharCode.CR);
177 _state = _State.REQUEST_LINE_ENDING;
178 }
179 break;
180
181 case _State.REQUEST_LINE_ENDING:
182 _expect(byte, _CharCode.LF);
183 _messageType = _MessageType.REQUEST;
184 if (requestStart != null) {
185 requestStart(_method_or_status_code.toString(),
186 _uri_or_reason_phrase.toString());
187 }
188 _method_or_status_code.clear();
189 _uri_or_reason_phrase.clear();
190 _state = _State.HEADER_START;
191 break;
192
193 case _State.RESPONSE_LINE_STATUS_CODE:
194 if (byte == _CharCode.SP) {
195 if (_method_or_status_code.length != 3) {
196 throw new HttpParserException("Invalid response status code");
197 }
198 _state = _State.RESPONSE_LINE_REASON_PHRASE;
199 } else {
200 if (byte < 0x30 && 0x39 < byte) {
201 throw new HttpParserException("Invalid response status code");
202 } else {
203 _method_or_status_code.addCharCode(byte);
204 }
205 }
206 break;
207
208 case _State.RESPONSE_LINE_REASON_PHRASE:
209 if (byte == _CharCode.CR) {
210 if (_uri_or_reason_phrase.length == 0) {
211 throw new HttpParserException("Invalid response reason phrase");
212 }
213 _state = _State.RESPONSE_LINE_ENDING;
214 } else {
215 if (byte == _CharCode.CR || byte == _CharCode.LF) {
216 throw new HttpParserException("Invalid response reason phrase");
217 }
218 _uri_or_reason_phrase.addCharCode(byte);
219 }
220 break;
221
222 case _State.RESPONSE_LINE_ENDING:
223 _expect(byte, _CharCode.LF);
224 _messageType == _MessageType.RESPONSE;
191 int statusCode = Math.parseInt(_method_or_status_code.toString()); 225 int statusCode = Math.parseInt(_method_or_status_code.toString());
192 if (statusCode < 100 || statusCode > 599) { 226 if (statusCode < 100 || statusCode > 599) {
193 if (error != null) error("Invalid response status code"); 227 throw new HttpParserException("Invalid response status code");
194 _failure = true;
195 } else { 228 } else {
196 // Check whether this response will never have a body. 229 // Check whether this response will never have a body.
197 _noMessageBody = 230 _noMessageBody =
198 statusCode <= 199 || statusCode == 204 || statusCode == 304; 231 statusCode <= 199 || statusCode == 204 || statusCode == 304;
232 }
233 if (responseStart != null) {
199 responseStart(statusCode, _uri_or_reason_phrase.toString()); 234 responseStart(statusCode, _uri_or_reason_phrase.toString());
200 } 235 }
201 } 236 _method_or_status_code.clear();
202 _method_or_status_code.clear(); 237 _uri_or_reason_phrase.clear();
203 _uri_or_reason_phrase.clear(); 238 _state = _State.HEADER_START;
204 _state = _State.HEADER_START; 239 break;
205 break;
206 240
207 case _State.HEADER_START: 241 case _State.HEADER_START:
208 if (byte == _CharCode.CR) {
209 _state = _State.HEADER_ENDING;
210 } else {
211 // Start of new header field.
212 _headerField.addCharCode(_toLowerCase(byte));
213 _state = _State.HEADER_FIELD;
214 }
215 break;
216
217 case _State.HEADER_FIELD:
218 if (byte == _CharCode.COLON) {
219 _state = _State.HEADER_VALUE_START;
220 } else {
221 _headerField.addCharCode(_toLowerCase(byte));
222 }
223 break;
224
225 case _State.HEADER_VALUE_START:
226 if (byte != _CharCode.SP && byte != _CharCode.HT) {
227 // Start of new header value.
228 _headerValue.addCharCode(byte);
229 _state = _State.HEADER_VALUE;
230 }
231 break;
232
233 case _State.HEADER_VALUE:
234 if (byte == _CharCode.CR) {
235 _state = _State.HEADER_VALUE_FOLDING_OR_ENDING;
236 } else {
237 _headerValue.addCharCode(byte);
238 }
239 break;
240
241 case _State.HEADER_VALUE_FOLDING_OR_ENDING:
242 _expect(byte, _CharCode.LF);
243 _state = _State.HEADER_VALUE_FOLD_OR_END;
244 break;
245
246 case _State.HEADER_VALUE_FOLD_OR_END:
247 if (byte == _CharCode.SP || byte == _CharCode.HT) {
248 _state = _State.HEADER_VALUE_START;
249 } else {
250 String headerField = _headerField.toString();
251 String headerValue =_headerValue.toString();
252 // Ignore the Content-Length header if Transfer-Encoding
253 // is chunked (RFC 2616 section 4.4)
254 if (headerField == "content-length" && !_chunked) {
255 _contentLength = Math.parseInt(headerValue);
256 } else if (headerField == "connection" &&
257 headerValue == "keep-alive") {
258 _keepAlive = true;
259 } else if (headerField == "transfer-encoding" &&
260 headerValue == "chunked") {
261 _chunked = true;
262 _contentLength = -1;
263 }
264 if (headerReceived != null) {
265 headerReceived(headerField, headerValue);
266 }
267 _headerField.clear();
268 _headerValue.clear();
269
270 if (byte == _CharCode.CR) { 242 if (byte == _CharCode.CR) {
271 _state = _State.HEADER_ENDING; 243 _state = _State.HEADER_ENDING;
272 } else { 244 } else {
273 // Start of new header field. 245 // Start of new header field.
274 _headerField.addCharCode(_toLowerCase(byte)); 246 _headerField.addCharCode(_toLowerCase(byte));
275 _state = _State.HEADER_FIELD; 247 _state = _State.HEADER_FIELD;
276 } 248 }
277 } 249 break;
278 break;
279 250
280 case _State.HEADER_ENDING: 251 case _State.HEADER_FIELD:
281 _expect(byte, _CharCode.LF); 252 if (byte == _CharCode.COLON) {
282 if (headersComplete != null) headersComplete(); 253 _state = _State.HEADER_VALUE_START;
254 } else {
255 _headerField.addCharCode(_toLowerCase(byte));
256 }
257 break;
283 258
284 if (_chunked) { 259 case _State.HEADER_VALUE_START:
260 if (byte != _CharCode.SP && byte != _CharCode.HT) {
261 // Start of new header value.
262 _headerValue.addCharCode(byte);
263 _state = _State.HEADER_VALUE;
264 }
265 break;
266
267 case _State.HEADER_VALUE:
268 if (byte == _CharCode.CR) {
269 _state = _State.HEADER_VALUE_FOLDING_OR_ENDING;
270 } else {
271 _headerValue.addCharCode(byte);
272 }
273 break;
274
275 case _State.HEADER_VALUE_FOLDING_OR_ENDING:
276 _expect(byte, _CharCode.LF);
277 _state = _State.HEADER_VALUE_FOLD_OR_END;
278 break;
279
280 case _State.HEADER_VALUE_FOLD_OR_END:
281 if (byte == _CharCode.SP || byte == _CharCode.HT) {
282 _state = _State.HEADER_VALUE_START;
283 } else {
284 String headerField = _headerField.toString();
285 String headerValue =_headerValue.toString();
286 // Ignore the Content-Length header if Transfer-Encoding
287 // is chunked (RFC 2616 section 4.4)
288 if (headerField == "content-length" && !_chunked) {
289 _contentLength = Math.parseInt(headerValue);
290 } else if (headerField == "connection" &&
291 headerValue == "keep-alive") {
292 _keepAlive = true;
293 } else if (headerField == "transfer-encoding" &&
294 headerValue == "chunked") {
295 _chunked = true;
296 _contentLength = -1;
297 }
298 if (headerReceived != null) {
299 headerReceived(headerField, headerValue);
300 }
301 _headerField.clear();
302 _headerValue.clear();
303
304 if (byte == _CharCode.CR) {
305 _state = _State.HEADER_ENDING;
306 } else {
307 // Start of new header field.
308 _headerField.addCharCode(_toLowerCase(byte));
309 _state = _State.HEADER_FIELD;
310 }
311 }
312 break;
313
314 case _State.HEADER_ENDING:
315 _expect(byte, _CharCode.LF);
316 if (headersComplete != null) headersComplete();
317
318 if (_chunked) {
319 _state = _State.CHUNK_SIZE;
320 _remainingContent = 0;
321 } else if (_contentLength == 0 ||
322 (_messageType == _MessageType.REQUEST &&
323 _contentLength == -1) ||
324 (_messageType == _MessageType.RESPONSE &&
325 (_noMessageBody || _responseToMethod == "HEAD"))) {
326 // If there is no message body get ready to process the
327 // next request.
328 if (dataEnd != null) dataEnd();
329 _state = _State.START;
330 } else if (_contentLength > 0) {
331 _remainingContent = _contentLength;
332 _state = _State.BODY;
333 } else {
334 // Neither chunked nor content length. End of body
335 // indicated by close.
336 _state = _State.BODY;
337 }
338 break;
339
340 case _State.CHUNK_SIZE_STARTING_CR:
341 _expect(byte, _CharCode.CR);
342 _state = _State.CHUNK_SIZE_STARTING_LF;
343 break;
344
345 case _State.CHUNK_SIZE_STARTING_LF:
346 _expect(byte, _CharCode.LF);
285 _state = _State.CHUNK_SIZE; 347 _state = _State.CHUNK_SIZE;
286 _remainingContent = 0; 348 break;
287 } else if (_contentLength == 0 || 349
288 (_messageType == _MessageType.REQUEST && 350 case _State.CHUNK_SIZE:
289 _contentLength == -1) || 351 if (byte == _CharCode.CR) {
290 (_messageType == _MessageType.RESPONSE && 352 _state = _State.CHUNK_SIZE_ENDING;
291 (_noMessageBody || _responseToMethod == "HEAD"))) { 353 } else if (byte == _CharCode.SEMI_COLON) {
292 // If there is no message body get ready to process the 354 _state = _State.CHUNK_SIZE_EXTENSION;
293 // next request. 355 } else {
356 int value = _expectHexDigit(byte);
357 _remainingContent = _remainingContent * 16 + value;
358 }
359 break;
360
361 case _State.CHUNK_SIZE_EXTENSION:
362 if (byte == _CharCode.CR) {
363 _state = _State.CHUNK_SIZE_ENDING;
364 }
365 break;
366
367 case _State.CHUNK_SIZE_ENDING:
368 _expect(byte, _CharCode.LF);
369 if (_remainingContent > 0) {
370 _state = _State.BODY;
371 } else {
372 _state = _State.CHUNKED_BODY_DONE_CR;
373 }
374 break;
375
376 case _State.CHUNKED_BODY_DONE_CR:
377 _expect(byte, _CharCode.CR);
378 _state = _State.CHUNKED_BODY_DONE_LF;
379 break;
380
381 case _State.CHUNKED_BODY_DONE_LF:
382 _expect(byte, _CharCode.LF);
294 if (dataEnd != null) dataEnd(); 383 if (dataEnd != null) dataEnd();
295 _state = _State.START; 384 _reset();
296 } else if (_contentLength > 0) { 385 break;
297 _remainingContent = _contentLength;
298 _state = _State.BODY;
299 } else {
300 // Neither chunked nor content length. End of body
301 // indicated by close.
302 _state = _State.BODY;
303 }
304 break;
305 386
306 case _State.CHUNK_SIZE_STARTING_CR: 387 case _State.BODY:
307 _expect(byte, _CharCode.CR); 388 // The body is not handled one byte at the time but in blocks.
308 _state = _State.CHUNK_SIZE_STARTING_LF; 389 int dataAvailable = lastIndex - index;
309 break; 390 ByteArray data;
391 if (_remainingContent == null || dataAvailable <= _remainingContent) {
Mads Ager (google) 2012/03/22 14:30:08 Long line?
Søren Gjesse 2012/03/23 07:34:20 Done.
392 data = new ByteArray(dataAvailable);
393 data.setRange(0, dataAvailable, buffer, index);
394 } else {
395 data = new ByteArray(_remainingContent);
396 data.setRange(0, _remainingContent, buffer, index);
397 }
310 398
311 case _State.CHUNK_SIZE_STARTING_LF: 399 if (dataReceived != null) dataReceived(data);
312 _expect(byte, _CharCode.LF); 400 if (_remainingContent != null) {
313 _state = _State.CHUNK_SIZE; 401 _remainingContent -= data.length;
314 break; 402 }
403 index += data.length;
404 if (_remainingContent == 0) {
405 if (!_chunked) {
406 if (dataEnd != null) dataEnd();
407 _reset();
408 } else {
409 _state = _State.CHUNK_SIZE_STARTING_CR;
410 }
411 }
315 412
316 case _State.CHUNK_SIZE: 413 // Hack - as we always do index++ below.
317 if (byte == _CharCode.CR) { 414 index--;
318 _state = _State.CHUNK_SIZE_ENDING; 415 break;
319 } else if (byte == _CharCode.SEMI_COLON) {
320 _state = _State.CHUNK_SIZE_EXTENSION;
321 } else {
322 int value = _expectHexDigit(byte);
323 _remainingContent = _remainingContent * 16 + value;
324 }
325 break;
326 416
327 case _State.CHUNK_SIZE_EXTENSION: 417 case _state = _State.FAILURE:
328 if (byte == _CharCode.CR) { 418 // Should be unreachable.
329 _state = _State.CHUNK_SIZE_ENDING; 419 assert(false);
330 } 420 break;
331 break;
332 421
333 case _State.CHUNK_SIZE_ENDING: 422 default:
334 _expect(byte, _CharCode.LF); 423 // Should be unreachable.
335 if (_remainingContent > 0) { 424 assert(false);
336 _state = _State.BODY; 425 break;
337 } else { 426 }
338 _state = _State.CHUNKED_BODY_DONE_CR;
339 }
340 break;
341 427
342 case _State.CHUNKED_BODY_DONE_CR: 428 // Move to the next byte.
343 _expect(byte, _CharCode.CR); 429 index++;
344 _state = _State.CHUNKED_BODY_DONE_LF;
345 break;
346
347 case _State.CHUNKED_BODY_DONE_LF:
348 _expect(byte, _CharCode.LF);
349 if (dataEnd != null) dataEnd();
350 _reset();
351 break;
352
353 case _State.BODY:
354 // The body is not handled one byte at the time but in blocks.
355 int dataAvailable = lastIndex - index;
356 ByteArray data;
357 if (_remainingContent == null || dataAvailable <= _remainingContent) {
358 data = new ByteArray(dataAvailable);
359 data.setRange(0, dataAvailable, buffer, index);
360 } else {
361 data = new ByteArray(_remainingContent);
362 data.setRange(0, _remainingContent, buffer, index);
363 }
364
365 if (dataReceived != null) dataReceived(data);
366 if (_remainingContent != null) {
367 _remainingContent -= data.length;
368 }
369 index += data.length;
370 if (_remainingContent == 0) {
371 if (!_chunked) {
372 if (dataEnd != null) dataEnd();
373 _reset();
374 } else {
375 _state = _State.CHUNK_SIZE_STARTING_CR;
376 }
377 }
378
379 // Hack - as we always do index++ below.
380 index--;
381 break;
382
383 default:
384 // Should be unreachable.
385 assert(false);
386 } 430 }
387 431 } catch (var e) {
388 // Move to the next byte. 432 // Report the error through the error callback if any. Otherwise
389 index++; 433 // throw the error.
434 if (error != null) {
435 error(e);
436 _state = _State.FAILURE;
437 } else {
438 throw e;
439 }
390 } 440 }
391 441
392 // Return the number of bytes parsed. 442 // Return the number of bytes parsed.
393 return index - offset; 443 return index - offset;
394 } 444 }
395 445
396 int connectionClosed() { 446 int connectionClosed() {
397 if (!_chunked && _contentLength == -1) { 447 if (!_chunked && _contentLength == -1) {
398 if (dataEnd != null) dataEnd(); 448 if (dataEnd != null) dataEnd();
399 } else { 449 } else {
450 // Report the error through the error callback if any. Otherwise
Mads Ager (google) 2012/03/22 14:30:08 Maybe this should really be our behavior for all o
Søren Gjesse 2012/03/23 07:34:20 That might be a good idea. This can lead to VM ter
451 // throw the error.
452 var e = new HttpParserException(
453 "Connection closed before full body was received");
400 if (error != null) { 454 if (error != null) {
401 error("Connection closed before full body was received"); 455 error(e);
456 } else {
457 throw e;
402 } 458 }
403 } 459 }
404 } 460 }
405 461
406 int get messageType() => _messageType; 462 int get messageType() => _messageType;
407 int get contentLength() => _contentLength; 463 int get contentLength() => _contentLength;
408 bool get keepAlive() => _keepAlive; 464 bool get keepAlive() => _keepAlive;
409 465
410 void set responseToMethod(String method) => _responseToMethod = method; 466 void set responseToMethod(String method) => _responseToMethod = method;
411 467
412 _reset() { 468 _reset() {
413 _state = _State.START; 469 _state = _State.START;
414 _failure = false;
415 _messageType = _MessageType.UNDETERMINED; 470 _messageType = _MessageType.UNDETERMINED;
416 _headerField = new StringBuffer(); 471 _headerField = new StringBuffer();
417 _headerValue = new StringBuffer(); 472 _headerValue = new StringBuffer();
418 _method_or_status_code = new StringBuffer(); 473 _method_or_status_code = new StringBuffer();
419 _uri_or_reason_phrase = new StringBuffer(); 474 _uri_or_reason_phrase = new StringBuffer();
420 475
421 _contentLength = -1; 476 _contentLength = -1;
422 _keepAlive = false; 477 _keepAlive = false;
423 _chunked = false; 478 _chunked = false;
424 479
425 _noMessageBody = false; 480 _noMessageBody = false;
426 _responseToMethod = null; 481 _responseToMethod = null;
427 _remainingContent = null; 482 _remainingContent = null;
428 } 483 }
429 484
430 int _toLowerCase(int byte) { 485 int _toLowerCase(int byte) {
431 final int aCode = "A".charCodeAt(0); 486 final int aCode = "A".charCodeAt(0);
432 final int zCode = "Z".charCodeAt(0); 487 final int zCode = "Z".charCodeAt(0);
433 final int delta = "a".charCodeAt(0) - aCode; 488 final int delta = "a".charCodeAt(0) - aCode;
434 return (aCode <= byte && byte <= zCode) ? byte + delta : byte; 489 return (aCode <= byte && byte <= zCode) ? byte + delta : byte;
435 } 490 }
436 491
437 int _expect(int val1, int val2) { 492 int _expect(int val1, int val2) {
438 if (val1 != val2) { 493 if (val1 != val2) {
439 if (error != null) error("Failed to parse HTTP"); 494 throw new HttpParserException("Failed to parse HTTP");
440 _failure = true;
441 } 495 }
442 } 496 }
443 497
444 int _expectHexDigit(int byte) { 498 int _expectHexDigit(int byte) {
445 if (0x30 <= byte && byte <= 0x39) { 499 if (0x30 <= byte && byte <= 0x39) {
446 return byte - 0x30; // 0 - 9 500 return byte - 0x30; // 0 - 9
447 } else if (0x41 <= byte && byte <= 0x46) { 501 } else if (0x41 <= byte && byte <= 0x46) {
448 return byte - 0x41 + 10; // A - F 502 return byte - 0x41 + 10; // A - F
449 } else if (0x61 <= byte && byte <= 0x66) { 503 } else if (0x61 <= byte && byte <= 0x66) {
450 return byte - 0x61 + 10; // a - f 504 return byte - 0x61 + 10; // a - f
451 } else { 505 } else {
452 _failure = true; 506 throw new HttpParserException("Failed to parse HTTP");
453 return 0; 507 return 0;
454 } 508 }
455 } 509 }
456 510
457 int _state; 511 int _state;
458 bool _failure;
459 int _httpVersionIndex; 512 int _httpVersionIndex;
460 int _messageType; 513 int _messageType;
461 StringBuffer _method_or_status_code; 514 StringBuffer _method_or_status_code;
462 StringBuffer _uri_or_reason_phrase; 515 StringBuffer _uri_or_reason_phrase;
463 StringBuffer _headerField; 516 StringBuffer _headerField;
464 StringBuffer _headerValue; 517 StringBuffer _headerValue;
465 518
466 int _contentLength; 519 int _contentLength;
467 bool _keepAlive; 520 bool _keepAlive;
468 bool _chunked; 521 bool _chunked;
469 522
470 bool _noMessageBody; 523 bool _noMessageBody;
471 String _responseToMethod; // Indicates the method used for the request. 524 String _responseToMethod; // Indicates the method used for the request.
472 int _remainingContent; 525 int _remainingContent;
473 526
474 // Callbacks. 527 // Callbacks.
475 Function requestStart; 528 Function requestStart;
476 Function responseStart; 529 Function responseStart;
477 Function headerReceived; 530 Function headerReceived;
478 Function headersComplete; 531 Function headersComplete;
479 Function dataReceived; 532 Function dataReceived;
480 Function dataEnd; 533 Function dataEnd;
481 Function error; 534 Function error;
482 } 535 }
536
537
538 class HttpParserException implements Exception {
539 const HttpParserException([String this.message = ""]);
540 String toString() => "HttpParserException: $message";
541 final String message;
542 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698