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

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

Issue 11337019: Use patching for dart:io. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Address comments Created 8 years, 1 month 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
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 // Global constants.
6 class _Const {
7 // Bytes for "HTTP".
8 static const HTTP = const [72, 84, 84, 80];
9 // Bytes for "HTTP/1.".
10 static const HTTP1DOT = const [72, 84, 84, 80, 47, 49, 46];
11 // Bytes for "HTTP/1.0".
12 static const HTTP10 = const [72, 84, 84, 80, 47, 49, 46, 48];
13 // Bytes for "HTTP/1.1".
14 static const HTTP11 = const [72, 84, 84, 80, 47, 49, 46, 49];
15
16 static const END_CHUNKED = const [0x30, 13, 10, 13, 10];
17
18 // Bytes for '()<>@,;:\\"/[]?={} \t'.
19 static const SEPARATORS = const [40, 41, 60, 62, 64, 44, 59, 58, 92, 34, 47,
20 91, 93, 63, 61, 123, 125, 32, 9];
21
22 // Bytes for '()<>@,;:\\"/[]?={} \t\r\n'.
23 static const SEPARATORS_AND_CR_LF = const [40, 41, 60, 62, 64, 44, 59, 58, 92,
24 34, 47, 91, 93, 63, 61, 123, 125,
25 32, 9, 13, 10];
26 }
27
28
29 // Frequently used character codes.
30 class _CharCode {
31 static const int HT = 9;
32 static const int LF = 10;
33 static const int CR = 13;
34 static const int SP = 32;
35 static const int COMMA = 44;
36 static const int DASH = 45;
37 static const int SLASH = 47;
38 static const int ZERO = 48;
39 static const int ONE = 49;
40 static const int COLON = 58;
41 static const int SEMI_COLON = 59;
42 }
43
44
45 // States of the HTTP parser state machine.
46 class _State {
47 static const int START = 0;
48 static const int METHOD_OR_RESPONSE_HTTP_VERSION = 1;
49 static const int RESPONSE_HTTP_VERSION = 2;
50 static const int REQUEST_LINE_METHOD = 3;
51 static const int REQUEST_LINE_URI = 4;
52 static const int REQUEST_LINE_HTTP_VERSION = 5;
53 static const int REQUEST_LINE_ENDING = 6;
54 static const int RESPONSE_LINE_STATUS_CODE = 7;
55 static const int RESPONSE_LINE_REASON_PHRASE = 8;
56 static const int RESPONSE_LINE_ENDING = 9;
57 static const int HEADER_START = 10;
58 static const int HEADER_FIELD = 11;
59 static const int HEADER_VALUE_START = 12;
60 static const int HEADER_VALUE = 13;
61 static const int HEADER_VALUE_FOLDING_OR_ENDING = 14;
62 static const int HEADER_VALUE_FOLD_OR_END = 15;
63 static const int HEADER_ENDING = 16;
64
65 static const int CHUNK_SIZE_STARTING_CR = 17;
66 static const int CHUNK_SIZE_STARTING_LF = 18;
67 static const int CHUNK_SIZE = 19;
68 static const int CHUNK_SIZE_EXTENSION = 20;
69 static const int CHUNK_SIZE_ENDING = 21;
70 static const int CHUNKED_BODY_DONE_CR = 22;
71 static const int CHUNKED_BODY_DONE_LF = 23;
72 static const int BODY = 24;
73 static const int CLOSED = 25;
74 static const int UPGRADED = 26;
75 static const int FAILURE = 27;
76
77 static const int FIRST_BODY_STATE = CHUNK_SIZE_STARTING_CR;
78 }
79
80 // HTTP version of the request or response being parsed.
81 class _HttpVersion {
82 static const int UNDETERMINED = 0;
83 static const int HTTP10 = 1;
84 static const int HTTP11 = 2;
85 }
86
87 // States of the HTTP parser state machine.
88 class _MessageType {
89 static const int UNDETERMINED = 0;
90 static const int REQUEST = 1;
91 static const int RESPONSE = 0;
92 }
93
94
95 /**
96 * HTTP parser which parses the HTTP stream as data is supplied
97 * through the [:writeList:] and [:connectionClosed:] methods. As the
98 * data is parsed the following callbacks are called:
99 *
100 * [:requestStart:]
101 * [:responseStart:]
102 * [:headerReceived:]
103 * [:headersComplete:]
104 * [:dataReceived:]
105 * [:dataEnd:]
106 * [:error:]
107 *
108 * If an HTTP parser error occours it is possible to get an exception
109 * thrown from the [:writeList:] and [:connectionClosed:] methods if
110 * the error callback is not set.
111 *
112 * The connection upgrades (e.g. switching from HTTP/1.1 to the
113 * WebSocket protocol) is handled in a special way. If connection
114 * upgrade is specified in the headers, then on the callback to
115 * [:headersComplete:] the [:upgrade:] property on the [:HttpParser:]
116 * object will be [:true:] indicating that from now on the protocol is
117 * not HTTP anymore and no more callbacks will happen, that is
118 * [:dataReceived:] and [:dataEnd:] are not called in this case as
119 * there is no more HTTP data. After the upgrade the call to
120 * [:writeList:] causing the upgrade will return with the number of
121 * bytes parsed as HTTP. Any unparsed bytes is part of the protocol
122 * the connection is upgrading to and should be handled according to
123 * that protocol.
124 */
125 class _HttpParser {
126 _HttpParser() {
127 _reset();
128 }
129
130 // From RFC 2616.
131 // generic-message = start-line
132 // *(message-header CRLF)
133 // CRLF
134 // [ message-body ]
135 // start-line = Request-Line | Status-Line
136 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
137 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
138 // message-header = field-name ":" [ field-value ]
139 int writeList(List<int> buffer, int offset, int count) {
140 int index = offset;
141 int lastIndex = offset + count;
142 try {
143 if (_state == _State.CLOSED) {
144 throw new HttpParserException("Data on closed connection");
145 }
146 if (_state == _State.UPGRADED) {
147 throw new HttpParserException("Data on upgraded connection");
148 }
149 if (_state == _State.FAILURE) {
150 throw new HttpParserException("Data on failed connection");
151 }
152 while ((index < lastIndex) &&
153 _state != _State.FAILURE &&
154 _state != _State.UPGRADED) {
155 int byte = buffer[index];
156 switch (_state) {
157 case _State.START:
158 if (byte == _Const.HTTP[0]) {
159 // Start parsing method or HTTP version.
160 _httpVersionIndex = 1;
161 _state = _State.METHOD_OR_RESPONSE_HTTP_VERSION;
162 } else {
163 // Start parsing method.
164 if (!_isTokenChar(byte)) {
165 throw new HttpParserException("Invalid request method");
166 }
167 _method_or_status_code.addCharCode(byte);
168 _state = _State.REQUEST_LINE_METHOD;
169 }
170 break;
171
172 case _State.METHOD_OR_RESPONSE_HTTP_VERSION:
173 if (_httpVersionIndex < _Const.HTTP.length &&
174 byte == _Const.HTTP[_httpVersionIndex]) {
175 // Continue parsing HTTP version.
176 _httpVersionIndex++;
177 } else if (_httpVersionIndex == _Const.HTTP.length &&
178 byte == _CharCode.SLASH) {
179 // HTTP/ parsed. As method is a token this cannot be a
180 // method anymore.
181 _httpVersionIndex++;
182 _state = _State.RESPONSE_HTTP_VERSION;
183 } else {
184 // Did not parse HTTP version. Expect method instead.
185 for (int i = 0; i < _httpVersionIndex; i++) {
186 _method_or_status_code.addCharCode(_Const.HTTP[i]);
187 }
188 if (byte == _CharCode.SP) {
189 _state = _State.REQUEST_LINE_URI;
190 } else {
191 _method_or_status_code.addCharCode(byte);
192 _httpVersion = _HttpVersion.UNDETERMINED;
193 _state = _State.REQUEST_LINE_METHOD;
194 }
195 }
196 break;
197
198 case _State.RESPONSE_HTTP_VERSION:
199 if (_httpVersionIndex < _Const.HTTP1DOT.length) {
200 // Continue parsing HTTP version.
201 _expect(byte, _Const.HTTP1DOT[_httpVersionIndex]);
202 _httpVersionIndex++;
203 } else if (_httpVersionIndex == _Const.HTTP1DOT.length &&
204 byte == _CharCode.ONE) {
205 // HTTP/1.1 parsed.
206 _httpVersion = _HttpVersion.HTTP11;
207 _persistentConnection = true;
208 _httpVersionIndex++;
209 } else if (_httpVersionIndex == _Const.HTTP1DOT.length &&
210 byte == _CharCode.ZERO) {
211 // HTTP/1.0 parsed.
212 _httpVersion = _HttpVersion.HTTP10;
213 _persistentConnection = false;
214 _httpVersionIndex++;
215 } else if (_httpVersionIndex == _Const.HTTP1DOT.length + 1) {
216 _expect(byte, _CharCode.SP);
217 // HTTP version parsed.
218 _state = _State.RESPONSE_LINE_STATUS_CODE;
219 } else {
220 throw new HttpParserException("Invalid response line");
221 }
222 break;
223
224 case _State.REQUEST_LINE_METHOD:
225 if (byte == _CharCode.SP) {
226 _state = _State.REQUEST_LINE_URI;
227 } else {
228 if (_Const.SEPARATORS_AND_CR_LF.indexOf(byte) != -1) {
229 throw new HttpParserException("Invalid request method");
230 }
231 _method_or_status_code.addCharCode(byte);
232 }
233 break;
234
235 case _State.REQUEST_LINE_URI:
236 if (byte == _CharCode.SP) {
237 if (_uri_or_reason_phrase.length == 0) {
238 throw new HttpParserException("Invalid request URI");
239 }
240 _state = _State.REQUEST_LINE_HTTP_VERSION;
241 _httpVersionIndex = 0;
242 } else {
243 if (byte == _CharCode.CR || byte == _CharCode.LF) {
244 throw new HttpParserException("Invalid request URI");
245 }
246 _uri_or_reason_phrase.addCharCode(byte);
247 }
248 break;
249
250 case _State.REQUEST_LINE_HTTP_VERSION:
251 if (_httpVersionIndex < _Const.HTTP1DOT.length) {
252 _expect(byte, _Const.HTTP11[_httpVersionIndex]);
253 _httpVersionIndex++;
254 } else if (_httpVersionIndex == _Const.HTTP1DOT.length) {
255 if (byte == _CharCode.ONE) {
256 // HTTP/1.1 parsed.
257 _httpVersion = _HttpVersion.HTTP11;
258 _persistentConnection = true;
259 _httpVersionIndex++;
260 } else if (byte == _CharCode.ZERO) {
261 // HTTP/1.0 parsed.
262 _httpVersion = _HttpVersion.HTTP10;
263 _persistentConnection = false;
264 _httpVersionIndex++;
265 } else {
266 throw new HttpParserException("Invalid response line");
267 }
268 } else {
269 _expect(byte, _CharCode.CR);
270 _state = _State.REQUEST_LINE_ENDING;
271 }
272 break;
273
274 case _State.REQUEST_LINE_ENDING:
275 _expect(byte, _CharCode.LF);
276 _messageType = _MessageType.REQUEST;
277 if (requestStart != null) {
278 requestStart(_method_or_status_code.toString(),
279 _uri_or_reason_phrase.toString(),
280 version);
281 }
282 _method_or_status_code.clear();
283 _uri_or_reason_phrase.clear();
284 _state = _State.HEADER_START;
285 break;
286
287 case _State.RESPONSE_LINE_STATUS_CODE:
288 if (byte == _CharCode.SP) {
289 if (_method_or_status_code.length != 3) {
290 throw new HttpParserException("Invalid response status code");
291 }
292 _state = _State.RESPONSE_LINE_REASON_PHRASE;
293 } else {
294 if (byte < 0x30 && 0x39 < byte) {
295 throw new HttpParserException("Invalid response status code");
296 } else {
297 _method_or_status_code.addCharCode(byte);
298 }
299 }
300 break;
301
302 case _State.RESPONSE_LINE_REASON_PHRASE:
303 if (byte == _CharCode.CR) {
304 if (_uri_or_reason_phrase.length == 0) {
305 throw new HttpParserException("Invalid response reason phrase");
306 }
307 _state = _State.RESPONSE_LINE_ENDING;
308 } else {
309 if (byte == _CharCode.CR || byte == _CharCode.LF) {
310 throw new HttpParserException("Invalid response reason phrase");
311 }
312 _uri_or_reason_phrase.addCharCode(byte);
313 }
314 break;
315
316 case _State.RESPONSE_LINE_ENDING:
317 _expect(byte, _CharCode.LF);
318 _messageType == _MessageType.RESPONSE;
319 int statusCode = parseInt(_method_or_status_code.toString());
320 if (statusCode < 100 || statusCode > 599) {
321 throw new HttpParserException("Invalid response status code");
322 } else {
323 // Check whether this response will never have a body.
324 _noMessageBody =
325 statusCode <= 199 || statusCode == 204 || statusCode == 304;
326 }
327 if (responseStart != null) {
328 responseStart(statusCode, _uri_or_reason_phrase.toString(), versio n);
329 }
330 _method_or_status_code.clear();
331 _uri_or_reason_phrase.clear();
332 _state = _State.HEADER_START;
333 break;
334
335 case _State.HEADER_START:
336 if (byte == _CharCode.CR) {
337 _state = _State.HEADER_ENDING;
338 } else {
339 // Start of new header field.
340 _headerField.addCharCode(_toLowerCase(byte));
341 _state = _State.HEADER_FIELD;
342 }
343 break;
344
345 case _State.HEADER_FIELD:
346 if (byte == _CharCode.COLON) {
347 _state = _State.HEADER_VALUE_START;
348 } else {
349 if (!_isTokenChar(byte)) {
350 throw new HttpParserException("Invalid header field name");
351 }
352 _headerField.addCharCode(_toLowerCase(byte));
353 }
354 break;
355
356 case _State.HEADER_VALUE_START:
357 if (byte == _CharCode.CR) {
358 _state = _State.HEADER_VALUE_FOLDING_OR_ENDING;
359 } else if (byte != _CharCode.SP && byte != _CharCode.HT) {
360 // Start of new header value.
361 _headerValue.addCharCode(byte);
362 _state = _State.HEADER_VALUE;
363 }
364 break;
365
366 case _State.HEADER_VALUE:
367 if (byte == _CharCode.CR) {
368 _state = _State.HEADER_VALUE_FOLDING_OR_ENDING;
369 } else {
370 _headerValue.addCharCode(byte);
371 }
372 break;
373
374 case _State.HEADER_VALUE_FOLDING_OR_ENDING:
375 _expect(byte, _CharCode.LF);
376 _state = _State.HEADER_VALUE_FOLD_OR_END;
377 break;
378
379 case _State.HEADER_VALUE_FOLD_OR_END:
380 if (byte == _CharCode.SP || byte == _CharCode.HT) {
381 _state = _State.HEADER_VALUE_START;
382 } else {
383 String headerField = _headerField.toString();
384 String headerValue =_headerValue.toString();
385 bool reportHeader = true;
386 if (headerField == "content-length" && !_chunked) {
387 // Ignore the Content-Length header if Transfer-Encoding
388 // is chunked (RFC 2616 section 4.4)
389 _contentLength = parseInt(headerValue);
390 } else if (headerField == "connection") {
391 List<String> tokens = _tokenizeFieldValue(headerValue);
392 for (int i = 0; i < tokens.length; i++) {
393 String token = tokens[i].toLowerCase();
394 if (token == "keep-alive") {
395 _persistentConnection = true;
396 } else if (token == "close") {
397 _persistentConnection = false;
398 } else if (token == "upgrade") {
399 _connectionUpgrade = true;
400 }
401 if (headerReceived != null) {
402 headerReceived(headerField, token);
403 }
404 }
405 reportHeader = false;
406 } else if (headerField == "transfer-encoding" &&
407 headerValue.toLowerCase() == "chunked") {
408 // Ignore the Content-Length header if Transfer-Encoding
409 // is chunked (RFC 2616 section 4.4)
410 _chunked = true;
411 _contentLength = -1;
412 }
413 if (reportHeader && headerReceived != null) {
414 headerReceived(headerField, headerValue);
415 }
416 _headerField.clear();
417 _headerValue.clear();
418
419 if (byte == _CharCode.CR) {
420 _state = _State.HEADER_ENDING;
421 } else {
422 // Start of new header field.
423 _headerField.addCharCode(_toLowerCase(byte));
424 _state = _State.HEADER_FIELD;
425 }
426 }
427 break;
428
429 case _State.HEADER_ENDING:
430 _expect(byte, _CharCode.LF);
431 // If a request message has neither Content-Length nor
432 // Transfer-Encoding the message must not have a body (RFC
433 // 2616 section 4.3).
434 if (_messageType == _MessageType.REQUEST &&
435 _contentLength < 0 &&
436 _chunked == false) {
437 _contentLength = 0;
438 }
439 if (_connectionUpgrade) {
440 _state = _State.UPGRADED;
441 _unparsedData =
442 buffer.getRange(index + 1, count - (index + 1 - offset));
443 if (headersComplete != null) headersComplete();
444 } else {
445 if (headersComplete != null) headersComplete();
446 if (_chunked) {
447 _state = _State.CHUNK_SIZE;
448 _remainingContent = 0;
449 } else if (_contentLength == 0 ||
450 (_messageType == _MessageType.RESPONSE &&
451 (_noMessageBody || _responseToMethod == "HEAD"))) {
452 // If there is no message body get ready to process the
453 // next request.
454 _bodyEnd();
455 _reset();
456 } else if (_contentLength > 0) {
457 _remainingContent = _contentLength;
458 _state = _State.BODY;
459 } else {
460 // Neither chunked nor content length. End of body
461 // indicated by close.
462 _state = _State.BODY;
463 }
464 }
465 break;
466
467 case _State.CHUNK_SIZE_STARTING_CR:
468 _expect(byte, _CharCode.CR);
469 _state = _State.CHUNK_SIZE_STARTING_LF;
470 break;
471
472 case _State.CHUNK_SIZE_STARTING_LF:
473 _expect(byte, _CharCode.LF);
474 _state = _State.CHUNK_SIZE;
475 break;
476
477 case _State.CHUNK_SIZE:
478 if (byte == _CharCode.CR) {
479 _state = _State.CHUNK_SIZE_ENDING;
480 } else if (byte == _CharCode.SEMI_COLON) {
481 _state = _State.CHUNK_SIZE_EXTENSION;
482 } else {
483 int value = _expectHexDigit(byte);
484 _remainingContent = _remainingContent * 16 + value;
485 }
486 break;
487
488 case _State.CHUNK_SIZE_EXTENSION:
489 if (byte == _CharCode.CR) {
490 _state = _State.CHUNK_SIZE_ENDING;
491 }
492 break;
493
494 case _State.CHUNK_SIZE_ENDING:
495 _expect(byte, _CharCode.LF);
496 if (_remainingContent > 0) {
497 _state = _State.BODY;
498 } else {
499 _state = _State.CHUNKED_BODY_DONE_CR;
500 }
501 break;
502
503 case _State.CHUNKED_BODY_DONE_CR:
504 _expect(byte, _CharCode.CR);
505 _state = _State.CHUNKED_BODY_DONE_LF;
506 break;
507
508 case _State.CHUNKED_BODY_DONE_LF:
509 _expect(byte, _CharCode.LF);
510 _bodyEnd();
511 _reset();
512 break;
513
514 case _State.BODY:
515 // The body is not handled one byte at a time but in blocks.
516 int dataAvailable = lastIndex - index;
517 List<int> data;
518 if (_remainingContent == null ||
519 dataAvailable <= _remainingContent) {
520 data = new Uint8List(dataAvailable);
521 data.setRange(0, dataAvailable, buffer, index);
522 } else {
523 data = new Uint8List(_remainingContent);
524 data.setRange(0, _remainingContent, buffer, index);
525 }
526
527 if (dataReceived != null) dataReceived(data);
528 if (_remainingContent != null) {
529 _remainingContent -= data.length;
530 }
531 index += data.length;
532 if (_remainingContent == 0) {
533 if (!_chunked) {
534 _bodyEnd();
535 _reset();
536 } else {
537 _state = _State.CHUNK_SIZE_STARTING_CR;
538 }
539 }
540
541 // Hack - as we always do index++ below.
542 index--;
543 break;
544
545 case _State.FAILURE:
546 // Should be unreachable.
547 assert(false);
548 break;
549
550 default:
551 // Should be unreachable.
552 assert(false);
553 break;
554 }
555
556 // Move to the next byte.
557 index++;
558 }
559 } catch (e) {
560 // Report the error through the error callback if any. Otherwise
561 // throw the error.
562 if (error != null) {
563 error(e);
564 _state = _State.FAILURE;
565 } else {
566 throw e;
567 }
568 }
569
570 // Return the number of bytes parsed.
571 return index - offset;
572 }
573
574 void connectionClosed() {
575 if (_state < _State.FIRST_BODY_STATE) {
576 _state = _State.FAILURE;
577 // Report the error through the error callback if any. Otherwise
578 // throw the error.
579 var e = new HttpParserException(
580 "Connection closed before full header was received");
581 if (error != null) {
582 error(e);
583 return;
584 }
585 throw e;
586 }
587
588 if (!_chunked && _contentLength == -1) {
589 if (_state != _State.START) {
590 if (dataEnd != null) dataEnd(true);
591 }
592 _state = _State.CLOSED;
593 } else {
594 _state = _State.FAILURE;
595 // Report the error through the error callback if any. Otherwise
596 // throw the error.
597 var e = new HttpParserException(
598 "Connection closed before full body was received");
599 if (error != null) {
600 error(e);
601 return;
602 }
603 throw e;
604 }
605 }
606
607 String get version {
608 switch (_httpVersion) {
609 case _HttpVersion.HTTP10:
610 return "1.0";
611 case _HttpVersion.HTTP11:
612 return "1.1";
613 }
614 return null;
615 }
616
617 int get messageType => _messageType;
618 int get contentLength => _contentLength;
619 bool get upgrade => _connectionUpgrade && _state == _State.UPGRADED;
620 bool get persistentConnection => _persistentConnection;
621
622 void set responseToMethod(String method) { _responseToMethod = method; }
623
624 bool get isIdle => _state == _State.START;
625
626 List<int> get unparsedData => _unparsedData;
627
628 void _bodyEnd() {
629 if (dataEnd != null) {
630 dataEnd(_messageType == _MessageType.RESPONSE && !_persistentConnection);
631 }
632 }
633
634 _reset() {
635 _state = _State.START;
636 _messageType = _MessageType.UNDETERMINED;
637 _headerField = new StringBuffer();
638 _headerValue = new StringBuffer();
639 _method_or_status_code = new StringBuffer();
640 _uri_or_reason_phrase = new StringBuffer();
641
642 _httpVersion = _HttpVersion.UNDETERMINED;
643 _contentLength = -1;
644 _persistentConnection = false;
645 _connectionUpgrade = false;
646 _chunked = false;
647
648 _noMessageBody = false;
649 _responseToMethod = null;
650 _remainingContent = null;
651 }
652
653 bool _isTokenChar(int byte) {
654 return byte > 31 && byte < 128 && _Const.SEPARATORS.indexOf(byte) == -1;
655 }
656
657 List<String> _tokenizeFieldValue(String headerValue) {
658 List<String> tokens = new List<String>();
659 int start = 0;
660 int index = 0;
661 while (index < headerValue.length) {
662 if (headerValue[index] == ",") {
663 tokens.add(headerValue.substring(start, index));
664 start = index + 1;
665 } else if (headerValue[index] == " " || headerValue[index] == "\t") {
666 start++;
667 }
668 index++;
669 }
670 tokens.add(headerValue.substring(start, index));
671 return tokens;
672 }
673
674 int _toLowerCase(int byte) {
675 final int aCode = "A".charCodeAt(0);
676 final int zCode = "Z".charCodeAt(0);
677 final int delta = "a".charCodeAt(0) - aCode;
678 return (aCode <= byte && byte <= zCode) ? byte + delta : byte;
679 }
680
681 int _expect(int val1, int val2) {
682 if (val1 != val2) {
683 throw new HttpParserException("Failed to parse HTTP");
684 }
685 }
686
687 int _expectHexDigit(int byte) {
688 if (0x30 <= byte && byte <= 0x39) {
689 return byte - 0x30; // 0 - 9
690 } else if (0x41 <= byte && byte <= 0x46) {
691 return byte - 0x41 + 10; // A - F
692 } else if (0x61 <= byte && byte <= 0x66) {
693 return byte - 0x61 + 10; // a - f
694 } else {
695 throw new HttpParserException("Failed to parse HTTP");
696 }
697 }
698
699 int _state;
700 int _httpVersionIndex;
701 int _messageType;
702 StringBuffer _method_or_status_code;
703 StringBuffer _uri_or_reason_phrase;
704 StringBuffer _headerField;
705 StringBuffer _headerValue;
706
707 int _httpVersion;
708 int _contentLength;
709 bool _persistentConnection;
710 bool _connectionUpgrade;
711 bool _chunked;
712
713 bool _noMessageBody;
714 String _responseToMethod; // Indicates the method used for the request.
715 int _remainingContent;
716
717 List<int> _unparsedData; // Unparsed data after connection upgrade.
718 // Callbacks.
719 Function requestStart;
720 Function responseStart;
721 Function headerReceived;
722 Function headersComplete;
723 Function dataReceived;
724 Function dataEnd;
725 Function error;
726 }
727
728
729 class HttpParserException implements Exception {
730 const HttpParserException([String this.message = ""]);
731 String toString() => "HttpParserException: $message";
732 final String message;
733 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698