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 |