| 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 class _HttpIncoming extends Stream<List<int>> { | 7 class _HttpIncoming extends Stream<List<int>> { |
| 8 final int _transferLength; | 8 final int _transferLength; |
| 9 final Completer _dataCompleter = new Completer(); | 9 final Completer _dataCompleter = new Completer(); |
| 10 Stream<List<int>> _stream; | 10 Stream<List<int>> _stream; |
| (...skipping 304 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 315 return new Future.immediate(this); | 315 return new Future.immediate(this); |
| 316 } | 316 } |
| 317 } | 317 } |
| 318 | 318 |
| 319 | 319 |
| 320 abstract class _HttpOutboundMessage<T> implements IOSink { | 320 abstract class _HttpOutboundMessage<T> implements IOSink { |
| 321 // Used to mark when the body should be written. This is used for HEAD | 321 // Used to mark when the body should be written. This is used for HEAD |
| 322 // requests and in error handling. | 322 // requests and in error handling. |
| 323 bool _ignoreBody = false; | 323 bool _ignoreBody = false; |
| 324 bool _headersWritten = false; | 324 bool _headersWritten = false; |
| 325 bool _chunked = false; | |
| 326 | 325 |
| 327 final IOSink _ioSink; | 326 IOSink _ioSink; |
| 328 final _HttpOutgoing _outgoing; | 327 final _HttpOutgoing _outgoing; |
| 329 | 328 |
| 330 final _HttpHeaders headers; | 329 final _HttpHeaders headers; |
| 331 | 330 |
| 332 _HttpOutboundMessage(String protocolVersion, _HttpOutgoing outgoing) | 331 _HttpOutboundMessage(String protocolVersion, _HttpOutgoing outgoing) |
| 333 : _outgoing = outgoing, | 332 : _outgoing = outgoing, |
| 334 _ioSink = new IOSink(outgoing, encoding: Encoding.ASCII), | 333 _ioSink = new IOSink(outgoing, encoding: Encoding.ASCII), |
| 335 headers = new _HttpHeaders(protocolVersion); | 334 headers = new _HttpHeaders(protocolVersion); |
| 336 | 335 |
| 337 int get contentLength => headers.contentLength; | 336 int get contentLength => headers.contentLength; |
| 338 void set contentLength(int contentLength) { | 337 void set contentLength(int contentLength) { |
| 339 headers.contentLength = contentLength; | 338 headers.contentLength = contentLength; |
| 340 } | 339 } |
| 341 | 340 |
| 342 bool get persistentConnection => headers.persistentConnection; | 341 bool get persistentConnection => headers.persistentConnection; |
| 343 void set persistentConnection(bool p) { | 342 void set persistentConnection(bool p) { |
| 344 headers.persistentConnection = p; | 343 headers.persistentConnection = p; |
| 345 } | 344 } |
| 346 | 345 |
| 347 Encoding get encoding { | 346 Encoding get encoding { |
| 348 var charset; | 347 var charset; |
| 349 if (headers.contentType != null && headers.contentType.charset != null) { | 348 if (headers.contentType != null && headers.contentType.charset != null) { |
| 350 charset = headers.contentType.charset; | 349 charset = headers.contentType.charset; |
| 351 } else { | 350 } else { |
| 352 charset = "iso-8859-1"; | 351 charset = "iso-8859-1"; |
| 353 } | 352 } |
| 354 return Encoding.fromName(charset); | 353 return Encoding.fromName(charset); |
| 355 } | 354 } |
| 356 | 355 |
| 357 void set encoding(Encoding value) { | 356 void set encoding(Encoding value) { |
| 358 throw new StateError("IOSink encoding is not mutable"); | 357 throw new StateError("IOSink encoding is not mutable"); |
| 359 } | 358 } |
| 360 | 359 |
| 361 void write(Object obj) { | 360 void write(Object obj) { |
| 362 _writeHeaders(); | 361 _writeHeaders(); |
| 363 if (_ignoreBody) return; | |
| 364 // This comment is copied from runtime/lib/string_buffer_patch.dart. | 362 // This comment is copied from runtime/lib/string_buffer_patch.dart. |
| 365 // TODO(srdjan): The following four lines could be replaced by | 363 // TODO(srdjan): The following four lines could be replaced by |
| 366 // '$obj', but apparently this is too slow on the Dart VM. | 364 // '$obj', but apparently this is too slow on the Dart VM. |
| 367 String string; | 365 String string; |
| 368 if (obj is String) { | 366 if (obj is String) { |
| 369 string = obj; | 367 string = obj; |
| 370 } else { | 368 } else { |
| 371 string = obj.toString(); | 369 string = obj.toString(); |
| 372 if (string is! String) { | 370 if (string is! String) { |
| 373 throw new ArgumentError('toString() did not return a string'); | 371 throw new ArgumentError('toString() did not return a string'); |
| 374 } | 372 } |
| 375 } | 373 } |
| 376 if (string.isEmpty) return; | 374 if (string.isEmpty) return; |
| 377 if (_chunked) { | 375 _ioSink.write(string); |
| 378 _ChunkedTransformer._addChunk(_encodeString(string, encoding), | |
| 379 _ioSink.writeBytes); | |
| 380 } else { | |
| 381 _ioSink.write(string); | |
| 382 } | |
| 383 } | 376 } |
| 384 | 377 |
| 385 void writeAll(Iterable objects) { | 378 void writeAll(Iterable objects) { |
| 386 for (Object obj in objects) write(obj); | 379 for (Object obj in objects) write(obj); |
| 387 } | 380 } |
| 388 | 381 |
| 389 void writeln(Object obj) { | 382 void writeln(Object obj) { |
| 390 write(obj); | 383 write(obj); |
| 391 write("\n"); | 384 write("\n"); |
| 392 } | 385 } |
| 393 | 386 |
| 394 void writeCharCode(int charCode) { | 387 void writeCharCode(int charCode) { |
| 395 write(new String.fromCharCode(charCode)); | 388 write(new String.fromCharCode(charCode)); |
| 396 } | 389 } |
| 397 | 390 |
| 398 void writeBytes(List<int> data) { | 391 void writeBytes(List<int> data) { |
| 399 _writeHeaders(); | 392 _writeHeaders(); |
| 400 if (_ignoreBody || data.length == 0) return; | 393 if (data.length == 0) return; |
| 401 if (_chunked) { | 394 _ioSink.writeBytes(data); |
| 402 _ChunkedTransformer._addChunk(data, _ioSink.writeBytes); | |
| 403 } else { | |
| 404 _ioSink.writeBytes(data); | |
| 405 } | |
| 406 } | 395 } |
| 407 | 396 |
| 408 Future<T> consume(Stream<List<int>> stream) { | 397 Future<T> consume(Stream<List<int>> stream) { |
| 409 _writeHeaders(); | 398 _writeHeaders(); |
| 410 if (_ignoreBody) return new Future.immediate(this); | 399 return _ioSink.consume(stream); |
| 411 if (_chunked) { | |
| 412 // Transform when chunked. | |
| 413 stream = stream.transform(new _ChunkedTransformer()); | |
| 414 } | |
| 415 return _ioSink.consume(stream).then((_) => this); | |
| 416 } | 400 } |
| 417 | 401 |
| 418 Future<T> writeStream(Stream<List<int>> stream) { | 402 Future<T> writeStream(Stream<List<int>> stream) { |
| 419 _writeHeaders(); | 403 _writeHeaders(); |
| 420 if (_ignoreBody) return new Future.immediate(this); | |
| 421 if (_chunked) { | |
| 422 // Transform when chunked. | |
| 423 stream = stream.transform(new _ChunkedTransformer(writeEnd: false)); | |
| 424 } | |
| 425 return _ioSink.writeStream(stream).then((_) => this); | 404 return _ioSink.writeStream(stream).then((_) => this); |
| 426 } | 405 } |
| 427 | 406 |
| 428 void close() { | 407 void close() { |
| 429 if (!_headersWritten && !_ignoreBody && headers.chunkedTransferEncoding) { | 408 if (!_headersWritten && !_ignoreBody && headers.chunkedTransferEncoding) { |
| 430 // If no body was written, _ignoreBody is false (it's not a HEAD | 409 // If no body was written, _ignoreBody is false (it's not a HEAD |
| 431 // request) and the content-length is unspecified, set contentLength to 0. | 410 // request) and the content-length is unspecified, set contentLength to 0. |
| 411 headers.chunkedTransferEncoding = false; |
| 432 headers.contentLength = 0; | 412 headers.contentLength = 0; |
| 433 } | 413 } |
| 434 _writeHeaders(); | 414 _writeHeaders(); |
| 435 if (!_ignoreBody) { | |
| 436 if (_chunked) { | |
| 437 _ChunkedTransformer._addChunk([], _ioSink.writeBytes); | |
| 438 } | |
| 439 } | |
| 440 _ioSink.close(); | 415 _ioSink.close(); |
| 441 } | 416 } |
| 442 | 417 |
| 443 Future<T> get done => _ioSink.done.then((_) => this); | 418 Future<T> get done { |
| 419 _writeHeaders(); |
| 420 return _ioSink.done; |
| 421 } |
| 444 | 422 |
| 445 void _writeHeaders() { | 423 void _writeHeaders() { |
| 446 if (_headersWritten) return; | 424 if (_headersWritten) return; |
| 447 _headersWritten = true; | 425 _headersWritten = true; |
| 448 _ioSink.encoding = Encoding.ASCII; | 426 _ioSink.encoding = Encoding.ASCII; |
| 427 headers._synchronize(); // Be sure the 'chunked' option is updated. |
| 428 bool asGZip = false; |
| 429 bool isServerSide = this is _HttpResponse; |
| 430 if (isServerSide && headers.chunkedTransferEncoding) { |
| 431 List acceptEncodings = |
| 432 _httpRequest.headers[HttpHeaders.ACCEPT_ENCODING]; |
| 433 List contentEncoding = headers[HttpHeaders.CONTENT_ENCODING]; |
| 434 if (acceptEncodings != null && |
| 435 acceptEncodings.any((encoding) => encoding.toLowerCase() == "gzip") && |
| 436 contentEncoding == null) { |
| 437 headers.set(HttpHeaders.CONTENT_ENCODING, "gzip"); |
| 438 asGZip = true; |
| 439 } |
| 440 } |
| 449 _writeHeader(); | 441 _writeHeader(); |
| 442 _ioSink = new IOSink(new _HttpOutboundConsumer(_ioSink, _consume, asGZip)); |
| 450 _ioSink.encoding = encoding; | 443 _ioSink.encoding = encoding; |
| 444 } |
| 445 |
| 446 Future _consume(IOSink ioSink, Stream<List<int>> stream, bool asGZip) { |
| 447 int contentLength = headers.contentLength; |
| 451 if (_ignoreBody) { | 448 if (_ignoreBody) { |
| 452 _ioSink.close(); | 449 ioSink.close(); |
| 453 return; | 450 return stream.reduce(null, (x, y) {}).then((_) => this); |
| 454 } | 451 } |
| 455 _chunked = headers.chunkedTransferEncoding; | 452 if (headers.chunkedTransferEncoding) { |
| 456 if (headers.contentLength >= 0) { | 453 if (asGZip) { |
| 457 _outgoing.setTransferLength(headers.contentLength); | 454 stream = stream.transform(new ZLibDeflater(gzip: true, level: 6)); |
| 455 } |
| 456 stream = stream.transform(new _ChunkedTransformer()); |
| 457 } else if (contentLength >= 0) { |
| 458 stream = stream.transform(new _ContentLengthValidator(contentLength)); |
| 458 } | 459 } |
| 460 return stream.pipe(ioSink).then((_) => this); |
| 459 } | 461 } |
| 460 | 462 |
| 461 void _writeHeader(); // TODO(ajohnsen): Better name. | 463 void _writeHeader(); // TODO(ajohnsen): Better name. |
| 462 } | 464 } |
| 463 | 465 |
| 464 | 466 |
| 467 class _HttpOutboundConsumer implements StreamConsumer { |
| 468 Function _consume; |
| 469 IOSink _ioSink; |
| 470 bool _asGZip; |
| 471 _HttpOutboundConsumer(IOSink this._ioSink, |
| 472 Function this._consume, |
| 473 bool this._asGZip); |
| 474 |
| 475 Future consume(var stream) => _consume(_ioSink, stream, _asGZip); |
| 476 } |
| 477 |
| 478 |
| 465 class _HttpResponse extends _HttpOutboundMessage<HttpResponse> | 479 class _HttpResponse extends _HttpOutboundMessage<HttpResponse> |
| 466 implements HttpResponse { | 480 implements HttpResponse { |
| 467 int statusCode = 200; | 481 int statusCode = 200; |
| 468 String _reasonPhrase; | 482 String _reasonPhrase; |
| 469 List<Cookie> _cookies; | 483 List<Cookie> _cookies; |
| 470 _HttpRequest _httpRequest; | 484 _HttpRequest _httpRequest; |
| 471 | 485 |
| 472 _HttpResponse(String protocolVersion, | 486 _HttpResponse(String protocolVersion, |
| 473 _HttpOutgoing _outgoing) | 487 _HttpOutgoing _outgoing) |
| 474 : super(protocolVersion, _outgoing); | 488 : super(protocolVersion, _outgoing); |
| (...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 737 | 751 |
| 738 // Write headers. | 752 // Write headers. |
| 739 headers._write(_ioSink); | 753 headers._write(_ioSink); |
| 740 writeCRLF(); | 754 writeCRLF(); |
| 741 } | 755 } |
| 742 } | 756 } |
| 743 | 757 |
| 744 | 758 |
| 745 // Transformer that transforms data to HTTP Chunked Encoding. | 759 // Transformer that transforms data to HTTP Chunked Encoding. |
| 746 class _ChunkedTransformer extends StreamEventTransformer<List<int>, List<int>> { | 760 class _ChunkedTransformer extends StreamEventTransformer<List<int>, List<int>> { |
| 747 final bool writeEnd; | |
| 748 _ChunkedTransformer({this.writeEnd: true}); | |
| 749 | |
| 750 void handleData(List<int> data, EventSink<List<int>> sink) { | 761 void handleData(List<int> data, EventSink<List<int>> sink) { |
| 751 _addChunk(data, sink.add); | 762 sink.add(_chunkHeader(data.length)); |
| 763 if (data.length > 0) sink.add(data); |
| 764 sink.add(_chunkFooter); |
| 752 } | 765 } |
| 753 | 766 |
| 754 void handleDone(EventSink<List<int>> sink) { | 767 void handleDone(EventSink<List<int>> sink) { |
| 755 if (writeEnd) { | 768 handleData([], sink); |
| 756 _addChunk([], sink.add); | |
| 757 } | |
| 758 sink.close(); | 769 sink.close(); |
| 759 } | 770 } |
| 760 | 771 |
| 761 static void _addChunk(List<int> data, void add(List<int> data)) { | |
| 762 add(_chunkHeader(data.length)); | |
| 763 if (data.length > 0) add(data); | |
| 764 add(_chunkFooter); | |
| 765 } | |
| 766 | |
| 767 static List<int> _chunkHeader(int length) { | 772 static List<int> _chunkHeader(int length) { |
| 768 const hexDigits = const [0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, | 773 const hexDigits = const [0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, |
| 769 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46]; | 774 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46]; |
| 770 var header = []; | 775 var header = []; |
| 771 if (length == 0) { | 776 if (length == 0) { |
| 772 header.add(hexDigits[length]); | 777 header.add(hexDigits[length]); |
| 773 } else { | 778 } else { |
| 774 while (length > 0) { | 779 while (length > 0) { |
| 775 header.insertRange(0, 1, hexDigits[length % 16]); | 780 header.insertRange(0, 1, hexDigits[length % 16]); |
| 776 length = length >> 4; | 781 length = length >> 4; |
| 777 } | 782 } |
| 778 } | 783 } |
| 779 header.add(_CharCode.CR); | 784 header.add(_CharCode.CR); |
| 780 header.add(_CharCode.LF); | 785 header.add(_CharCode.LF); |
| 781 return header; | 786 return header; |
| 782 } | 787 } |
| 783 | 788 |
| 784 // Footer is just a CRLF. | 789 // Footer is just a CRLF. |
| 785 static List<int> get _chunkFooter => const [_CharCode.CR, _CharCode.LF]; | 790 static List<int> get _chunkFooter => const [_CharCode.CR, _CharCode.LF]; |
| 786 } | 791 } |
| 787 | 792 |
| 788 | 793 |
| 789 // Transformer that invokes [_onDone] when completed. | 794 // Transformer that validates the content length. |
| 790 class _DoneTransformer implements StreamTransformer<List<int>, List<int>> { | 795 class _ContentLengthValidator |
| 791 final StreamController<List<int>> _controller | 796 extends StreamEventTransformer<List<int>, List<int>> { |
| 792 = new StreamController<List<int>>(); | 797 final int expectedContentLength; |
| 793 final Function _onDone; | 798 int _bytesWritten = 0; |
| 794 | 799 |
| 795 _DoneTransformer(this._onDone); | 800 _ContentLengthValidator(int this.expectedContentLength); |
| 796 | 801 |
| 797 Stream<List<int>> bind(Stream<List<int>> stream) { | 802 void handleData(List<int> data, EventSink<List<int>> sink) { |
| 798 var subscription = stream.listen( | 803 _bytesWritten += data.length; |
| 799 _controller.add, | 804 if (_bytesWritten > expectedContentLength) { |
| 800 onError: _controller.addError, | 805 sink.addError(new AsyncError(new HttpException( |
| 801 onDone: () { | 806 "Content size exceeds specified contentLength. " |
| 802 _onDone(); | 807 "$_bytesWritten bytes written while expected " |
| 803 _controller.close(); | 808 "$expectedContentLength. " |
| 804 }); | 809 "[${new String.fromCharCodes(data)}]"))); |
| 805 return _controller.stream; | 810 sink.close(); |
| 811 } else { |
| 812 sink.add(data); |
| 813 } |
| 814 } |
| 815 |
| 816 void handleDone(EventSink<List<int>> sink) { |
| 817 if (_bytesWritten < expectedContentLength) { |
| 818 sink.addError(new AsyncError(new HttpException( |
| 819 "Content size below specified contentLength. " |
| 820 " $_bytesWritten bytes written while expected " |
| 821 "$expectedContentLength."))); |
| 822 } |
| 823 sink.close(); |
| 806 } | 824 } |
| 807 } | 825 } |
| 808 | 826 |
| 809 // Transformer that validates the data written. | |
| 810 class _DataValidatorTransformer | |
| 811 implements StreamTransformer<List<int>, List<int>> { | |
| 812 final StreamController<List<int>> _controller = | |
| 813 new StreamController<List<int>>(); | |
| 814 int _bytesWritten = 0; | |
| 815 | |
| 816 int expectedTransferLength; | |
| 817 | |
| 818 Stream<List<int>> bind(Stream<List<int>> stream) { | |
| 819 var subscription; | |
| 820 subscription = stream.listen( | |
| 821 (data) { | |
| 822 if (expectedTransferLength != null) { | |
| 823 _bytesWritten += data.length; | |
| 824 if (_bytesWritten > expectedTransferLength) { | |
| 825 subscription.cancel(); | |
| 826 _controller.addError(new HttpException( | |
| 827 "Content size exceeds specified contentLength. " | |
| 828 "$_bytesWritten bytes written while expected " | |
| 829 "$expectedTransferLength. " | |
| 830 "[${new String.fromCharCodes(data)}]")); | |
| 831 _controller.close(); | |
| 832 return; | |
| 833 } | |
| 834 } | |
| 835 _controller.add(data); | |
| 836 }, | |
| 837 onError: (error) { | |
| 838 _controller.addError(error); | |
| 839 _controller.close(); | |
| 840 }, | |
| 841 onDone: () { | |
| 842 if (expectedTransferLength != null) { | |
| 843 if (_bytesWritten < expectedTransferLength) { | |
| 844 _controller.addError(new HttpException( | |
| 845 "Content size below specified contentLength. " | |
| 846 " $_bytesWritten bytes written while expected " | |
| 847 "$expectedTransferLength.")); | |
| 848 } | |
| 849 } | |
| 850 _controller.close(); | |
| 851 }, | |
| 852 unsubscribeOnError: true); | |
| 853 return _controller.stream; | |
| 854 } | |
| 855 } | |
| 856 | 827 |
| 857 // Extends StreamConsumer as this is an internal type, only used to pipe to. | 828 // Extends StreamConsumer as this is an internal type, only used to pipe to. |
| 858 class _HttpOutgoing implements StreamConsumer<List<int>, dynamic> { | 829 class _HttpOutgoing implements StreamConsumer<List<int>, dynamic> { |
| 859 final _DataValidatorTransformer _validator = new _DataValidatorTransformer(); | |
| 860 Function _onStream; | 830 Function _onStream; |
| 861 final Completer _consumeCompleter = new Completer(); | 831 final Completer _consumeCompleter = new Completer(); |
| 862 | 832 |
| 863 Future onStream(Future callback(Stream<List<int>> stream)) { | 833 Future onStream(Future callback(Stream<List<int>> stream)) { |
| 864 _onStream = callback; | 834 _onStream = callback; |
| 865 return _consumeCompleter.future; | 835 return _consumeCompleter.future; |
| 866 } | 836 } |
| 867 | 837 |
| 868 void setTransferLength(int transferLength) { | |
| 869 _validator.expectedTransferLength = transferLength; | |
| 870 } | |
| 871 | |
| 872 Future consume(Stream<List<int>> stream) { | 838 Future consume(Stream<List<int>> stream) { |
| 873 _onStream(stream.transform(_validator)) | 839 _onStream(stream) |
| 874 .then((_) => _consumeCompleter.complete(), | 840 .then((_) => _consumeCompleter.complete(), |
| 875 onError: _consumeCompleter.completeError); | 841 onError: _consumeCompleter.completeError); |
| 876 // Use .then to ensure a Future branch. | 842 // Use .then to ensure a Future branch. |
| 877 return _consumeCompleter.future.then((_) => this); | 843 return _consumeCompleter.future.then((_) => this); |
| 878 } | 844 } |
| 879 } | 845 } |
| 880 | 846 |
| 881 | 847 |
| 882 class _HttpClientConnection { | 848 class _HttpClientConnection { |
| 883 final String key; | 849 final String key; |
| (...skipping 820 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1704 | 1670 |
| 1705 | 1671 |
| 1706 class _RedirectInfo implements RedirectInfo { | 1672 class _RedirectInfo implements RedirectInfo { |
| 1707 const _RedirectInfo(int this.statusCode, | 1673 const _RedirectInfo(int this.statusCode, |
| 1708 String this.method, | 1674 String this.method, |
| 1709 Uri this.location); | 1675 Uri this.location); |
| 1710 final int statusCode; | 1676 final int statusCode; |
| 1711 final String method; | 1677 final String method; |
| 1712 final Uri location; | 1678 final Uri location; |
| 1713 } | 1679 } |
| OLD | NEW |