| 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 431 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 442 _ioSink = new IOSink(new _HttpOutboundConsumer(_ioSink, _consume, asGZip)); | 442 _ioSink = new IOSink(new _HttpOutboundConsumer(_ioSink, _consume, asGZip)); |
| 443 _ioSink.encoding = encoding; | 443 _ioSink.encoding = encoding; |
| 444 } | 444 } |
| 445 | 445 |
| 446 Future _consume(IOSink ioSink, Stream<List<int>> stream, bool asGZip) { | 446 Future _consume(IOSink ioSink, Stream<List<int>> stream, bool asGZip) { |
| 447 int contentLength = headers.contentLength; | 447 int contentLength = headers.contentLength; |
| 448 if (_ignoreBody) { | 448 if (_ignoreBody) { |
| 449 ioSink.close(); | 449 ioSink.close(); |
| 450 return stream.reduce(null, (x, y) {}).then((_) => this); | 450 return stream.reduce(null, (x, y) {}).then((_) => this); |
| 451 } | 451 } |
| 452 stream = stream.transform(new _BufferTransformer()); |
| 452 if (headers.chunkedTransferEncoding) { | 453 if (headers.chunkedTransferEncoding) { |
| 453 if (asGZip) { | 454 if (asGZip) { |
| 454 stream = stream.transform(new ZLibDeflater(gzip: true, level: 6)); | 455 stream = stream.transform(new ZLibDeflater(gzip: true, level: 6)); |
| 455 } | 456 } |
| 456 stream = stream.transform(new _ChunkedTransformer()); | 457 stream = stream.transform(new _ChunkedTransformer()); |
| 457 } else if (contentLength >= 0) { | 458 } else if (contentLength >= 0) { |
| 458 stream = stream.transform(new _ContentLengthValidator(contentLength)); | 459 stream = stream.transform(new _ContentLengthValidator(contentLength)); |
| 459 } | 460 } |
| 460 return stream.pipe(ioSink).then((_) => this); | 461 return stream.pipe(ioSink).then((_) => this); |
| 461 } | 462 } |
| 462 | 463 |
| 463 void _writeHeader(); // TODO(ajohnsen): Better name. | 464 void _writeHeader(); // TODO(ajohnsen): Better name. |
| 464 } | 465 } |
| 465 | 466 |
| 466 | 467 |
| 467 class _HttpOutboundConsumer implements StreamConsumer { | 468 class _HttpOutboundConsumer implements StreamConsumer { |
| 468 Function _consume; | 469 Function _consume; |
| 469 IOSink _ioSink; | 470 IOSink _ioSink; |
| 470 bool _asGZip; | 471 bool _asGZip; |
| 471 _HttpOutboundConsumer(IOSink this._ioSink, | 472 _HttpOutboundConsumer(IOSink this._ioSink, |
| 472 Function this._consume, | 473 Function this._consume, |
| 473 bool this._asGZip); | 474 bool this._asGZip); |
| 474 | 475 |
| 475 Future consume(var stream) => _consume(_ioSink, stream, _asGZip); | 476 Future consume(var stream) => _consume(_ioSink, stream, _asGZip); |
| 476 } | 477 } |
| 477 | 478 |
| 478 | 479 |
| 480 class _BufferTransformer extends StreamEventTransformer<List<int>, List<int>> { |
| 481 const int MIN_CHUNK_SIZE = 4 * 1024; |
| 482 const int MAX_BUFFER_SIZE = 16 * 1024; |
| 483 |
| 484 final _BufferList _buffer = new _BufferList(); |
| 485 |
| 486 void handleData(List<int> data, EventSink<List<int>> sink) { |
| 487 // TODO(ajohnsen): Use timeout? |
| 488 if (data.length == 0) return; |
| 489 if (data.length >= MIN_CHUNK_SIZE) { |
| 490 flush(sink); |
| 491 sink.add(data); |
| 492 } else { |
| 493 _buffer.add(data); |
| 494 if (_buffer.length >= MAX_BUFFER_SIZE) { |
| 495 flush(sink); |
| 496 } |
| 497 } |
| 498 } |
| 499 |
| 500 void handleDone(EventSink<List<int>> sink) { |
| 501 flush(sink); |
| 502 sink.close(); |
| 503 } |
| 504 |
| 505 void flush(EventSink<List<int>> sink) { |
| 506 if (_buffer.length > 0) { |
| 507 sink.add(_buffer.readBytes()); |
| 508 _buffer.clear(); |
| 509 } |
| 510 } |
| 511 } |
| 512 |
| 513 |
| 479 class _HttpResponse extends _HttpOutboundMessage<HttpResponse> | 514 class _HttpResponse extends _HttpOutboundMessage<HttpResponse> |
| 480 implements HttpResponse { | 515 implements HttpResponse { |
| 481 int statusCode = 200; | 516 int statusCode = 200; |
| 482 String _reasonPhrase; | 517 String _reasonPhrase; |
| 483 List<Cookie> _cookies; | 518 List<Cookie> _cookies; |
| 484 _HttpRequest _httpRequest; | 519 _HttpRequest _httpRequest; |
| 485 | 520 |
| 486 _HttpResponse(String protocolVersion, | 521 _HttpResponse(String protocolVersion, |
| 487 _HttpOutgoing _outgoing) | 522 _HttpOutgoing _outgoing) |
| 488 : super(protocolVersion, _outgoing); | 523 : super(protocolVersion, _outgoing); |
| (...skipping 18 matching lines...) Expand all Loading... |
| 507 done.catchError((_) { | 542 done.catchError((_) { |
| 508 // Catch any error on done, as they automatically will be propegated to | 543 // Catch any error on done, as they automatically will be propegated to |
| 509 // the websocket. | 544 // the websocket. |
| 510 }); | 545 }); |
| 511 return future; | 546 return future; |
| 512 } | 547 } |
| 513 | 548 |
| 514 HttpConnectionInfo get connectionInfo => _httpRequest.connectionInfo; | 549 HttpConnectionInfo get connectionInfo => _httpRequest.connectionInfo; |
| 515 | 550 |
| 516 void _writeHeader() { | 551 void _writeHeader() { |
| 517 writeSP() => _ioSink.writeBytes([_CharCode.SP]); | 552 var buffer = new _BufferList(); |
| 518 writeCRLF() => _ioSink.writeBytes([_CharCode.CR, _CharCode.LF]); | 553 writeSP() => buffer.add(const [_CharCode.SP]); |
| 554 writeCRLF() => buffer.add(const [_CharCode.CR, _CharCode.LF]); |
| 519 | 555 |
| 520 // Write status line. | 556 // Write status line. |
| 521 if (headers.protocolVersion == "1.1") { | 557 if (headers.protocolVersion == "1.1") { |
| 522 _ioSink.writeBytes(_Const.HTTP11); | 558 buffer.add(_Const.HTTP11); |
| 523 } else { | 559 } else { |
| 524 _ioSink.writeBytes(_Const.HTTP10); | 560 buffer.add(_Const.HTTP10); |
| 525 } | 561 } |
| 526 writeSP(); | 562 writeSP(); |
| 527 _ioSink.write(statusCode.toString()); | 563 buffer.add(statusCode.toString().codeUnits); |
| 528 writeSP(); | 564 writeSP(); |
| 529 _ioSink.write(reasonPhrase); | 565 buffer.add(reasonPhrase.codeUnits); |
| 530 writeCRLF(); | 566 writeCRLF(); |
| 531 | 567 |
| 532 var session = _httpRequest._session; | 568 var session = _httpRequest._session; |
| 533 if (session != null && !session._destroyed) { | 569 if (session != null && !session._destroyed) { |
| 534 // Mark as not new. | 570 // Mark as not new. |
| 535 session._isNew = false; | 571 session._isNew = false; |
| 536 // Make sure we only send the current session id. | 572 // Make sure we only send the current session id. |
| 537 bool found = false; | 573 bool found = false; |
| 538 for (int i = 0; i < cookies.length; i++) { | 574 for (int i = 0; i < cookies.length; i++) { |
| 539 if (cookies[i].name.toUpperCase() == _DART_SESSION_ID) { | 575 if (cookies[i].name.toUpperCase() == _DART_SESSION_ID) { |
| (...skipping 13 matching lines...) Expand all Loading... |
| 553 // Add all the cookies set to the headers. | 589 // Add all the cookies set to the headers. |
| 554 if (_cookies != null) { | 590 if (_cookies != null) { |
| 555 _cookies.forEach((cookie) { | 591 _cookies.forEach((cookie) { |
| 556 headers.add(HttpHeaders.SET_COOKIE, cookie); | 592 headers.add(HttpHeaders.SET_COOKIE, cookie); |
| 557 }); | 593 }); |
| 558 } | 594 } |
| 559 | 595 |
| 560 headers._finalize(); | 596 headers._finalize(); |
| 561 | 597 |
| 562 // Write headers. | 598 // Write headers. |
| 563 headers._write(_ioSink); | 599 headers._write(buffer); |
| 564 writeCRLF(); | 600 writeCRLF(); |
| 601 _ioSink.writeBytes(buffer.readBytes()); |
| 565 } | 602 } |
| 566 | 603 |
| 567 String _findReasonPhrase(int statusCode) { | 604 String _findReasonPhrase(int statusCode) { |
| 568 if (_reasonPhrase != null) { | 605 if (_reasonPhrase != null) { |
| 569 return _reasonPhrase; | 606 return _reasonPhrase; |
| 570 } | 607 } |
| 571 | 608 |
| 572 switch (statusCode) { | 609 switch (statusCode) { |
| 573 case HttpStatus.CONTINUE: return "Continue"; | 610 case HttpStatus.CONTINUE: return "Continue"; |
| 574 case HttpStatus.SWITCHING_PROTOCOLS: return "Switching Protocols"; | 611 case HttpStatus.SWITCHING_PROTOCOLS: return "Switching Protocols"; |
| (...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 703 onError: (e) { | 740 onError: (e) { |
| 704 _responseCompleter.completeError(e); | 741 _responseCompleter.completeError(e); |
| 705 }); | 742 }); |
| 706 } | 743 } |
| 707 | 744 |
| 708 void _onError(AsyncError error) { | 745 void _onError(AsyncError error) { |
| 709 _responseCompleter.completeError(error); | 746 _responseCompleter.completeError(error); |
| 710 } | 747 } |
| 711 | 748 |
| 712 void _writeHeader() { | 749 void _writeHeader() { |
| 713 writeSP() => _ioSink.writeBytes([_CharCode.SP]); | 750 var buffer = new _BufferList(); |
| 714 writeCRLF() => _ioSink.writeBytes([_CharCode.CR, _CharCode.LF]); | 751 writeSP() => buffer.add(const [_CharCode.SP]); |
| 752 writeCRLF() => buffer.add(const [_CharCode.CR, _CharCode.LF]); |
| 715 | 753 |
| 716 _ioSink.write(method); | 754 buffer.add(method.codeUnits); |
| 717 writeSP(); | 755 writeSP(); |
| 718 // Send the path for direct connections and the whole URL for | 756 // Send the path for direct connections and the whole URL for |
| 719 // proxy connections. | 757 // proxy connections. |
| 720 if (!_usingProxy) { | 758 if (!_usingProxy) { |
| 721 String path = uri.path; | 759 String path = uri.path; |
| 722 if (path.length == 0) path = "/"; | 760 if (path.length == 0) path = "/"; |
| 723 if (uri.query != "") { | 761 if (uri.query != "") { |
| 724 if (uri.fragment != "") { | 762 if (uri.fragment != "") { |
| 725 path = "${path}?${uri.query}#${uri.fragment}"; | 763 path = "${path}?${uri.query}#${uri.fragment}"; |
| 726 } else { | 764 } else { |
| 727 path = "${path}?${uri.query}"; | 765 path = "${path}?${uri.query}"; |
| 728 } | 766 } |
| 729 } | 767 } |
| 730 _ioSink.write(path); | 768 buffer.add(path.codeUnits); |
| 731 } else { | 769 } else { |
| 732 _ioSink.write(uri.toString()); | 770 buffer.add(uri.toString().codeUnits); |
| 733 } | 771 } |
| 734 writeSP(); | 772 writeSP(); |
| 735 _ioSink.writeBytes(_Const.HTTP11); | 773 buffer.add(_Const.HTTP11); |
| 736 writeCRLF(); | 774 writeCRLF(); |
| 737 | 775 |
| 738 // Add the cookies to the headers. | 776 // Add the cookies to the headers. |
| 739 if (!cookies.isEmpty) { | 777 if (!cookies.isEmpty) { |
| 740 StringBuffer sb = new StringBuffer(); | 778 StringBuffer sb = new StringBuffer(); |
| 741 for (int i = 0; i < cookies.length; i++) { | 779 for (int i = 0; i < cookies.length; i++) { |
| 742 if (i > 0) sb.write("; "); | 780 if (i > 0) sb.write("; "); |
| 743 sb.write(cookies[i].name); | 781 sb.write(cookies[i].name); |
| 744 sb.write("="); | 782 sb.write("="); |
| 745 sb.write(cookies[i].value); | 783 sb.write(cookies[i].value); |
| 746 } | 784 } |
| 747 headers.add(HttpHeaders.COOKIE, sb.toString()); | 785 headers.add(HttpHeaders.COOKIE, sb.toString()); |
| 748 } | 786 } |
| 749 | 787 |
| 750 headers._finalize(); | 788 headers._finalize(); |
| 751 | 789 |
| 752 // Write headers. | 790 // Write headers. |
| 753 headers._write(_ioSink); | 791 headers._write(buffer); |
| 754 writeCRLF(); | 792 writeCRLF(); |
| 793 _ioSink.writeBytes(buffer.readBytes()); |
| 755 } | 794 } |
| 756 } | 795 } |
| 757 | 796 |
| 758 | 797 |
| 759 // Transformer that transforms data to HTTP Chunked Encoding. | 798 // Transformer that transforms data to HTTP Chunked Encoding. |
| 760 class _ChunkedTransformer extends StreamEventTransformer<List<int>, List<int>> { | 799 class _ChunkedTransformer extends StreamEventTransformer<List<int>, List<int>> { |
| 761 void handleData(List<int> data, EventSink<List<int>> sink) { | 800 void handleData(List<int> data, EventSink<List<int>> sink) { |
| 762 sink.add(_chunkHeader(data.length)); | 801 sink.add(_chunkHeader(data.length)); |
| 763 if (data.length > 0) sink.add(data); | 802 if (data.length > 0) sink.add(data); |
| 764 sink.add(_chunkFooter); | 803 sink.add(_chunkFooter); |
| (...skipping 905 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1670 | 1709 |
| 1671 | 1710 |
| 1672 class _RedirectInfo implements RedirectInfo { | 1711 class _RedirectInfo implements RedirectInfo { |
| 1673 const _RedirectInfo(int this.statusCode, | 1712 const _RedirectInfo(int this.statusCode, |
| 1674 String this.method, | 1713 String this.method, |
| 1675 Uri this.location); | 1714 Uri this.location); |
| 1676 final int statusCode; | 1715 final int statusCode; |
| 1677 final String method; | 1716 final String method; |
| 1678 final Uri location; | 1717 final Uri location; |
| 1679 } | 1718 } |
| OLD | NEW |