| 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 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 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:] | 102 * [:headerReceived:] |
| 103 * [:headersComplete:] | 103 * [:headersComplete:] |
| 104 * [:dataReceived:] | 104 * [:dataReceived:] |
| 105 * [:dataEnd:] | 105 * [:dataEnd:] |
| 106 * [:closed:] |
| 106 * [:error:] | 107 * [:error:] |
| 107 * | 108 * |
| 108 * If an HTTP parser error occours it is possible to get an exception | 109 * If an HTTP parser error occours it is possible to get an exception |
| 109 * thrown from the [:writeList:] and [:connectionClosed:] methods if | 110 * thrown from the [:writeList:] and [:connectionClosed:] methods if |
| 110 * the error callback is not set. | 111 * the error callback is not set. |
| 111 * | 112 * |
| 112 * The connection upgrades (e.g. switching from HTTP/1.1 to the | 113 * The connection upgrades (e.g. switching from HTTP/1.1 to the |
| 113 * WebSocket protocol) is handled in a special way. If connection | 114 * WebSocket protocol) is handled in a special way. If connection |
| 114 * upgrade is specified in the headers, then on the callback to | 115 * upgrade is specified in the headers, then on the callback to |
| 115 * [:headersComplete:] the [:upgrade:] property on the [:HttpParser:] | 116 * [:headersComplete:] the [:upgrade:] property on the [:HttpParser:] |
| 116 * object will be [:true:] indicating that from now on the protocol is | 117 * 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 * not HTTP anymore and no more callbacks will happen, that is |
| 118 * [:dataReceived:] and [:dataEnd:] are not called in this case as | 119 * [:dataReceived:] and [:dataEnd:] are not called in this case as |
| 119 * there is no more HTTP data. After the upgrade the method | 120 * there is no more HTTP data. After the upgrade the method |
| 120 * [:readUnparsedData:] can be used to read any remaining bytes in the | 121 * [:readUnparsedData:] can be used to read any remaining bytes in the |
| 121 * HTTP parser which are part of the protocol the connection is | 122 * HTTP parser which are part of the protocol the connection is |
| 122 * upgrading to. These bytes cannot be processed by the HTTP parser | 123 * upgrading to. These bytes cannot be processed by the HTTP parser |
| 123 * and should be handled according to whatever protocol is being | 124 * and should be handled according to whatever protocol is being |
| 124 * upgraded to. | 125 * upgraded to. |
| 125 */ | 126 */ |
| 126 class _HttpParser { | 127 class _HttpParser { |
| 127 _HttpParser() { | 128 _HttpParser.requestParser() { |
| 129 _requestParser = true; |
| 130 _reset(); |
| 131 } |
| 132 _HttpParser.responseParser() { |
| 133 _requestParser = false; |
| 128 _reset(); | 134 _reset(); |
| 129 } | 135 } |
| 130 | 136 |
| 131 // From RFC 2616. | 137 // From RFC 2616. |
| 132 // generic-message = start-line | 138 // generic-message = start-line |
| 133 // *(message-header CRLF) | 139 // *(message-header CRLF) |
| 134 // CRLF | 140 // CRLF |
| 135 // [ message-body ] | 141 // [ message-body ] |
| 136 // start-line = Request-Line | Status-Line | 142 // start-line = Request-Line | Status-Line |
| 137 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF | 143 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF |
| (...skipping 20 matching lines...) Expand all Loading... |
| 158 if (byte == _Const.HTTP[0]) { | 164 if (byte == _Const.HTTP[0]) { |
| 159 // Start parsing method or HTTP version. | 165 // Start parsing method or HTTP version. |
| 160 _httpVersionIndex = 1; | 166 _httpVersionIndex = 1; |
| 161 _state = _State.METHOD_OR_RESPONSE_HTTP_VERSION; | 167 _state = _State.METHOD_OR_RESPONSE_HTTP_VERSION; |
| 162 } else { | 168 } else { |
| 163 // Start parsing method. | 169 // Start parsing method. |
| 164 if (!_isTokenChar(byte)) { | 170 if (!_isTokenChar(byte)) { |
| 165 throw new HttpParserException("Invalid request method"); | 171 throw new HttpParserException("Invalid request method"); |
| 166 } | 172 } |
| 167 _method_or_status_code.addCharCode(byte); | 173 _method_or_status_code.addCharCode(byte); |
| 174 if (!_requestParser) { |
| 175 throw new HttpParserException("Invalid response line"); |
| 176 } |
| 168 _state = _State.REQUEST_LINE_METHOD; | 177 _state = _State.REQUEST_LINE_METHOD; |
| 169 } | 178 } |
| 170 break; | 179 break; |
| 171 | 180 |
| 172 case _State.METHOD_OR_RESPONSE_HTTP_VERSION: | 181 case _State.METHOD_OR_RESPONSE_HTTP_VERSION: |
| 173 if (_httpVersionIndex < _Const.HTTP.length && | 182 if (_httpVersionIndex < _Const.HTTP.length && |
| 174 byte == _Const.HTTP[_httpVersionIndex]) { | 183 byte == _Const.HTTP[_httpVersionIndex]) { |
| 175 // Continue parsing HTTP version. | 184 // Continue parsing HTTP version. |
| 176 _httpVersionIndex++; | 185 _httpVersionIndex++; |
| 177 } else if (_httpVersionIndex == _Const.HTTP.length && | 186 } else if (_httpVersionIndex == _Const.HTTP.length && |
| 178 byte == _CharCode.SLASH) { | 187 byte == _CharCode.SLASH) { |
| 179 // HTTP/ parsed. As method is a token this cannot be a | 188 // HTTP/ parsed. As method is a token this cannot be a |
| 180 // method anymore. | 189 // method anymore. |
| 181 _httpVersionIndex++; | 190 _httpVersionIndex++; |
| 191 if (_requestParser) { |
| 192 throw new HttpParserException("Invalid request line"); |
| 193 } |
| 182 _state = _State.RESPONSE_HTTP_VERSION; | 194 _state = _State.RESPONSE_HTTP_VERSION; |
| 183 } else { | 195 } else { |
| 184 // Did not parse HTTP version. Expect method instead. | 196 // Did not parse HTTP version. Expect method instead. |
| 185 for (int i = 0; i < _httpVersionIndex; i++) { | 197 for (int i = 0; i < _httpVersionIndex; i++) { |
| 186 _method_or_status_code.addCharCode(_Const.HTTP[i]); | 198 _method_or_status_code.addCharCode(_Const.HTTP[i]); |
| 187 } | 199 } |
| 188 if (byte == _CharCode.SP) { | 200 if (byte == _CharCode.SP) { |
| 189 _state = _State.REQUEST_LINE_URI; | 201 _state = _State.REQUEST_LINE_URI; |
| 190 } else { | 202 } else { |
| 191 _method_or_status_code.addCharCode(byte); | 203 _method_or_status_code.addCharCode(byte); |
| 192 _httpVersion = _HttpVersion.UNDETERMINED; | 204 _httpVersion = _HttpVersion.UNDETERMINED; |
| 205 if (!_requestParser) { |
| 206 throw new HttpParserException("Invalid response line"); |
| 207 } |
| 193 _state = _State.REQUEST_LINE_METHOD; | 208 _state = _State.REQUEST_LINE_METHOD; |
| 194 } | 209 } |
| 195 } | 210 } |
| 196 break; | 211 break; |
| 197 | 212 |
| 198 case _State.RESPONSE_HTTP_VERSION: | 213 case _State.RESPONSE_HTTP_VERSION: |
| 199 if (_httpVersionIndex < _Const.HTTP1DOT.length) { | 214 if (_httpVersionIndex < _Const.HTTP1DOT.length) { |
| 200 // Continue parsing HTTP version. | 215 // Continue parsing HTTP version. |
| 201 _expect(byte, _Const.HTTP1DOT[_httpVersionIndex]); | 216 _expect(byte, _Const.HTTP1DOT[_httpVersionIndex]); |
| 202 _httpVersionIndex++; | 217 _httpVersionIndex++; |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 267 } | 282 } |
| 268 } else { | 283 } else { |
| 269 _expect(byte, _CharCode.CR); | 284 _expect(byte, _CharCode.CR); |
| 270 _state = _State.REQUEST_LINE_ENDING; | 285 _state = _State.REQUEST_LINE_ENDING; |
| 271 } | 286 } |
| 272 break; | 287 break; |
| 273 | 288 |
| 274 case _State.REQUEST_LINE_ENDING: | 289 case _State.REQUEST_LINE_ENDING: |
| 275 _expect(byte, _CharCode.LF); | 290 _expect(byte, _CharCode.LF); |
| 276 _messageType = _MessageType.REQUEST; | 291 _messageType = _MessageType.REQUEST; |
| 277 if (requestStart != null) { | 292 requestStart(_method_or_status_code.toString(), |
| 278 requestStart(_method_or_status_code.toString(), | 293 _uri_or_reason_phrase.toString(), |
| 279 _uri_or_reason_phrase.toString(), | 294 version); |
| 280 version); | |
| 281 } | |
| 282 _method_or_status_code.clear(); | 295 _method_or_status_code.clear(); |
| 283 _uri_or_reason_phrase.clear(); | 296 _uri_or_reason_phrase.clear(); |
| 284 _state = _State.HEADER_START; | 297 _state = _State.HEADER_START; |
| 285 break; | 298 break; |
| 286 | 299 |
| 287 case _State.RESPONSE_LINE_STATUS_CODE: | 300 case _State.RESPONSE_LINE_STATUS_CODE: |
| 288 if (byte == _CharCode.SP) { | 301 if (byte == _CharCode.SP) { |
| 289 if (_method_or_status_code.length != 3) { | 302 if (_method_or_status_code.length != 3) { |
| 290 throw new HttpParserException("Invalid response status code"); | 303 throw new HttpParserException("Invalid response status code"); |
| 291 } | 304 } |
| (...skipping 25 matching lines...) Expand all Loading... |
| 317 _expect(byte, _CharCode.LF); | 330 _expect(byte, _CharCode.LF); |
| 318 _messageType == _MessageType.RESPONSE; | 331 _messageType == _MessageType.RESPONSE; |
| 319 int statusCode = parseInt(_method_or_status_code.toString()); | 332 int statusCode = parseInt(_method_or_status_code.toString()); |
| 320 if (statusCode < 100 || statusCode > 599) { | 333 if (statusCode < 100 || statusCode > 599) { |
| 321 throw new HttpParserException("Invalid response status code"); | 334 throw new HttpParserException("Invalid response status code"); |
| 322 } else { | 335 } else { |
| 323 // Check whether this response will never have a body. | 336 // Check whether this response will never have a body. |
| 324 _noMessageBody = | 337 _noMessageBody = |
| 325 statusCode <= 199 || statusCode == 204 || statusCode == 304; | 338 statusCode <= 199 || statusCode == 204 || statusCode == 304; |
| 326 } | 339 } |
| 327 if (responseStart != null) { | 340 responseStart(statusCode, |
| 328 responseStart(statusCode, | 341 _uri_or_reason_phrase.toString(), |
| 329 _uri_or_reason_phrase.toString(), | 342 version); |
| 330 version); | |
| 331 } | |
| 332 _method_or_status_code.clear(); | 343 _method_or_status_code.clear(); |
| 333 _uri_or_reason_phrase.clear(); | 344 _uri_or_reason_phrase.clear(); |
| 334 _state = _State.HEADER_START; | 345 _state = _State.HEADER_START; |
| 335 break; | 346 break; |
| 336 | 347 |
| 337 case _State.HEADER_START: | 348 case _State.HEADER_START: |
| 338 if (byte == _CharCode.CR) { | 349 if (byte == _CharCode.CR) { |
| 339 _state = _State.HEADER_ENDING; | 350 _state = _State.HEADER_ENDING; |
| 340 } else { | 351 } else { |
| 341 // Start of new header field. | 352 // Start of new header field. |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 393 List<String> tokens = _tokenizeFieldValue(headerValue); | 404 List<String> tokens = _tokenizeFieldValue(headerValue); |
| 394 for (int i = 0; i < tokens.length; i++) { | 405 for (int i = 0; i < tokens.length; i++) { |
| 395 String token = tokens[i].toLowerCase(); | 406 String token = tokens[i].toLowerCase(); |
| 396 if (token == "keep-alive") { | 407 if (token == "keep-alive") { |
| 397 _persistentConnection = true; | 408 _persistentConnection = true; |
| 398 } else if (token == "close") { | 409 } else if (token == "close") { |
| 399 _persistentConnection = false; | 410 _persistentConnection = false; |
| 400 } else if (token == "upgrade") { | 411 } else if (token == "upgrade") { |
| 401 _connectionUpgrade = true; | 412 _connectionUpgrade = true; |
| 402 } | 413 } |
| 403 if (headerReceived != null) { | 414 headerReceived(headerField, token); |
| 404 headerReceived(headerField, token); | |
| 405 } | |
| 406 } | 415 } |
| 407 reportHeader = false; | 416 reportHeader = false; |
| 408 } else if (headerField == "transfer-encoding" && | 417 } else if (headerField == "transfer-encoding" && |
| 409 headerValue.toLowerCase() == "chunked") { | 418 headerValue.toLowerCase() == "chunked") { |
| 410 // Ignore the Content-Length header if Transfer-Encoding | 419 // Ignore the Content-Length header if Transfer-Encoding |
| 411 // is chunked (RFC 2616 section 4.4) | 420 // is chunked (RFC 2616 section 4.4) |
| 412 _chunked = true; | 421 _chunked = true; |
| 413 _contentLength = -1; | 422 _contentLength = -1; |
| 414 } | 423 } |
| 415 if (reportHeader && headerReceived != null) { | 424 if (reportHeader) { |
| 416 headerReceived(headerField, headerValue); | 425 headerReceived(headerField, headerValue); |
| 417 } | 426 } |
| 418 _headerField.clear(); | 427 _headerField.clear(); |
| 419 _headerValue.clear(); | 428 _headerValue.clear(); |
| 420 | 429 |
| 421 if (byte == _CharCode.CR) { | 430 if (byte == _CharCode.CR) { |
| 422 _state = _State.HEADER_ENDING; | 431 _state = _State.HEADER_ENDING; |
| 423 } else { | 432 } else { |
| 424 // Start of new header field. | 433 // Start of new header field. |
| 425 _headerField.addCharCode(_toLowerCase(byte)); | 434 _headerField.addCharCode(_toLowerCase(byte)); |
| 426 _state = _State.HEADER_FIELD; | 435 _state = _State.HEADER_FIELD; |
| 427 } | 436 } |
| 428 } | 437 } |
| 429 break; | 438 break; |
| 430 | 439 |
| 431 case _State.HEADER_ENDING: | 440 case _State.HEADER_ENDING: |
| 432 _expect(byte, _CharCode.LF); | 441 _expect(byte, _CharCode.LF); |
| 433 // If a request message has neither Content-Length nor | 442 // If a request message has neither Content-Length nor |
| 434 // Transfer-Encoding the message must not have a body (RFC | 443 // Transfer-Encoding the message must not have a body (RFC |
| 435 // 2616 section 4.3). | 444 // 2616 section 4.3). |
| 436 if (_messageType == _MessageType.REQUEST && | 445 if (_messageType == _MessageType.REQUEST && |
| 437 _contentLength < 0 && | 446 _contentLength < 0 && |
| 438 _chunked == false) { | 447 _chunked == false) { |
| 439 _contentLength = 0; | 448 _contentLength = 0; |
| 440 } | 449 } |
| 441 if (_connectionUpgrade) { | 450 if (_connectionUpgrade) { |
| 442 _state = _State.UPGRADED; | 451 _state = _State.UPGRADED; |
| 443 if (headersComplete != null) headersComplete(); | 452 headersComplete(); |
| 444 } else { | 453 } else { |
| 445 if (headersComplete != null) headersComplete(); | 454 headersComplete(); |
| 446 if (_chunked) { | 455 if (_chunked) { |
| 447 _state = _State.CHUNK_SIZE; | 456 _state = _State.CHUNK_SIZE; |
| 448 _remainingContent = 0; | 457 _remainingContent = 0; |
| 449 } else if (_contentLength == 0 || | 458 } else if (_contentLength == 0 || |
| 450 (_messageType == _MessageType.RESPONSE && | 459 (_messageType == _MessageType.RESPONSE && |
| 451 (_noMessageBody || _responseToMethod == "HEAD"))) { | 460 (_noMessageBody || _responseToMethod == "HEAD"))) { |
| 452 // If there is no message body get ready to process the | 461 // If there is no message body get ready to process the |
| 453 // next request. | 462 // next request. |
| 454 _bodyEnd(); | 463 _bodyEnd(); |
| 455 _reset(); | 464 _reset(); |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 518 List<int> data; | 527 List<int> data; |
| 519 if (_remainingContent == null || | 528 if (_remainingContent == null || |
| 520 dataAvailable <= _remainingContent) { | 529 dataAvailable <= _remainingContent) { |
| 521 data = new Uint8List(dataAvailable); | 530 data = new Uint8List(dataAvailable); |
| 522 data.setRange(0, dataAvailable, _buffer, _index); | 531 data.setRange(0, dataAvailable, _buffer, _index); |
| 523 } else { | 532 } else { |
| 524 data = new Uint8List(_remainingContent); | 533 data = new Uint8List(_remainingContent); |
| 525 data.setRange(0, _remainingContent, _buffer, _index); | 534 data.setRange(0, _remainingContent, _buffer, _index); |
| 526 } | 535 } |
| 527 | 536 |
| 528 if (dataReceived != null) dataReceived(data); | 537 dataReceived(data); |
| 529 if (_remainingContent != null) { | 538 if (_remainingContent != null) { |
| 530 _remainingContent -= data.length; | 539 _remainingContent -= data.length; |
| 531 } | 540 } |
| 532 _index += data.length; | 541 _index += data.length; |
| 533 if (_remainingContent == 0) { | 542 if (_remainingContent == 0) { |
| 534 if (!_chunked) { | 543 if (!_chunked) { |
| 535 _bodyEnd(); | 544 _bodyEnd(); |
| 536 _reset(); | 545 _reset(); |
| 537 } else { | 546 } else { |
| 538 _state = _State.CHUNK_SIZE_STARTING_CR; | 547 _state = _State.CHUNK_SIZE_STARTING_CR; |
| 539 } | 548 } |
| 540 } | 549 } |
| 541 break; | 550 break; |
| 542 | 551 |
| 543 case _State.FAILURE: | 552 case _State.FAILURE: |
| 544 // Should be unreachable. | 553 // Should be unreachable. |
| 545 assert(false); | 554 assert(false); |
| 546 break; | 555 break; |
| 547 | 556 |
| 548 default: | 557 default: |
| 549 // Should be unreachable. | 558 // Should be unreachable. |
| 550 assert(false); | 559 assert(false); |
| 551 break; | 560 break; |
| 552 } | 561 } |
| 553 } | 562 } |
| 554 } catch (e) { | 563 } catch (e) { |
| 555 // Report the error through the error callback if any. Otherwise | 564 _state = _State.FAILURE; |
| 556 // throw the error. | 565 error(e); |
| 557 if (error != null) { | |
| 558 error(e); | |
| 559 _state = _State.FAILURE; | |
| 560 } else { | |
| 561 throw e; | |
| 562 } | |
| 563 } | 566 } |
| 564 | 567 |
| 565 // If all data is parsed or not needed due to failure there is no | 568 // If all data is parsed or not needed due to failure there is no |
| 566 // need to hold on to the buffer. | 569 // need to hold on to the buffer. |
| 567 if (_state != _State.UPGRADED) _releaseBuffer(); | 570 if (_state != _State.UPGRADED) _releaseBuffer(); |
| 568 } | 571 } |
| 569 | 572 |
| 570 void writeList(List<int> buffer, int offset, int count) { | 573 void streamData(List<int> buffer) { |
| 571 assert(_buffer == null); | 574 assert(_buffer == null); |
| 572 _buffer = buffer; | 575 _buffer = buffer; |
| 573 _index = offset; | 576 _index = 0; |
| 574 _lastIndex = offset + count; | 577 _lastIndex = buffer.length; |
| 575 _parse(); | 578 _parse(); |
| 576 } | 579 } |
| 577 | 580 |
| 578 void connectionClosed() { | 581 void streamDone() { |
| 582 // If the connection is idle the HTTP stream is closed. |
| 583 if (_state == _State.START) { |
| 584 if (_requestParser) { |
| 585 closed(); |
| 586 } else { |
| 587 error( |
| 588 new HttpParserException( |
| 589 "Connection closed before full header was received")); |
| 590 } |
| 591 return; |
| 592 } |
| 593 |
| 579 if (_state < _State.FIRST_BODY_STATE) { | 594 if (_state < _State.FIRST_BODY_STATE) { |
| 580 _state = _State.FAILURE; | 595 _state = _State.FAILURE; |
| 581 // Report the error through the error callback if any. Otherwise | 596 // Report the error through the error callback if any. Otherwise |
| 582 // throw the error. | 597 // throw the error. |
| 583 var e = new HttpParserException( | 598 error( |
| 584 "Connection closed before full header was received"); | 599 new HttpParserException( |
| 585 if (error != null) { | 600 "Connection closed before full header was received")); |
| 586 error(e); | 601 return; |
| 587 return; | |
| 588 } | |
| 589 throw e; | |
| 590 } | 602 } |
| 591 | 603 |
| 592 if (!_chunked && _contentLength == -1) { | 604 if (!_chunked && _contentLength == -1) { |
| 593 if (_state != _State.START) { | 605 dataEnd(true); |
| 594 if (dataEnd != null) dataEnd(true); | |
| 595 } | |
| 596 _state = _State.CLOSED; | 606 _state = _State.CLOSED; |
| 607 closed(); |
| 597 } else { | 608 } else { |
| 598 _state = _State.FAILURE; | 609 _state = _State.FAILURE; |
| 599 // Report the error through the error callback if any. Otherwise | 610 // Report the error through the error callback if any. Otherwise |
| 600 // throw the error. | 611 // throw the error. |
| 601 var e = new HttpParserException( | 612 error( |
| 602 "Connection closed before full body was received"); | 613 new HttpParserException( |
| 603 if (error != null) { | 614 "Connection closed before full body was received")); |
| 604 error(e); | |
| 605 return; | |
| 606 } | |
| 607 throw e; | |
| 608 } | 615 } |
| 609 } | 616 } |
| 610 | 617 |
| 618 void streamError(e) { |
| 619 // Don't report errors when HTTP parser is in idle state. Clients |
| 620 // can close the connection and cause a connection reset by peer |
| 621 // error which is OK. |
| 622 if (_state == _State.START) { |
| 623 closed(); |
| 624 return; |
| 625 } |
| 626 error(e); |
| 627 } |
| 628 |
| 611 String get version { | 629 String get version { |
| 612 switch (_httpVersion) { | 630 switch (_httpVersion) { |
| 613 case _HttpVersion.HTTP10: | 631 case _HttpVersion.HTTP10: |
| 614 return "1.0"; | 632 return "1.0"; |
| 615 case _HttpVersion.HTTP11: | 633 case _HttpVersion.HTTP11: |
| 616 return "1.1"; | 634 return "1.1"; |
| 617 } | 635 } |
| 618 return null; | 636 return null; |
| 619 } | 637 } |
| 620 | 638 |
| 621 int get messageType => _messageType; | 639 int get messageType => _messageType; |
| 622 int get contentLength => _contentLength; | 640 int get contentLength => _contentLength; |
| 623 bool get upgrade => _connectionUpgrade && _state == _State.UPGRADED; | 641 bool get upgrade => _connectionUpgrade && _state == _State.UPGRADED; |
| 624 bool get persistentConnection => _persistentConnection; | 642 bool get persistentConnection => _persistentConnection; |
| 625 | 643 |
| 626 void set responseToMethod(String method) { _responseToMethod = method; } | 644 void set responseToMethod(String method) { _responseToMethod = method; } |
| 627 | 645 |
| 628 bool get isIdle => _state == _State.START; | |
| 629 | |
| 630 List<int> readUnparsedData() { | 646 List<int> readUnparsedData() { |
| 631 if (_buffer == null) return []; | 647 if (_buffer == null) return []; |
| 632 if (_index == _lastIndex) return []; | 648 if (_index == _lastIndex) return []; |
| 633 var result = _buffer.getRange(_index, _lastIndex - _index); | 649 var result = _buffer.getRange(_index, _lastIndex - _index); |
| 634 _releaseBuffer(); | 650 _releaseBuffer(); |
| 635 return result; | 651 return result; |
| 636 } | 652 } |
| 637 | 653 |
| 638 void _bodyEnd() { | 654 void _bodyEnd() { |
| 639 if (dataEnd != null) { | 655 dataEnd(_messageType == _MessageType.RESPONSE && !_persistentConnection); |
| 640 dataEnd(_messageType == _MessageType.RESPONSE && !_persistentConnection); | |
| 641 } | |
| 642 } | 656 } |
| 643 | 657 |
| 644 _reset() { | 658 _reset() { |
| 645 _state = _State.START; | 659 _state = _State.START; |
| 646 _messageType = _MessageType.UNDETERMINED; | 660 _messageType = _MessageType.UNDETERMINED; |
| 647 _headerField = new StringBuffer(); | 661 _headerField = new StringBuffer(); |
| 648 _headerValue = new StringBuffer(); | 662 _headerValue = new StringBuffer(); |
| 649 _method_or_status_code = new StringBuffer(); | 663 _method_or_status_code = new StringBuffer(); |
| 650 _uri_or_reason_phrase = new StringBuffer(); | 664 _uri_or_reason_phrase = new StringBuffer(); |
| 651 | 665 |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 710 } else { | 724 } else { |
| 711 throw new HttpParserException("Failed to parse HTTP"); | 725 throw new HttpParserException("Failed to parse HTTP"); |
| 712 } | 726 } |
| 713 } | 727 } |
| 714 | 728 |
| 715 // The data that is currently being parsed. | 729 // The data that is currently being parsed. |
| 716 List<int> _buffer; | 730 List<int> _buffer; |
| 717 int _index; | 731 int _index; |
| 718 int _lastIndex; | 732 int _lastIndex; |
| 719 | 733 |
| 734 bool _requestParser; |
| 720 int _state; | 735 int _state; |
| 721 int _httpVersionIndex; | 736 int _httpVersionIndex; |
| 722 int _messageType; | 737 int _messageType; |
| 723 StringBuffer _method_or_status_code; | 738 StringBuffer _method_or_status_code; |
| 724 StringBuffer _uri_or_reason_phrase; | 739 StringBuffer _uri_or_reason_phrase; |
| 725 StringBuffer _headerField; | 740 StringBuffer _headerField; |
| 726 StringBuffer _headerValue; | 741 StringBuffer _headerValue; |
| 727 | 742 |
| 728 int _httpVersion; | 743 int _httpVersion; |
| 729 int _contentLength; | 744 int _contentLength; |
| 730 bool _persistentConnection; | 745 bool _persistentConnection; |
| 731 bool _connectionUpgrade; | 746 bool _connectionUpgrade; |
| 732 bool _chunked; | 747 bool _chunked; |
| 733 | 748 |
| 734 bool _noMessageBody; | 749 bool _noMessageBody; |
| 735 String _responseToMethod; // Indicates the method used for the request. | 750 String _responseToMethod; // Indicates the method used for the request. |
| 736 int _remainingContent; | 751 int _remainingContent; |
| 737 | 752 |
| 738 // Callbacks. | 753 // Callbacks. |
| 739 Function requestStart; | 754 Function requestStart; |
| 740 Function responseStart; | 755 Function responseStart; |
| 741 Function headerReceived; | 756 Function headerReceived; |
| 742 Function headersComplete; | 757 Function headersComplete; |
| 743 Function dataReceived; | 758 Function dataReceived; |
| 744 Function dataEnd; | 759 Function dataEnd; |
| 745 Function error; | 760 Function error; |
| 761 Function closed; |
| 746 } | 762 } |
| 747 | 763 |
| 748 | 764 |
| 749 class HttpParserException implements Exception { | 765 class HttpParserException implements Exception { |
| 750 const HttpParserException([String this.message = ""]); | 766 const HttpParserException([String this.message = ""]); |
| 751 String toString() => "HttpParserException: $message"; | 767 String toString() => "HttpParserException: $message"; |
| 752 final String message; | 768 final String message; |
| 753 } | 769 } |
| OLD | NEW |