Chromium Code Reviews| 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 |