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 292 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
303 .then((request) => request.close()); | 303 .then((request) => request.close()); |
304 }); | 304 }); |
305 } | 305 } |
306 | 306 |
307 List<String> authChallenge() { | 307 List<String> authChallenge() { |
308 return proxyAuth ? headers[HttpHeaders.PROXY_AUTHENTICATE] | 308 return proxyAuth ? headers[HttpHeaders.PROXY_AUTHENTICATE] |
309 : headers[HttpHeaders.WWW_AUTHENTICATE]; | 309 : headers[HttpHeaders.WWW_AUTHENTICATE]; |
310 } | 310 } |
311 | 311 |
312 _Credentials findCredentials(_AuthenticationScheme scheme) { | 312 _Credentials findCredentials(_AuthenticationScheme scheme) { |
313 return proxyAuth ? _httpClient._findProxyCredentials(_httpRequest._proxy,
scheme) | 313 return proxyAuth ? _httpClient._findProxyCredentials(_httpRequest._proxy, |
| 314 scheme) |
314 : _httpClient._findCredentials(_httpRequest.uri, scheme); | 315 : _httpClient._findCredentials(_httpRequest.uri, scheme); |
315 } | 316 } |
316 | 317 |
317 void removeCredentials(_Credentials cr) { | 318 void removeCredentials(_Credentials cr) { |
318 if (proxyAuth) { | 319 if (proxyAuth) { |
319 _httpClient._removeProxyCredentials(cr); | 320 _httpClient._removeProxyCredentials(cr); |
320 } else { | 321 } else { |
321 _httpClient._removeCredentials(cr); | 322 _httpClient._removeCredentials(cr); |
322 } | 323 } |
323 } | 324 } |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
404 }); | 405 }); |
405 } | 406 } |
406 } | 407 } |
407 | 408 |
408 | 409 |
409 abstract class _HttpOutboundMessage<T> extends _IOSinkImpl { | 410 abstract class _HttpOutboundMessage<T> extends _IOSinkImpl { |
410 // Used to mark when the body should be written. This is used for HEAD | 411 // Used to mark when the body should be written. This is used for HEAD |
411 // requests and in error handling. | 412 // requests and in error handling. |
412 bool _encodingSet = false; | 413 bool _encodingSet = false; |
413 | 414 |
| 415 bool _bufferOutput = true; |
| 416 |
414 final Uri _uri; | 417 final Uri _uri; |
415 final _HttpOutgoing _outgoing; | 418 final _HttpOutgoing _outgoing; |
416 | 419 |
417 final _HttpHeaders headers; | 420 final _HttpHeaders headers; |
418 | 421 |
419 _HttpOutboundMessage(Uri uri, | 422 _HttpOutboundMessage(Uri uri, |
420 String protocolVersion, | 423 String protocolVersion, |
421 _HttpOutgoing outgoing) | 424 _HttpOutgoing outgoing) |
422 : super(outgoing, null), | 425 : super(outgoing, null), |
423 _uri = uri, | 426 _uri = uri, |
(...skipping 10 matching lines...) Expand all Loading... |
434 int get contentLength => headers.contentLength; | 437 int get contentLength => headers.contentLength; |
435 void set contentLength(int contentLength) { | 438 void set contentLength(int contentLength) { |
436 headers.contentLength = contentLength; | 439 headers.contentLength = contentLength; |
437 } | 440 } |
438 | 441 |
439 bool get persistentConnection => headers.persistentConnection; | 442 bool get persistentConnection => headers.persistentConnection; |
440 void set persistentConnection(bool p) { | 443 void set persistentConnection(bool p) { |
441 headers.persistentConnection = p; | 444 headers.persistentConnection = p; |
442 } | 445 } |
443 | 446 |
| 447 bool get bufferOutput => _bufferOutput; |
| 448 void set bufferOutput(bool bufferOutput) { |
| 449 if (_outgoing.headersWritten) throw new StateError("Header already sent"); |
| 450 _bufferOutput = bufferOutput; |
| 451 } |
| 452 |
| 453 |
444 Encoding get encoding { | 454 Encoding get encoding { |
445 if (_encodingSet && _outgoing.headersWritten) { | 455 if (_encodingSet && _outgoing.headersWritten) { |
446 return _encoding; | 456 return _encoding; |
447 } | 457 } |
448 var charset; | 458 var charset; |
449 if (headers.contentType != null && headers.contentType.charset != null) { | 459 if (headers.contentType != null && headers.contentType.charset != null) { |
450 charset = headers.contentType.charset; | 460 charset = headers.contentType.charset; |
451 } else { | 461 } else { |
452 charset = "iso-8859-1"; | 462 charset = "iso-8859-1"; |
453 } | 463 } |
454 return Encoding.getByName(charset); | 464 return Encoding.getByName(charset); |
455 } | 465 } |
456 | 466 |
457 void add(List<int> data) { | 467 void add(List<int> data) { |
458 if (data.length == 0) return; | 468 if (data.length == 0) return; |
459 super.add(data); | 469 super.add(data); |
460 } | 470 } |
461 | 471 |
462 void write(Object obj) { | 472 void write(Object obj) { |
463 if (!_encodingSet) { | 473 if (!_encodingSet) { |
464 _encoding = encoding; | 474 _encoding = encoding; |
465 _encodingSet = true; | 475 _encodingSet = true; |
466 } | 476 } |
467 super.write(obj); | 477 super.write(obj); |
468 } | 478 } |
469 | 479 |
470 void _writeHeader(); | 480 void _writeHeader(); |
| 481 |
| 482 bool get _isConnectionClosed => false; |
471 } | 483 } |
472 | 484 |
473 | 485 |
474 class _HttpResponse extends _HttpOutboundMessage<HttpResponse> | 486 class _HttpResponse extends _HttpOutboundMessage<HttpResponse> |
475 implements HttpResponse { | 487 implements HttpResponse { |
476 int _statusCode = 200; | 488 int _statusCode = 200; |
477 String _reasonPhrase; | 489 String _reasonPhrase; |
478 List<Cookie> _cookies; | 490 List<Cookie> _cookies; |
479 _HttpRequest _httpRequest; | 491 _HttpRequest _httpRequest; |
480 Duration _deadline; | 492 Duration _deadline; |
481 Timer _deadlineTimer; | 493 Timer _deadlineTimer; |
482 | 494 |
483 _HttpResponse(Uri uri, | 495 _HttpResponse(Uri uri, |
484 String protocolVersion, | 496 String protocolVersion, |
485 _HttpOutgoing outgoing, | 497 _HttpOutgoing outgoing, |
486 String serverHeader) | 498 String serverHeader) |
487 : super(uri, protocolVersion, outgoing) { | 499 : super(uri, protocolVersion, outgoing) { |
488 if (serverHeader != null) headers._add('server', serverHeader); | 500 if (serverHeader != null) headers._add('server', serverHeader); |
489 } | 501 } |
490 | 502 |
| 503 bool get _isConnectionClosed => _httpRequest._httpConnection._isClosing; |
| 504 |
491 List<Cookie> get cookies { | 505 List<Cookie> get cookies { |
492 if (_cookies == null) _cookies = new List<Cookie>(); | 506 if (_cookies == null) _cookies = new List<Cookie>(); |
493 return _cookies; | 507 return _cookies; |
494 } | 508 } |
495 | 509 |
496 int get statusCode => _statusCode; | 510 int get statusCode => _statusCode; |
497 void set statusCode(int statusCode) { | 511 void set statusCode(int statusCode) { |
498 if (_outgoing.headersWritten) throw new StateError("Header already sent"); | 512 if (_outgoing.headersWritten) throw new StateError("Header already sent"); |
499 _statusCode = statusCode; | 513 _statusCode = statusCode; |
500 } | 514 } |
(...skipping 30 matching lines...) Expand all Loading... |
531 HttpConnectionInfo get connectionInfo => _httpRequest.connectionInfo; | 545 HttpConnectionInfo get connectionInfo => _httpRequest.connectionInfo; |
532 | 546 |
533 Duration get deadline => _deadline; | 547 Duration get deadline => _deadline; |
534 | 548 |
535 void set deadline(Duration d) { | 549 void set deadline(Duration d) { |
536 if (_deadlineTimer != null) _deadlineTimer.cancel(); | 550 if (_deadlineTimer != null) _deadlineTimer.cancel(); |
537 _deadline = d; | 551 _deadline = d; |
538 | 552 |
539 if (_deadline == null) return; | 553 if (_deadline == null) return; |
540 _deadlineTimer = new Timer(_deadline, () { | 554 _deadlineTimer = new Timer(_deadline, () { |
541 _outgoing._socketError = true; | 555 _httpRequest._httpConnection.destroy(); |
542 _outgoing.socket.destroy(); | |
543 }); | 556 }); |
544 } | 557 } |
545 | 558 |
546 void _writeHeader() { | 559 void _writeHeader() { |
547 Uint8List buffer = new Uint8List(_OUTGOING_BUFFER_SIZE); | 560 Uint8List buffer = new Uint8List(_OUTGOING_BUFFER_SIZE); |
548 int offset = 0; | 561 int offset = 0; |
549 | 562 |
550 void write(List<int> bytes) { | 563 void write(List<int> bytes) { |
551 int len = bytes.length; | 564 int len = bytes.length; |
552 for (int i = 0; i < len; i++) { | 565 for (int i = 0; i < len; i++) { |
(...skipping 359 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
912 " bytes")); | 925 " bytes")); |
913 } | 926 } |
914 } | 927 } |
915 if (headersWritten) return null; | 928 if (headersWritten) return null; |
916 headersWritten = true; | 929 headersWritten = true; |
917 Future drainFuture; | 930 Future drainFuture; |
918 bool isServerSide = outbound is _HttpResponse; | 931 bool isServerSide = outbound is _HttpResponse; |
919 bool gzip = false; | 932 bool gzip = false; |
920 if (isServerSide) { | 933 if (isServerSide) { |
921 var response = outbound; | 934 var response = outbound; |
922 if (outbound.headers.chunkedTransferEncoding) { | 935 if (outbound.bufferOutput && outbound.headers.chunkedTransferEncoding) { |
923 List acceptEncodings = | 936 List acceptEncodings = |
924 response._httpRequest.headers[HttpHeaders.ACCEPT_ENCODING]; | 937 response._httpRequest.headers[HttpHeaders.ACCEPT_ENCODING]; |
925 List contentEncoding = outbound.headers[HttpHeaders.CONTENT_ENCODING]; | 938 List contentEncoding = outbound.headers[HttpHeaders.CONTENT_ENCODING]; |
926 if (acceptEncodings != null && | 939 if (acceptEncodings != null && |
927 acceptEncodings | 940 acceptEncodings |
928 .expand((list) => list.split(",")) | 941 .expand((list) => list.split(",")) |
929 .any((encoding) => encoding.trim().toLowerCase() == "gzip") && | 942 .any((encoding) => encoding.trim().toLowerCase() == "gzip") && |
930 contentEncoding == null) { | 943 contentEncoding == null) { |
931 outbound.headers.set(HttpHeaders.CONTENT_ENCODING, "gzip"); | 944 outbound.headers.set(HttpHeaders.CONTENT_ENCODING, "gzip"); |
932 gzip = true; | 945 gzip = true; |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1036 } | 1049 } |
1037 }); | 1050 }); |
1038 } | 1051 } |
1039 | 1052 |
1040 Future close() { | 1053 Future close() { |
1041 // If we are already closed, return that future. | 1054 // If we are already closed, return that future. |
1042 if (_closeFuture != null) return _closeFuture; | 1055 if (_closeFuture != null) return _closeFuture; |
1043 // If we earlier saw an error, return immediate. The notification to | 1056 // If we earlier saw an error, return immediate. The notification to |
1044 // _Http*Connection is already done. | 1057 // _Http*Connection is already done. |
1045 if (_socketError) return new Future.value(outbound); | 1058 if (_socketError) return new Future.value(outbound); |
| 1059 if (outbound._isConnectionClosed) return new Future.value(outbound); |
1046 if (!headersWritten && !ignoreBody) { | 1060 if (!headersWritten && !ignoreBody) { |
1047 if (outbound.headers.contentLength == -1) { | 1061 if (outbound.headers.contentLength == -1) { |
1048 // If no body was written, ignoreBody is false (it's not a HEAD | 1062 // If no body was written, ignoreBody is false (it's not a HEAD |
1049 // request) and the content-length is unspecified, set contentLength to | 1063 // request) and the content-length is unspecified, set contentLength to |
1050 // 0. | 1064 // 0. |
1051 outbound.headers.chunkedTransferEncoding = false; | 1065 outbound.headers.chunkedTransferEncoding = false; |
1052 outbound.headers.contentLength = 0; | 1066 outbound.headers.contentLength = 0; |
1053 } else if (outbound.headers.contentLength > 0) { | 1067 } else if (outbound.headers.contentLength > 0) { |
1054 var error = new HttpException( | 1068 var error = new HttpException( |
1055 "No content even though contentLength was specified to be " | 1069 "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); | 1156 _addChunk(data, _gzipAdd); |
1143 })); | 1157 })); |
1144 } | 1158 } |
1145 } | 1159 } |
1146 | 1160 |
1147 bool _ignoreError(error) | 1161 bool _ignoreError(error) |
1148 => (error is SocketException || error is TlsException) && | 1162 => (error is SocketException || error is TlsException) && |
1149 outbound is HttpResponse; | 1163 outbound is HttpResponse; |
1150 | 1164 |
1151 void _addGZipChunk(chunk, void add(List<int> data)) { | 1165 void _addGZipChunk(chunk, void add(List<int> data)) { |
| 1166 if (!outbound.bufferOutput) { |
| 1167 add(chunk); |
| 1168 return; |
| 1169 } |
1152 if (chunk.length > _gzipBuffer.length - _gzipBufferLength) { | 1170 if (chunk.length > _gzipBuffer.length - _gzipBufferLength) { |
1153 add(new Uint8List.view( | 1171 add(new Uint8List.view( |
1154 _gzipBuffer.buffer, 0, _gzipBufferLength)); | 1172 _gzipBuffer.buffer, 0, _gzipBufferLength)); |
1155 _gzipBuffer = new Uint8List(_OUTGOING_BUFFER_SIZE); | 1173 _gzipBuffer = new Uint8List(_OUTGOING_BUFFER_SIZE); |
1156 _gzipBufferLength = 0; | 1174 _gzipBufferLength = 0; |
1157 } | 1175 } |
1158 if (chunk.length > _OUTGOING_BUFFER_SIZE) { | 1176 if (chunk.length > _OUTGOING_BUFFER_SIZE) { |
1159 add(chunk); | 1177 add(chunk); |
1160 } else { | 1178 } else { |
1161 _gzipBuffer.setRange(_gzipBufferLength, | 1179 _gzipBuffer.setRange(_gzipBufferLength, |
1162 _gzipBufferLength + chunk.length, | 1180 _gzipBufferLength + chunk.length, |
1163 chunk); | 1181 chunk); |
1164 _gzipBufferLength += chunk.length; | 1182 _gzipBufferLength += chunk.length; |
1165 } | 1183 } |
1166 } | 1184 } |
1167 | 1185 |
1168 void _addChunk(chunk, void add(List<int> data)) { | 1186 void _addChunk(chunk, void add(List<int> data)) { |
| 1187 if (!outbound.bufferOutput) { |
| 1188 if (_buffer != null) { |
| 1189 // If _buffer is not null, we have not written the header yet. Write |
| 1190 // it now. |
| 1191 add(new Uint8List.view(_buffer.buffer, 0, _length)); |
| 1192 _buffer = null; |
| 1193 _length = 0; |
| 1194 } |
| 1195 add(chunk); |
| 1196 return; |
| 1197 } |
1169 if (chunk.length > _buffer.length - _length) { | 1198 if (chunk.length > _buffer.length - _length) { |
1170 add(new Uint8List.view(_buffer.buffer, 0, _length)); | 1199 add(new Uint8List.view(_buffer.buffer, 0, _length)); |
1171 _buffer = new Uint8List(_OUTGOING_BUFFER_SIZE); | 1200 _buffer = new Uint8List(_OUTGOING_BUFFER_SIZE); |
1172 _length = 0; | 1201 _length = 0; |
1173 } | 1202 } |
1174 if (chunk.length > _OUTGOING_BUFFER_SIZE) { | 1203 if (chunk.length > _OUTGOING_BUFFER_SIZE) { |
1175 add(chunk); | 1204 add(chunk); |
1176 } else { | 1205 } else { |
1177 _buffer.setRange(_length, _length + chunk.length, chunk); | 1206 _buffer.setRange(_length, _length + chunk.length, chunk); |
1178 _length += chunk.length; | 1207 _length += chunk.length; |
(...skipping 1398 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2577 const _RedirectInfo(this.statusCode, this.method, this.location); | 2606 const _RedirectInfo(this.statusCode, this.method, this.location); |
2578 } | 2607 } |
2579 | 2608 |
2580 String _getHttpVersion() { | 2609 String _getHttpVersion() { |
2581 var version = Platform.version; | 2610 var version = Platform.version; |
2582 // Only include major and minor version numbers. | 2611 // Only include major and minor version numbers. |
2583 int index = version.indexOf('.', version.indexOf('.') + 1); | 2612 int index = version.indexOf('.', version.indexOf('.') + 1); |
2584 version = version.substring(0, index); | 2613 version = version.substring(0, index); |
2585 return 'Dart/$version (dart:io)'; | 2614 return 'Dart/$version (dart:io)'; |
2586 } | 2615 } |
OLD | NEW |