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