Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5)

Side by Side Diff: sdk/lib/io/http_impl.dart

Issue 12758009: Support GZip encoding on the http server. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Revivew fixes Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « sdk/lib/io/http_headers.dart ('k') | sdk/lib/io/io_sink.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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 }
OLDNEW
« no previous file with comments | « sdk/lib/io/http_headers.dart ('k') | sdk/lib/io/io_sink.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698