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 |