| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 } |
| OLD | NEW |