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

Side by Side Diff: sdk/lib/io/http_parser.dart

Issue 11415027: Refactor the HTTP parser to collect headers (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Addessed review 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
« no previous file with comments | « sdk/lib/io/http_impl.dart ('k') | sdk/lib/io/iolib_sources.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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". 7 // Bytes for "HTTP".
8 static const HTTP = const [72, 84, 84, 80]; 8 static const HTTP = const [72, 84, 84, 80];
9 // Bytes for "HTTP/1.". 9 // Bytes for "HTTP/1.".
10 static const HTTP1DOT = const [72, 84, 84, 80, 47, 49, 46]; 10 static const HTTP1DOT = const [72, 84, 84, 80, 47, 49, 46];
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
92 } 92 }
93 93
94 94
95 /** 95 /**
96 * HTTP parser which parses the HTTP stream as data is supplied 96 * HTTP parser which parses the HTTP stream as data is supplied
97 * through the [:writeList:] and [:connectionClosed:] methods. As the 97 * through the [:writeList:] and [:connectionClosed:] methods. As the
98 * data is parsed the following callbacks are called: 98 * data is parsed the following callbacks are called:
99 * 99 *
100 * [:requestStart:] 100 * [:requestStart:]
101 * [:responseStart:] 101 * [:responseStart:]
102 * [:headerReceived:]
103 * [:headersComplete:]
104 * [:dataReceived:] 102 * [:dataReceived:]
105 * [:dataEnd:] 103 * [:dataEnd:]
106 * [:closed:] 104 * [:closed:]
107 * [:error:] 105 * [:error:]
108 * 106 *
109 * If an HTTP parser error occours it is possible to get an exception 107 * If an HTTP parser error occours it is possible to get an exception
110 * thrown from the [:writeList:] and [:connectionClosed:] methods if 108 * thrown from the [:writeList:] and [:connectionClosed:] methods if
111 * the error callback is not set. 109 * the error callback is not set.
112 * 110 *
113 * The connection upgrades (e.g. switching from HTTP/1.1 to the 111 * The connection upgrades (e.g. switching from HTTP/1.1 to the
114 * WebSocket protocol) is handled in a special way. If connection 112 * WebSocket protocol) is handled in a special way. If connection
115 * upgrade is specified in the headers, then on the callback to 113 * upgrade is specified in the headers, then on the callback to
116 * [:headersComplete:] the [:upgrade:] property on the [:HttpParser:] 114 * [:responseStart:] the [:upgrade:] property on the [:HttpParser:]
117 * object will be [:true:] indicating that from now on the protocol is 115 * object will be [:true:] indicating that from now on the protocol is
118 * not HTTP anymore and no more callbacks will happen, that is 116 * not HTTP anymore and no more callbacks will happen, that is
119 * [:dataReceived:] and [:dataEnd:] are not called in this case as 117 * [:dataReceived:] and [:dataEnd:] are not called in this case as
120 * there is no more HTTP data. After the upgrade the method 118 * there is no more HTTP data. After the upgrade the method
121 * [:readUnparsedData:] can be used to read any remaining bytes in the 119 * [:readUnparsedData:] can be used to read any remaining bytes in the
122 * HTTP parser which are part of the protocol the connection is 120 * HTTP parser which are part of the protocol the connection is
123 * upgrading to. These bytes cannot be processed by the HTTP parser 121 * upgrading to. These bytes cannot be processed by the HTTP parser
124 * and should be handled according to whatever protocol is being 122 * and should be handled according to whatever protocol is being
125 * upgraded to. 123 * upgraded to.
126 */ 124 */
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after
282 } 280 }
283 } else { 281 } else {
284 _expect(byte, _CharCode.CR); 282 _expect(byte, _CharCode.CR);
285 _state = _State.REQUEST_LINE_ENDING; 283 _state = _State.REQUEST_LINE_ENDING;
286 } 284 }
287 break; 285 break;
288 286
289 case _State.REQUEST_LINE_ENDING: 287 case _State.REQUEST_LINE_ENDING:
290 _expect(byte, _CharCode.LF); 288 _expect(byte, _CharCode.LF);
291 _messageType = _MessageType.REQUEST; 289 _messageType = _MessageType.REQUEST;
292 requestStart(new String.fromCharCodes(_method_or_status_code),
293 new String.fromCharCodes(_uri_or_reason_phrase),
294 version);
295 _method_or_status_code.clear();
296 _uri_or_reason_phrase.clear();
297 _state = _State.HEADER_START; 290 _state = _State.HEADER_START;
298 break; 291 break;
299 292
300 case _State.RESPONSE_LINE_STATUS_CODE: 293 case _State.RESPONSE_LINE_STATUS_CODE:
301 if (byte == _CharCode.SP) { 294 if (byte == _CharCode.SP) {
302 if (_method_or_status_code.length != 3) { 295 if (_method_or_status_code.length != 3) {
303 throw new HttpParserException("Invalid response status code"); 296 throw new HttpParserException("Invalid response status code");
304 } 297 }
305 _state = _State.RESPONSE_LINE_REASON_PHRASE; 298 _state = _State.RESPONSE_LINE_REASON_PHRASE;
306 } else { 299 } else {
(...skipping 15 matching lines...) Expand all
322 if (byte == _CharCode.CR || byte == _CharCode.LF) { 315 if (byte == _CharCode.CR || byte == _CharCode.LF) {
323 throw new HttpParserException("Invalid response reason phrase"); 316 throw new HttpParserException("Invalid response reason phrase");
324 } 317 }
325 _uri_or_reason_phrase.add(byte); 318 _uri_or_reason_phrase.add(byte);
326 } 319 }
327 break; 320 break;
328 321
329 case _State.RESPONSE_LINE_ENDING: 322 case _State.RESPONSE_LINE_ENDING:
330 _expect(byte, _CharCode.LF); 323 _expect(byte, _CharCode.LF);
331 _messageType == _MessageType.RESPONSE; 324 _messageType == _MessageType.RESPONSE;
332 int statusCode = parseInt(new String.fromCharCodes(_method_or_status _code)); 325 _statusCode = parseInt(new String.fromCharCodes(_method_or_status_c ode));
333 if (statusCode < 100 || statusCode > 599) { 326 if (_statusCode < 100 || _statusCode > 599) {
334 throw new HttpParserException("Invalid response status code"); 327 throw new HttpParserException("Invalid response status code");
335 } else { 328 } else {
336 // Check whether this response will never have a body. 329 // Check whether this response will never have a body.
337 _noMessageBody = 330 _noMessageBody =
338 statusCode <= 199 || statusCode == 204 || statusCode == 304; 331 _statusCode <= 199 || _statusCode == 204 || _statusCode == 304 ;
339 } 332 }
340 responseStart(statusCode,
341 new String.fromCharCodes(_uri_or_reason_phrase),
342 version);
343 _method_or_status_code.clear();
344 _uri_or_reason_phrase.clear();
345 _state = _State.HEADER_START; 333 _state = _State.HEADER_START;
346 break; 334 break;
347 335
348 case _State.HEADER_START: 336 case _State.HEADER_START:
349 if (byte == _CharCode.CR) { 337 if (byte == _CharCode.CR) {
350 _state = _State.HEADER_ENDING; 338 _state = _State.HEADER_ENDING;
351 } else { 339 } else {
352 // Start of new header field. 340 // Start of new header field.
353 _headerField.add(_toLowerCase(byte)); 341 _headerField.add(_toLowerCase(byte));
354 _state = _State.HEADER_FIELD; 342 _state = _State.HEADER_FIELD;
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
404 List<String> tokens = _tokenizeFieldValue(headerValue); 392 List<String> tokens = _tokenizeFieldValue(headerValue);
405 for (int i = 0; i < tokens.length; i++) { 393 for (int i = 0; i < tokens.length; i++) {
406 String token = tokens[i].toLowerCase(); 394 String token = tokens[i].toLowerCase();
407 if (token == "keep-alive") { 395 if (token == "keep-alive") {
408 _persistentConnection = true; 396 _persistentConnection = true;
409 } else if (token == "close") { 397 } else if (token == "close") {
410 _persistentConnection = false; 398 _persistentConnection = false;
411 } else if (token == "upgrade") { 399 } else if (token == "upgrade") {
412 _connectionUpgrade = true; 400 _connectionUpgrade = true;
413 } 401 }
414 headerReceived(headerField, token); 402 _headers.add(headerField, token);
403
415 } 404 }
416 reportHeader = false; 405 reportHeader = false;
417 } else if (headerField == "transfer-encoding" && 406 } else if (headerField == "transfer-encoding" &&
418 headerValue.toLowerCase() == "chunked") { 407 headerValue.toLowerCase() == "chunked") {
419 // Ignore the Content-Length header if Transfer-Encoding 408 // Ignore the Content-Length header if Transfer-Encoding
420 // is chunked (RFC 2616 section 4.4) 409 // is chunked (RFC 2616 section 4.4)
421 _chunked = true; 410 _chunked = true;
422 _contentLength = -1; 411 _contentLength = -1;
423 } 412 }
424 if (reportHeader) { 413 if (reportHeader) {
425 headerReceived(headerField, headerValue); 414 _headers.add(headerField, headerValue);
426 } 415 }
427 _headerField.clear(); 416 _headerField.clear();
428 _headerValue.clear(); 417 _headerValue.clear();
429 418
430 if (byte == _CharCode.CR) { 419 if (byte == _CharCode.CR) {
431 _state = _State.HEADER_ENDING; 420 _state = _State.HEADER_ENDING;
432 } else { 421 } else {
433 // Start of new header field. 422 // Start of new header field.
434 _headerField.add(_toLowerCase(byte)); 423 _headerField.add(_toLowerCase(byte));
435 _state = _State.HEADER_FIELD; 424 _state = _State.HEADER_FIELD;
436 } 425 }
437 } 426 }
438 break; 427 break;
439 428
440 case _State.HEADER_ENDING: 429 case _State.HEADER_ENDING:
441 _expect(byte, _CharCode.LF); 430 _expect(byte, _CharCode.LF);
442 // If a request message has neither Content-Length nor 431 // If a request message has neither Content-Length nor
443 // Transfer-Encoding the message must not have a body (RFC 432 // Transfer-Encoding the message must not have a body (RFC
444 // 2616 section 4.3). 433 // 2616 section 4.3).
445 if (_messageType == _MessageType.REQUEST && 434 if (_messageType == _MessageType.REQUEST &&
446 _contentLength < 0 && 435 _contentLength < 0 &&
447 _chunked == false) { 436 _chunked == false) {
448 _contentLength = 0; 437 _contentLength = 0;
449 } 438 }
450 if (_connectionUpgrade) { 439 if (_connectionUpgrade) {
451 _state = _State.UPGRADED; 440 _state = _State.UPGRADED;
452 headersComplete(); 441 }
442 if (_requestParser) {
443 requestStart(new String.fromCharCodes(_method_or_status_code),
444 new String.fromCharCodes(_uri_or_reason_phrase),
445 version,
446 _headers);
453 } else { 447 } else {
454 headersComplete(); 448 responseStart(_statusCode,
449 new String.fromCharCodes(_uri_or_reason_phrase),
450 version,
451 _headers);
452 }
453 _method_or_status_code.clear();
454 _uri_or_reason_phrase.clear();
455 if (!_connectionUpgrade) {
456 _method_or_status_code.clear();
457 _uri_or_reason_phrase.clear();
455 if (_chunked) { 458 if (_chunked) {
456 _state = _State.CHUNK_SIZE; 459 _state = _State.CHUNK_SIZE;
457 _remainingContent = 0; 460 _remainingContent = 0;
458 } else if (_contentLength == 0 || 461 } else if (_contentLength == 0 ||
459 (_messageType == _MessageType.RESPONSE && 462 (_messageType == _MessageType.RESPONSE &&
460 (_noMessageBody || _responseToMethod == "HEAD"))) { 463 (_noMessageBody || _responseToMethod == "HEAD"))) {
461 // If there is no message body get ready to process the 464 // If there is no message body get ready to process the
462 // next request. 465 // next request.
463 _bodyEnd(); 466 _bodyEnd();
464 _reset(); 467 _reset();
(...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after
665 668
666 _httpVersion = _HttpVersion.UNDETERMINED; 669 _httpVersion = _HttpVersion.UNDETERMINED;
667 _contentLength = -1; 670 _contentLength = -1;
668 _persistentConnection = false; 671 _persistentConnection = false;
669 _connectionUpgrade = false; 672 _connectionUpgrade = false;
670 _chunked = false; 673 _chunked = false;
671 674
672 _noMessageBody = false; 675 _noMessageBody = false;
673 _responseToMethod = null; 676 _responseToMethod = null;
674 _remainingContent = null; 677 _remainingContent = null;
678
679 _headers = new _HttpHeaders();
675 } 680 }
676 681
677 _releaseBuffer() { 682 _releaseBuffer() {
678 _buffer = null; 683 _buffer = null;
679 _index = null; 684 _index = null;
680 _lastIndex = null; 685 _lastIndex = null;
681 } 686 }
682 687
683 bool _isTokenChar(int byte) { 688 bool _isTokenChar(int byte) {
684 return byte > 31 && byte < 128 && _Const.SEPARATORS.indexOf(byte) == -1; 689 return byte > 31 && byte < 128 && _Const.SEPARATORS.indexOf(byte) == -1;
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
728 733
729 // The data that is currently being parsed. 734 // The data that is currently being parsed.
730 List<int> _buffer; 735 List<int> _buffer;
731 int _index; 736 int _index;
732 int _lastIndex; 737 int _lastIndex;
733 738
734 bool _requestParser; 739 bool _requestParser;
735 int _state; 740 int _state;
736 int _httpVersionIndex; 741 int _httpVersionIndex;
737 int _messageType; 742 int _messageType;
743 int _statusCode;
738 List _method_or_status_code; 744 List _method_or_status_code;
739 List _uri_or_reason_phrase; 745 List _uri_or_reason_phrase;
740 List _headerField; 746 List _headerField;
741 List _headerValue; 747 List _headerValue;
742 748
743 int _httpVersion; 749 int _httpVersion;
744 int _contentLength; 750 int _contentLength;
745 bool _persistentConnection; 751 bool _persistentConnection;
746 bool _connectionUpgrade; 752 bool _connectionUpgrade;
747 bool _chunked; 753 bool _chunked;
748 754
749 bool _noMessageBody; 755 bool _noMessageBody;
750 String _responseToMethod; // Indicates the method used for the request. 756 String _responseToMethod; // Indicates the method used for the request.
751 int _remainingContent; 757 int _remainingContent;
752 758
759 _HttpHeaders _headers = new _HttpHeaders();
760
753 // Callbacks. 761 // Callbacks.
754 Function requestStart; 762 Function requestStart;
755 Function responseStart; 763 Function responseStart;
756 Function headerReceived;
757 Function headersComplete;
758 Function dataReceived; 764 Function dataReceived;
759 Function dataEnd; 765 Function dataEnd;
760 Function error; 766 Function error;
761 Function closed; 767 Function closed;
762 } 768 }
763 769
764 770
765 class HttpParserException implements Exception { 771 class HttpParserException implements Exception {
766 const HttpParserException([String this.message = ""]); 772 const HttpParserException([String this.message = ""]);
767 String toString() => "HttpParserException: $message"; 773 String toString() => "HttpParserException: $message";
768 final String message; 774 final String message;
769 } 775 }
OLDNEW
« no previous file with comments | « sdk/lib/io/http_impl.dart ('k') | sdk/lib/io/iolib_sources.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698