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 _OUTGOING_BUFFER_SIZE = 8 * 1024; | 7 const int _OUTGOING_BUFFER_SIZE = 8 * 1024; |
8 | 8 |
9 class _HttpIncoming extends Stream<List<int>> { | 9 class _HttpIncoming extends Stream<List<int>> { |
10 final int _transferLength; | 10 final int _transferLength; |
(...skipping 393 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
404 }); | 404 }); |
405 } | 405 } |
406 } | 406 } |
407 | 407 |
408 | 408 |
409 abstract class _HttpOutboundMessage<T> extends _IOSinkImpl { | 409 abstract class _HttpOutboundMessage<T> extends _IOSinkImpl { |
410 // Used to mark when the body should be written. This is used for HEAD | 410 // Used to mark when the body should be written. This is used for HEAD |
411 // requests and in error handling. | 411 // requests and in error handling. |
412 bool _encodingSet = false; | 412 bool _encodingSet = false; |
413 | 413 |
414 bool bufferOutput; | |
Søren Gjesse
2014/04/08 11:54:25
What happens if this is changed while the body is
Anders Johnsen
2014/04/08 12:40:07
Done. Sadly, this is needed, as zlib buffers as we
| |
415 | |
414 final Uri _uri; | 416 final Uri _uri; |
415 final _HttpOutgoing _outgoing; | 417 final _HttpOutgoing _outgoing; |
416 | 418 |
417 final _HttpHeaders headers; | 419 final _HttpHeaders headers; |
418 | 420 |
419 _HttpOutboundMessage(Uri uri, | 421 _HttpOutboundMessage(Uri uri, |
420 String protocolVersion, | 422 String protocolVersion, |
421 _HttpOutgoing outgoing) | 423 _HttpOutgoing outgoing, |
424 this.bufferOutput) | |
422 : super(outgoing, null), | 425 : super(outgoing, null), |
423 _uri = uri, | 426 _uri = uri, |
424 headers = new _HttpHeaders( | 427 headers = new _HttpHeaders( |
425 protocolVersion, | 428 protocolVersion, |
426 defaultPortForScheme: uri.scheme == 'https' ? | 429 defaultPortForScheme: uri.scheme == 'https' ? |
427 HttpClient.DEFAULT_HTTPS_PORT : | 430 HttpClient.DEFAULT_HTTPS_PORT : |
428 HttpClient.DEFAULT_HTTP_PORT), | 431 HttpClient.DEFAULT_HTTP_PORT), |
429 _outgoing = outgoing { | 432 _outgoing = outgoing { |
430 _outgoing.outbound = this; | 433 _outgoing.outbound = this; |
431 _encodingMutable = false; | 434 _encodingMutable = false; |
(...skipping 29 matching lines...) Expand all Loading... | |
461 | 464 |
462 void write(Object obj) { | 465 void write(Object obj) { |
463 if (!_encodingSet) { | 466 if (!_encodingSet) { |
464 _encoding = encoding; | 467 _encoding = encoding; |
465 _encodingSet = true; | 468 _encodingSet = true; |
466 } | 469 } |
467 super.write(obj); | 470 super.write(obj); |
468 } | 471 } |
469 | 472 |
470 void _writeHeader(); | 473 void _writeHeader(); |
474 | |
475 bool get _isConnectionClosed => false; | |
471 } | 476 } |
472 | 477 |
473 | 478 |
474 class _HttpResponse extends _HttpOutboundMessage<HttpResponse> | 479 class _HttpResponse extends _HttpOutboundMessage<HttpResponse> |
475 implements HttpResponse { | 480 implements HttpResponse { |
476 int _statusCode = 200; | 481 int _statusCode = 200; |
477 String _reasonPhrase; | 482 String _reasonPhrase; |
478 List<Cookie> _cookies; | 483 List<Cookie> _cookies; |
479 _HttpRequest _httpRequest; | 484 _HttpRequest _httpRequest; |
480 Duration _deadline; | 485 Duration _deadline; |
481 Timer _deadlineTimer; | 486 Timer _deadlineTimer; |
482 | 487 |
483 _HttpResponse(Uri uri, | 488 _HttpResponse(Uri uri, |
484 String protocolVersion, | 489 String protocolVersion, |
485 _HttpOutgoing outgoing, | 490 _HttpOutgoing outgoing, |
486 String serverHeader) | 491 String serverHeader, |
487 : super(uri, protocolVersion, outgoing) { | 492 bool bufferOutput) |
493 : super(uri, protocolVersion, outgoing, bufferOutput) { | |
488 if (serverHeader != null) headers._add('server', serverHeader); | 494 if (serverHeader != null) headers._add('server', serverHeader); |
489 } | 495 } |
490 | 496 |
497 bool get _isConnectionClosed => _httpRequest._httpConnection._isClosing; | |
498 | |
491 List<Cookie> get cookies { | 499 List<Cookie> get cookies { |
492 if (_cookies == null) _cookies = new List<Cookie>(); | 500 if (_cookies == null) _cookies = new List<Cookie>(); |
493 return _cookies; | 501 return _cookies; |
494 } | 502 } |
495 | 503 |
496 int get statusCode => _statusCode; | 504 int get statusCode => _statusCode; |
497 void set statusCode(int statusCode) { | 505 void set statusCode(int statusCode) { |
498 if (_outgoing.headersWritten) throw new StateError("Header already sent"); | 506 if (_outgoing.headersWritten) throw new StateError("Header already sent"); |
499 _statusCode = statusCode; | 507 _statusCode = statusCode; |
500 } | 508 } |
(...skipping 30 matching lines...) Expand all Loading... | |
531 HttpConnectionInfo get connectionInfo => _httpRequest.connectionInfo; | 539 HttpConnectionInfo get connectionInfo => _httpRequest.connectionInfo; |
532 | 540 |
533 Duration get deadline => _deadline; | 541 Duration get deadline => _deadline; |
534 | 542 |
535 void set deadline(Duration d) { | 543 void set deadline(Duration d) { |
536 if (_deadlineTimer != null) _deadlineTimer.cancel(); | 544 if (_deadlineTimer != null) _deadlineTimer.cancel(); |
537 _deadline = d; | 545 _deadline = d; |
538 | 546 |
539 if (_deadline == null) return; | 547 if (_deadline == null) return; |
540 _deadlineTimer = new Timer(_deadline, () { | 548 _deadlineTimer = new Timer(_deadline, () { |
541 _outgoing._socketError = true; | 549 _httpRequest._httpConnection.destroy(); |
542 _outgoing.socket.destroy(); | |
543 }); | 550 }); |
544 } | 551 } |
545 | 552 |
546 void _writeHeader() { | 553 void _writeHeader() { |
547 Uint8List buffer = new Uint8List(_OUTGOING_BUFFER_SIZE); | 554 Uint8List buffer = new Uint8List(_OUTGOING_BUFFER_SIZE); |
548 int offset = 0; | 555 int offset = 0; |
549 | 556 |
550 void write(List<int> bytes) { | 557 void write(List<int> bytes) { |
551 int len = bytes.length; | 558 int len = bytes.length; |
552 for (int i = 0; i < len; i++) { | 559 for (int i = 0; i < len; i++) { |
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
682 | 689 |
683 // TODO(ajohnsen): Get default value from client? | 690 // TODO(ajohnsen): Get default value from client? |
684 bool _followRedirects = true; | 691 bool _followRedirects = true; |
685 | 692 |
686 int _maxRedirects = 5; | 693 int _maxRedirects = 5; |
687 | 694 |
688 List<RedirectInfo> _responseRedirects = []; | 695 List<RedirectInfo> _responseRedirects = []; |
689 | 696 |
690 _HttpClientRequest(_HttpOutgoing outgoing, Uri uri, this.method, this._proxy, | 697 _HttpClientRequest(_HttpOutgoing outgoing, Uri uri, this.method, this._proxy, |
691 this._httpClient, this._httpClientConnection) | 698 this._httpClient, this._httpClientConnection) |
692 : super(uri, "1.1", outgoing), | 699 : super(uri, "1.1", outgoing, true), |
693 uri = uri { | 700 uri = uri { |
694 // GET and HEAD have 'content-length: 0' by default. | 701 // GET and HEAD have 'content-length: 0' by default. |
695 if (method == "GET" || method == "HEAD") { | 702 if (method == "GET" || method == "HEAD") { |
696 contentLength = 0; | 703 contentLength = 0; |
697 } else { | 704 } else { |
698 headers.chunkedTransferEncoding = true; | 705 headers.chunkedTransferEncoding = true; |
699 } | 706 } |
700 } | 707 } |
701 | 708 |
702 Future<HttpClientResponse> get done { | 709 Future<HttpClientResponse> get done { |
(...skipping 333 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1036 } | 1043 } |
1037 }); | 1044 }); |
1038 } | 1045 } |
1039 | 1046 |
1040 Future close() { | 1047 Future close() { |
1041 // If we are already closed, return that future. | 1048 // If we are already closed, return that future. |
1042 if (_closeFuture != null) return _closeFuture; | 1049 if (_closeFuture != null) return _closeFuture; |
1043 // If we earlier saw an error, return immediate. The notification to | 1050 // If we earlier saw an error, return immediate. The notification to |
1044 // _Http*Connection is already done. | 1051 // _Http*Connection is already done. |
1045 if (_socketError) return new Future.value(outbound); | 1052 if (_socketError) return new Future.value(outbound); |
1053 if (outbound._isConnectionClosed) return new Future.value(outbound); | |
1046 if (!headersWritten && !ignoreBody) { | 1054 if (!headersWritten && !ignoreBody) { |
1047 if (outbound.headers.contentLength == -1) { | 1055 if (outbound.headers.contentLength == -1) { |
1048 // If no body was written, ignoreBody is false (it's not a HEAD | 1056 // If no body was written, ignoreBody is false (it's not a HEAD |
1049 // request) and the content-length is unspecified, set contentLength to | 1057 // request) and the content-length is unspecified, set contentLength to |
1050 // 0. | 1058 // 0. |
1051 outbound.headers.chunkedTransferEncoding = false; | 1059 outbound.headers.chunkedTransferEncoding = false; |
1052 outbound.headers.contentLength = 0; | 1060 outbound.headers.contentLength = 0; |
1053 } else if (outbound.headers.contentLength > 0) { | 1061 } else if (outbound.headers.contentLength > 0) { |
1054 var error = new HttpException( | 1062 var error = new HttpException( |
1055 "No content even though contentLength was specified to be " | 1063 "No content even though contentLength was specified to be " |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1142 _addChunk(data, _gzipAdd); | 1150 _addChunk(data, _gzipAdd); |
1143 })); | 1151 })); |
1144 } | 1152 } |
1145 } | 1153 } |
1146 | 1154 |
1147 bool _ignoreError(error) | 1155 bool _ignoreError(error) |
1148 => (error is SocketException || error is TlsException) && | 1156 => (error is SocketException || error is TlsException) && |
1149 outbound is HttpResponse; | 1157 outbound is HttpResponse; |
1150 | 1158 |
1151 void _addGZipChunk(chunk, void add(List<int> data)) { | 1159 void _addGZipChunk(chunk, void add(List<int> data)) { |
1160 if (!outbound.bufferOutput) { | |
1161 add(chunk); | |
1162 return; | |
1163 } | |
1152 if (chunk.length > _gzipBuffer.length - _gzipBufferLength) { | 1164 if (chunk.length > _gzipBuffer.length - _gzipBufferLength) { |
1153 add(new Uint8List.view( | 1165 add(new Uint8List.view( |
1154 _gzipBuffer.buffer, 0, _gzipBufferLength)); | 1166 _gzipBuffer.buffer, 0, _gzipBufferLength)); |
1155 _gzipBuffer = new Uint8List(_OUTGOING_BUFFER_SIZE); | 1167 _gzipBuffer = new Uint8List(_OUTGOING_BUFFER_SIZE); |
1156 _gzipBufferLength = 0; | 1168 _gzipBufferLength = 0; |
1157 } | 1169 } |
1158 if (chunk.length > _OUTGOING_BUFFER_SIZE) { | 1170 if (chunk.length > _OUTGOING_BUFFER_SIZE) { |
1159 add(chunk); | 1171 add(chunk); |
1160 } else { | 1172 } else { |
1161 _gzipBuffer.setRange(_gzipBufferLength, | 1173 _gzipBuffer.setRange(_gzipBufferLength, |
1162 _gzipBufferLength + chunk.length, | 1174 _gzipBufferLength + chunk.length, |
1163 chunk); | 1175 chunk); |
1164 _gzipBufferLength += chunk.length; | 1176 _gzipBufferLength += chunk.length; |
1165 } | 1177 } |
1166 } | 1178 } |
1167 | 1179 |
1168 void _addChunk(chunk, void add(List<int> data)) { | 1180 void _addChunk(chunk, void add(List<int> data)) { |
1181 if (!outbound.bufferOutput) { | |
1182 if (_buffer != null) { | |
1183 add(new Uint8List.view(_buffer.buffer, 0, _length)); | |
Søren Gjesse
2014/04/08 11:54:25
Please add a comment on why you are doing this.
Anders Johnsen
2014/04/08 12:40:07
Done.
| |
1184 _buffer = null; | |
1185 _length = 0; | |
1186 } | |
1187 add(chunk); | |
1188 return; | |
1189 } | |
1169 if (chunk.length > _buffer.length - _length) { | 1190 if (chunk.length > _buffer.length - _length) { |
1170 add(new Uint8List.view(_buffer.buffer, 0, _length)); | 1191 add(new Uint8List.view(_buffer.buffer, 0, _length)); |
1171 _buffer = new Uint8List(_OUTGOING_BUFFER_SIZE); | 1192 _buffer = new Uint8List(_OUTGOING_BUFFER_SIZE); |
1172 _length = 0; | 1193 _length = 0; |
1173 } | 1194 } |
1174 if (chunk.length > _OUTGOING_BUFFER_SIZE) { | 1195 if (chunk.length > _OUTGOING_BUFFER_SIZE) { |
1175 add(chunk); | 1196 add(chunk); |
1176 } else { | 1197 } else { |
1177 _buffer.setRange(_length, _length + chunk.length, chunk); | 1198 _buffer.setRange(_length, _length + chunk.length, chunk); |
1178 _length += chunk.length; | 1199 _length += chunk.length; |
(...skipping 717 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1896 if (closing) destroy(); | 1917 if (closing) destroy(); |
1897 }); | 1918 }); |
1898 // Only handle one incoming request at the time. Keep the | 1919 // Only handle one incoming request at the time. Keep the |
1899 // stream paused until the request has been send. | 1920 // stream paused until the request has been send. |
1900 _subscription.pause(); | 1921 _subscription.pause(); |
1901 _state = _ACTIVE; | 1922 _state = _ACTIVE; |
1902 var outgoing = new _HttpOutgoing(_socket); | 1923 var outgoing = new _HttpOutgoing(_socket); |
1903 var response = new _HttpResponse(incoming.uri, | 1924 var response = new _HttpResponse(incoming.uri, |
1904 incoming.headers.protocolVersion, | 1925 incoming.headers.protocolVersion, |
1905 outgoing, | 1926 outgoing, |
1906 _httpServer.serverHeader); | 1927 _httpServer.serverHeader, |
1928 _httpServer.bufferOutput); | |
1907 var request = new _HttpRequest(response, incoming, _httpServer, this); | 1929 var request = new _HttpRequest(response, incoming, _httpServer, this); |
1908 _streamFuture = outgoing.done | 1930 _streamFuture = outgoing.done |
1909 .then((_) { | 1931 .then((_) { |
1910 response.deadline = null; | 1932 response.deadline = null; |
1911 if (_state == _DETACHED) return; | 1933 if (_state == _DETACHED) return; |
1912 if (response.persistentConnection && | 1934 if (response.persistentConnection && |
1913 request.persistentConnection && | 1935 request.persistentConnection && |
1914 incoming.fullBodyRead && | 1936 incoming.fullBodyRead && |
1915 !_httpParser.upgrade && | 1937 !_httpParser.upgrade && |
1916 !_httpServer.closed) { | 1938 !_httpServer.closed) { |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1971 bool get _isActive => _state == _ACTIVE; | 1993 bool get _isActive => _state == _ACTIVE; |
1972 bool get _isIdle => _state == _IDLE; | 1994 bool get _isIdle => _state == _IDLE; |
1973 bool get _isClosing => _state == _CLOSING; | 1995 bool get _isClosing => _state == _CLOSING; |
1974 bool get _isDetached => _state == _DETACHED; | 1996 bool get _isDetached => _state == _DETACHED; |
1975 } | 1997 } |
1976 | 1998 |
1977 | 1999 |
1978 // HTTP server waiting for socket connections. | 2000 // HTTP server waiting for socket connections. |
1979 class _HttpServer extends Stream<HttpRequest> implements HttpServer { | 2001 class _HttpServer extends Stream<HttpRequest> implements HttpServer { |
1980 String serverHeader; | 2002 String serverHeader; |
2003 bool bufferOutput = true; | |
1981 | 2004 |
1982 Duration _idleTimeout; | 2005 Duration _idleTimeout; |
1983 Timer _idleTimer; | 2006 Timer _idleTimer; |
1984 | 2007 |
1985 static Future<HttpServer> bind(address, int port, int backlog) { | 2008 static Future<HttpServer> bind(address, int port, int backlog) { |
1986 return ServerSocket.bind(address, port, backlog: backlog).then((socket) { | 2009 return ServerSocket.bind(address, port, backlog: backlog).then((socket) { |
1987 return new _HttpServer._(socket, true); | 2010 return new _HttpServer._(socket, true); |
1988 }); | 2011 }); |
1989 } | 2012 } |
1990 | 2013 |
(...skipping 586 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2577 const _RedirectInfo(this.statusCode, this.method, this.location); | 2600 const _RedirectInfo(this.statusCode, this.method, this.location); |
2578 } | 2601 } |
2579 | 2602 |
2580 String _getHttpVersion() { | 2603 String _getHttpVersion() { |
2581 var version = Platform.version; | 2604 var version = Platform.version; |
2582 // Only include major and minor version numbers. | 2605 // Only include major and minor version numbers. |
2583 int index = version.indexOf('.', version.indexOf('.') + 1); | 2606 int index = version.indexOf('.', version.indexOf('.') + 1); |
2584 version = version.substring(0, index); | 2607 version = version.substring(0, index); |
2585 return 'Dart/$version (dart:io)'; | 2608 return 'Dart/$version (dart:io)'; |
2586 } | 2609 } |
OLD | NEW |