OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 part of dart.io; | 5 part of dart.io; |
6 | 6 |
| 7 const int _HEADERS_BUFFER_SIZE = 8 * 1024; |
| 8 |
7 class _HttpIncoming extends Stream<List<int>> { | 9 class _HttpIncoming extends Stream<List<int>> { |
8 final int _transferLength; | 10 final int _transferLength; |
9 final Completer _dataCompleter = new Completer(); | 11 final Completer _dataCompleter = new Completer(); |
10 Stream<List<int>> _stream; | 12 Stream<List<int>> _stream; |
11 | 13 |
12 bool fullBodyRead = false; | 14 bool fullBodyRead = false; |
13 | 15 |
14 // Common properties. | 16 // Common properties. |
15 final _HttpHeaders headers; | 17 final _HttpHeaders headers; |
16 bool upgraded = false; | 18 bool upgraded = false; |
(...skipping 455 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
472 return _dataSink.flush(); | 474 return _dataSink.flush(); |
473 } | 475 } |
474 | 476 |
475 Future close() { | 477 Future close() { |
476 return _dataSink.close(); | 478 return _dataSink.close(); |
477 } | 479 } |
478 | 480 |
479 Future<T> get done => _dataSink.done; | 481 Future<T> get done => _dataSink.done; |
480 | 482 |
481 Future _writeHeaders({bool drainRequest: true}) { | 483 Future _writeHeaders({bool drainRequest: true}) { |
| 484 void write() { |
| 485 try { |
| 486 _writeHeader(); |
| 487 } catch (error) { |
| 488 // Headers too large. |
| 489 throw new HttpException( |
| 490 "Headers size exceeded the of '$_HEADERS_BUFFER_SIZE' bytes"); |
| 491 } |
| 492 } |
482 if (_headersWritten) return new Future.value(); | 493 if (_headersWritten) return new Future.value(); |
483 _headersWritten = true; | 494 _headersWritten = true; |
484 headers._synchronize(); // Be sure the 'chunked' option is updated. | 495 headers._synchronize(); // Be sure the 'chunked' option is updated. |
485 _dataSink.encoding = encoding; | 496 _dataSink.encoding = encoding; |
486 bool isServerSide = this is _HttpResponse; | 497 bool isServerSide = this is _HttpResponse; |
487 if (isServerSide) { | 498 if (isServerSide) { |
488 var response = this; | 499 var response = this; |
489 if (headers.chunkedTransferEncoding) { | 500 if (headers.chunkedTransferEncoding) { |
490 List acceptEncodings = | 501 List acceptEncodings = |
491 response._httpRequest.headers[HttpHeaders.ACCEPT_ENCODING]; | 502 response._httpRequest.headers[HttpHeaders.ACCEPT_ENCODING]; |
492 List contentEncoding = headers[HttpHeaders.CONTENT_ENCODING]; | 503 List contentEncoding = headers[HttpHeaders.CONTENT_ENCODING]; |
493 if (acceptEncodings != null && | 504 if (acceptEncodings != null && |
494 acceptEncodings | 505 acceptEncodings |
495 .expand((list) => list.split(",")) | 506 .expand((list) => list.split(",")) |
496 .any((encoding) => encoding.trim().toLowerCase() == "gzip") && | 507 .any((encoding) => encoding.trim().toLowerCase() == "gzip") && |
497 contentEncoding == null) { | 508 contentEncoding == null) { |
498 headers.set(HttpHeaders.CONTENT_ENCODING, "gzip"); | 509 headers.set(HttpHeaders.CONTENT_ENCODING, "gzip"); |
499 _asGZip = true; | 510 _asGZip = true; |
500 } | 511 } |
501 } | 512 } |
502 if (drainRequest && !response._httpRequest._incoming.hasSubscriber) { | 513 if (drainRequest && !response._httpRequest._incoming.hasSubscriber) { |
503 return response._httpRequest.drain() | 514 return response._httpRequest.drain() |
504 // TODO(ajohnsen): Timeout on drain? | 515 // TODO(ajohnsen): Timeout on drain? |
505 .catchError((_) {}) // Ignore errors. | 516 .catchError((_) {}) // Ignore errors. |
506 .then((_) => _writeHeader()); | 517 .then((_) => write()); |
507 } | 518 } |
508 } | 519 } |
509 return new Future.sync(_writeHeader); | 520 return new Future.sync(write); |
510 } | 521 } |
511 | 522 |
512 Future _addStream(Stream<List<int>> stream) { | 523 Future _addStream(Stream<List<int>> stream) { |
513 return _writeHeaders() | 524 return _writeHeaders() |
514 .then((_) { | 525 .then((_) { |
515 int contentLength = headers.contentLength; | 526 int contentLength = headers.contentLength; |
516 if (_ignoreBody) { | 527 if (_ignoreBody) { |
517 stream.drain().catchError((_) {}); | 528 stream.drain().catchError((_) {}); |
518 return _headersSink.close(); | 529 return _headersSink.close(); |
519 } | 530 } |
(...skipping 22 matching lines...) Expand all Loading... |
542 headers.chunkedTransferEncoding = false; | 553 headers.chunkedTransferEncoding = false; |
543 headers.contentLength = 0; | 554 headers.contentLength = 0; |
544 } else if (!_ignoreBody && headers.contentLength > 0) { | 555 } else if (!_ignoreBody && headers.contentLength > 0) { |
545 _headersSink.addError(new HttpException( | 556 _headersSink.addError(new HttpException( |
546 "No content while contentLength was specified to be greater " | 557 "No content while contentLength was specified to be greater " |
547 "than 0: ${headers.contentLength}.", | 558 "than 0: ${headers.contentLength}.", |
548 uri: _uri)); | 559 uri: _uri)); |
549 return _headersSink.done; | 560 return _headersSink.done; |
550 } | 561 } |
551 } | 562 } |
552 return _writeHeaders().then((_) => _headersSink.close()); | 563 return _writeHeaders().whenComplete(_headersSink.close); |
553 } | 564 } |
554 | 565 |
555 void _writeHeader(); // TODO(ajohnsen): Better name. | 566 void _writeHeader(); |
556 } | 567 } |
557 | 568 |
558 | 569 |
559 class _HttpOutboundConsumer implements StreamConsumer { | 570 class _HttpOutboundConsumer implements StreamConsumer { |
560 final _HttpOutboundMessage _outbound; | 571 final _HttpOutboundMessage _outbound; |
561 StreamController _controller; | 572 StreamController _controller; |
562 StreamSubscription _subscription; | 573 StreamSubscription _subscription; |
563 Completer _closeCompleter = new Completer(); | 574 Completer _closeCompleter = new Completer(); |
564 Completer _completer; | 575 Completer _completer; |
565 bool _socketError = false; | 576 bool _socketError = false; |
(...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
762 if (_deadlineTimer != null) _deadlineTimer.cancel(); | 773 if (_deadlineTimer != null) _deadlineTimer.cancel(); |
763 _deadline = d; | 774 _deadline = d; |
764 | 775 |
765 if (_deadline == null) return; | 776 if (_deadline == null) return; |
766 _deadlineTimer = new Timer(_deadline, () { | 777 _deadlineTimer = new Timer(_deadline, () { |
767 _outgoing.socket.destroy(); | 778 _outgoing.socket.destroy(); |
768 }); | 779 }); |
769 } | 780 } |
770 | 781 |
771 void _writeHeader() { | 782 void _writeHeader() { |
772 var builder = new BytesBuilder(); | 783 Uint8List buffer = _httpRequest._httpConnection._headersBuffer; |
773 writeSP() => builder.add(const [_CharCode.SP]); | 784 int offset = 0; |
774 writeCRLF() => builder.add(const [_CharCode.CR, _CharCode.LF]); | 785 |
| 786 void write(List<int> bytes) { |
| 787 int len = bytes.length; |
| 788 for (int i = 0; i < len; i++) { |
| 789 buffer[offset + i] = bytes[i]; |
| 790 } |
| 791 offset += len; |
| 792 } |
775 | 793 |
776 // Write status line. | 794 // Write status line. |
777 if (headers.protocolVersion == "1.1") { | 795 if (headers.protocolVersion == "1.1") { |
778 builder.add(_Const.HTTP11); | 796 write(_Const.HTTP11); |
779 } else { | 797 } else { |
780 builder.add(_Const.HTTP10); | 798 write(_Const.HTTP10); |
781 } | 799 } |
782 writeSP(); | 800 buffer[offset++] = _CharCode.SP; |
783 builder.add(statusCode.toString().codeUnits); | 801 write(statusCode.toString().codeUnits); |
784 writeSP(); | 802 buffer[offset++] = _CharCode.SP; |
785 builder.add(reasonPhrase.codeUnits); | 803 write(reasonPhrase.codeUnits); |
786 writeCRLF(); | 804 buffer[offset++] = _CharCode.CR; |
| 805 buffer[offset++] = _CharCode.LF; |
787 | 806 |
788 var session = _httpRequest._session; | 807 var session = _httpRequest._session; |
789 if (session != null && !session._destroyed) { | 808 if (session != null && !session._destroyed) { |
790 // Mark as not new. | 809 // Mark as not new. |
791 session._isNew = false; | 810 session._isNew = false; |
792 // Make sure we only send the current session id. | 811 // Make sure we only send the current session id. |
793 bool found = false; | 812 bool found = false; |
794 for (int i = 0; i < cookies.length; i++) { | 813 for (int i = 0; i < cookies.length; i++) { |
795 if (cookies[i].name.toUpperCase() == _DART_SESSION_ID) { | 814 if (cookies[i].name.toUpperCase() == _DART_SESSION_ID) { |
796 cookies[i].value = session.id; | 815 cookies[i].value = session.id; |
(...skipping 12 matching lines...) Expand all Loading... |
809 // Add all the cookies set to the headers. | 828 // Add all the cookies set to the headers. |
810 if (_cookies != null) { | 829 if (_cookies != null) { |
811 _cookies.forEach((cookie) { | 830 _cookies.forEach((cookie) { |
812 headers.add(HttpHeaders.SET_COOKIE, cookie); | 831 headers.add(HttpHeaders.SET_COOKIE, cookie); |
813 }); | 832 }); |
814 } | 833 } |
815 | 834 |
816 headers._finalize(); | 835 headers._finalize(); |
817 | 836 |
818 // Write headers. | 837 // Write headers. |
819 headers._write(builder); | 838 offset = headers._write(buffer, offset); |
820 writeCRLF(); | 839 buffer[offset++] = _CharCode.CR; |
821 _headersSink.add(builder.takeBytes()); | 840 buffer[offset++] = _CharCode.LF; |
| 841 _headersSink.add(new Uint8List.view(buffer.buffer, 0, offset)); |
822 } | 842 } |
823 | 843 |
824 String _findReasonPhrase(int statusCode) { | 844 String _findReasonPhrase(int statusCode) { |
825 if (_reasonPhrase != null) { | 845 if (_reasonPhrase != null) { |
826 return _reasonPhrase; | 846 return _reasonPhrase; |
827 } | 847 } |
828 | 848 |
829 switch (statusCode) { | 849 switch (statusCode) { |
830 case HttpStatus.CONTINUE: return "Continue"; | 850 case HttpStatus.CONTINUE: return "Continue"; |
831 case HttpStatus.SWITCHING_PROTOCOLS: return "Switching Protocols"; | 851 case HttpStatus.SWITCHING_PROTOCOLS: return "Switching Protocols"; |
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1005 if (_httpClientConnection._proxyTunnel) { | 1025 if (_httpClientConnection._proxyTunnel) { |
1006 return uriStartingFromPath(); | 1026 return uriStartingFromPath(); |
1007 } else { | 1027 } else { |
1008 return uri.toString(); | 1028 return uri.toString(); |
1009 } | 1029 } |
1010 } | 1030 } |
1011 } | 1031 } |
1012 } | 1032 } |
1013 | 1033 |
1014 void _writeHeader() { | 1034 void _writeHeader() { |
1015 var builder = new BytesBuilder(); | 1035 Uint8List buffer = _httpClientConnection._headersBuffer; |
| 1036 int offset = 0; |
1016 | 1037 |
1017 writeSP() => builder.add(const [_CharCode.SP]); | 1038 void write(List<int> bytes) { |
1018 | 1039 int len = bytes.length; |
1019 writeCRLF() => builder.add(const [_CharCode.CR, _CharCode.LF]); | 1040 for (int i = 0; i < len; i++) { |
| 1041 buffer[offset + i] = bytes[i]; |
| 1042 } |
| 1043 offset += len; |
| 1044 } |
1020 | 1045 |
1021 // Write the request method. | 1046 // Write the request method. |
1022 builder.add(method.codeUnits); | 1047 write(method.codeUnits); |
1023 writeSP(); | 1048 buffer[offset++] = _CharCode.SP; |
1024 // Write the request URI. | 1049 // Write the request URI. |
1025 builder.add(_requestUri().codeUnits); | 1050 write(_requestUri().codeUnits); |
1026 writeSP(); | 1051 buffer[offset++] = _CharCode.SP; |
1027 // Write HTTP/1.1. | 1052 // Write HTTP/1.1. |
1028 builder.add(_Const.HTTP11); | 1053 write(_Const.HTTP11); |
1029 writeCRLF(); | 1054 buffer[offset++] = _CharCode.CR; |
| 1055 buffer[offset++] = _CharCode.LF; |
1030 | 1056 |
1031 // Add the cookies to the headers. | 1057 // Add the cookies to the headers. |
1032 if (!cookies.isEmpty) { | 1058 if (!cookies.isEmpty) { |
1033 StringBuffer sb = new StringBuffer(); | 1059 StringBuffer sb = new StringBuffer(); |
1034 for (int i = 0; i < cookies.length; i++) { | 1060 for (int i = 0; i < cookies.length; i++) { |
1035 if (i > 0) sb.write("; "); | 1061 if (i > 0) sb.write("; "); |
1036 sb.write(cookies[i].name); | 1062 sb.write(cookies[i].name); |
1037 sb.write("="); | 1063 sb.write("="); |
1038 sb.write(cookies[i].value); | 1064 sb.write(cookies[i].value); |
1039 } | 1065 } |
1040 headers.add(HttpHeaders.COOKIE, sb.toString()); | 1066 headers.add(HttpHeaders.COOKIE, sb.toString()); |
1041 } | 1067 } |
1042 | 1068 |
1043 headers._finalize(); | 1069 headers._finalize(); |
1044 | 1070 |
1045 // Write headers. | 1071 // Write headers. |
1046 headers._write(builder); | 1072 offset = headers._write(buffer, offset); |
1047 writeCRLF(); | 1073 buffer[offset++] = _CharCode.CR; |
1048 _headersSink.add(builder.takeBytes()); | 1074 buffer[offset++] = _CharCode.LF; |
| 1075 _headersSink.add(new Uint8List.view(buffer.buffer, 0, offset)); |
1049 } | 1076 } |
1050 } | 1077 } |
1051 | 1078 |
1052 | 1079 |
1053 class _ChunkedTransformerSink implements EventSink<List<int>> { | 1080 class _ChunkedTransformerSink implements EventSink<List<int>> { |
1054 | 1081 |
1055 int _pendingFooter = 0; | 1082 int _pendingFooter = 0; |
1056 final EventSink<List<int>> _outSink; | 1083 final EventSink<List<int>> _outSink; |
1057 | 1084 |
1058 _ChunkedTransformerSink(this._outSink); | 1085 _ChunkedTransformerSink(this._outSink); |
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1202 final String key; | 1229 final String key; |
1203 final Socket _socket; | 1230 final Socket _socket; |
1204 final bool _proxyTunnel; | 1231 final bool _proxyTunnel; |
1205 final _HttpParser _httpParser; | 1232 final _HttpParser _httpParser; |
1206 StreamSubscription _subscription; | 1233 StreamSubscription _subscription; |
1207 final _HttpClient _httpClient; | 1234 final _HttpClient _httpClient; |
1208 bool _dispose = false; | 1235 bool _dispose = false; |
1209 Timer _idleTimer; | 1236 Timer _idleTimer; |
1210 bool closed = false; | 1237 bool closed = false; |
1211 Uri _currentUri; | 1238 Uri _currentUri; |
| 1239 final Uint8List _headersBuffer = new Uint8List(_HEADERS_BUFFER_SIZE); |
1212 | 1240 |
1213 Completer<_HttpIncoming> _nextResponseCompleter; | 1241 Completer<_HttpIncoming> _nextResponseCompleter; |
1214 Future _streamFuture; | 1242 Future _streamFuture; |
1215 | 1243 |
1216 _HttpClientConnection(String this.key, | 1244 _HttpClientConnection(String this.key, |
1217 Socket this._socket, | 1245 Socket this._socket, |
1218 _HttpClient this._httpClient, | 1246 _HttpClient this._httpClient, |
1219 [this._proxyTunnel = false]) | 1247 [this._proxyTunnel = false]) |
1220 : _httpParser = new _HttpParser.responseParser() { | 1248 : _httpParser = new _HttpParser.responseParser() { |
1221 _socket.pipe(_httpParser); | 1249 _socket.pipe(_httpParser); |
(...skipping 629 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1851 static const _CLOSING = 2; | 1879 static const _CLOSING = 2; |
1852 static const _DETACHED = 3; | 1880 static const _DETACHED = 3; |
1853 | 1881 |
1854 int _state = _IDLE; | 1882 int _state = _IDLE; |
1855 | 1883 |
1856 final Socket _socket; | 1884 final Socket _socket; |
1857 final _HttpServer _httpServer; | 1885 final _HttpServer _httpServer; |
1858 final _HttpParser _httpParser; | 1886 final _HttpParser _httpParser; |
1859 StreamSubscription _subscription; | 1887 StreamSubscription _subscription; |
1860 Timer _idleTimer; | 1888 Timer _idleTimer; |
| 1889 final Uint8List _headersBuffer = new Uint8List(_HEADERS_BUFFER_SIZE); |
1861 | 1890 |
1862 Future _streamFuture; | 1891 Future _streamFuture; |
1863 | 1892 |
1864 _HttpConnection(Socket this._socket, _HttpServer this._httpServer) | 1893 _HttpConnection(Socket this._socket, _HttpServer this._httpServer) |
1865 : _httpParser = new _HttpParser.requestParser() { | 1894 : _httpParser = new _HttpParser.requestParser() { |
1866 _startTimeout(); | 1895 _startTimeout(); |
1867 _socket.pipe(_httpParser); | 1896 _socket.pipe(_httpParser); |
1868 _subscription = _httpParser.listen( | 1897 _subscription = _httpParser.listen( |
1869 (incoming) { | 1898 (incoming) { |
1870 _stopTimeout(); | 1899 _stopTimeout(); |
(...skipping 650 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2521 final Uri location; | 2550 final Uri location; |
2522 } | 2551 } |
2523 | 2552 |
2524 String _getHttpVersion() { | 2553 String _getHttpVersion() { |
2525 var version = Platform.version; | 2554 var version = Platform.version; |
2526 // Only include major and minor version numbers. | 2555 // Only include major and minor version numbers. |
2527 int index = version.indexOf('.', version.indexOf('.') + 1); | 2556 int index = version.indexOf('.', version.indexOf('.') + 1); |
2528 version = version.substring(0, index); | 2557 version = version.substring(0, index); |
2529 return 'Dart/$version (dart:io)'; | 2558 return 'Dart/$version (dart:io)'; |
2530 } | 2559 } |
| 2560 |
| 2561 |
OLD | NEW |