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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « sdk/lib/io/http_headers.dart ('k') | sdk/lib/io/io_sink.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sdk/lib/io/http_impl.dart
diff --git a/sdk/lib/io/http_impl.dart b/sdk/lib/io/http_impl.dart
index 625154aceb8f6e1f30b63a1430f7dbec0d52cf07..55445491b8d6c28110af30ee02d3b9e8ca1ea0a7 100644
--- a/sdk/lib/io/http_impl.dart
+++ b/sdk/lib/io/http_impl.dart
@@ -322,16 +322,15 @@ abstract class _HttpOutboundMessage<T> implements IOSink {
// requests and in error handling.
bool _ignoreBody = false;
bool _headersWritten = false;
- bool _chunked = false;
- final IOSink _ioSink;
+ IOSink _ioSink;
final _HttpOutgoing _outgoing;
final _HttpHeaders headers;
_HttpOutboundMessage(String protocolVersion, _HttpOutgoing outgoing)
: _outgoing = outgoing,
- _ioSink = new IOSink(outgoing, encoding: Encoding.ASCII),
+ _ioSink = new IOSink(outgoing, encoding: Encoding.ASCII),
headers = new _HttpHeaders(protocolVersion);
int get contentLength => headers.contentLength;
@@ -360,7 +359,6 @@ abstract class _HttpOutboundMessage<T> implements IOSink {
void write(Object obj) {
_writeHeaders();
- if (_ignoreBody) return;
// This comment is copied from runtime/lib/string_buffer_patch.dart.
// TODO(srdjan): The following four lines could be replaced by
// '$obj', but apparently this is too slow on the Dart VM.
@@ -374,12 +372,7 @@ abstract class _HttpOutboundMessage<T> implements IOSink {
}
}
if (string.isEmpty) return;
- if (_chunked) {
- _ChunkedTransformer._addChunk(_encodeString(string, encoding),
- _ioSink.writeBytes);
- } else {
- _ioSink.write(string);
- }
+ _ioSink.write(string);
}
void writeAll(Iterable objects) {
@@ -397,31 +390,17 @@ abstract class _HttpOutboundMessage<T> implements IOSink {
void writeBytes(List<int> data) {
_writeHeaders();
- if (_ignoreBody || data.length == 0) return;
- if (_chunked) {
- _ChunkedTransformer._addChunk(data, _ioSink.writeBytes);
- } else {
- _ioSink.writeBytes(data);
- }
+ if (data.length == 0) return;
+ _ioSink.writeBytes(data);
}
Future<T> consume(Stream<List<int>> stream) {
_writeHeaders();
- if (_ignoreBody) return new Future.immediate(this);
- if (_chunked) {
- // Transform when chunked.
- stream = stream.transform(new _ChunkedTransformer());
- }
- return _ioSink.consume(stream).then((_) => this);
+ return _ioSink.consume(stream);
}
Future<T> writeStream(Stream<List<int>> stream) {
_writeHeaders();
- if (_ignoreBody) return new Future.immediate(this);
- if (_chunked) {
- // Transform when chunked.
- stream = stream.transform(new _ChunkedTransformer(writeEnd: false));
- }
return _ioSink.writeStream(stream).then((_) => this);
}
@@ -429,39 +408,74 @@ abstract class _HttpOutboundMessage<T> implements IOSink {
if (!_headersWritten && !_ignoreBody && headers.chunkedTransferEncoding) {
// If no body was written, _ignoreBody is false (it's not a HEAD
// request) and the content-length is unspecified, set contentLength to 0.
+ headers.chunkedTransferEncoding = false;
headers.contentLength = 0;
}
_writeHeaders();
- if (!_ignoreBody) {
- if (_chunked) {
- _ChunkedTransformer._addChunk([], _ioSink.writeBytes);
- }
- }
_ioSink.close();
}
- Future<T> get done => _ioSink.done.then((_) => this);
+ Future<T> get done {
+ _writeHeaders();
+ return _ioSink.done;
+ }
void _writeHeaders() {
if (_headersWritten) return;
_headersWritten = true;
_ioSink.encoding = Encoding.ASCII;
+ headers._synchronize(); // Be sure the 'chunked' option is updated.
+ bool asGZip = false;
+ bool isServerSide = this is _HttpResponse;
+ if (isServerSide && headers.chunkedTransferEncoding) {
+ List acceptEncodings =
+ _httpRequest.headers[HttpHeaders.ACCEPT_ENCODING];
+ List contentEncoding = headers[HttpHeaders.CONTENT_ENCODING];
+ if (acceptEncodings != null &&
+ acceptEncodings.any((encoding) => encoding.toLowerCase() == "gzip") &&
+ contentEncoding == null) {
+ headers.set(HttpHeaders.CONTENT_ENCODING, "gzip");
+ asGZip = true;
+ }
+ }
_writeHeader();
+ _ioSink = new IOSink(new _HttpOutboundConsumer(_ioSink, _consume, asGZip));
_ioSink.encoding = encoding;
+ }
+
+ Future _consume(IOSink ioSink, Stream<List<int>> stream, bool asGZip) {
+ int contentLength = headers.contentLength;
if (_ignoreBody) {
- _ioSink.close();
- return;
+ ioSink.close();
+ return stream.reduce(null, (x, y) {}).then((_) => this);
}
- _chunked = headers.chunkedTransferEncoding;
- if (headers.contentLength >= 0) {
- _outgoing.setTransferLength(headers.contentLength);
+ if (headers.chunkedTransferEncoding) {
+ if (asGZip) {
+ stream = stream.transform(new ZLibDeflater(gzip: true, level: 6));
+ }
+ stream = stream.transform(new _ChunkedTransformer());
+ } else if (contentLength >= 0) {
+ stream = stream.transform(new _ContentLengthValidator(contentLength));
}
+ return stream.pipe(ioSink).then((_) => this);
}
void _writeHeader(); // TODO(ajohnsen): Better name.
}
+class _HttpOutboundConsumer implements StreamConsumer {
+ Function _consume;
+ IOSink _ioSink;
+ bool _asGZip;
+ _HttpOutboundConsumer(IOSink this._ioSink,
+ Function this._consume,
+ bool this._asGZip);
+
+ Future consume(var stream) => _consume(_ioSink, stream, _asGZip);
+}
+
+
class _HttpResponse extends _HttpOutboundMessage<HttpResponse>
implements HttpResponse {
int statusCode = 200;
@@ -744,26 +758,17 @@ class _HttpClientRequest extends _HttpOutboundMessage<HttpClientRequest>
// Transformer that transforms data to HTTP Chunked Encoding.
class _ChunkedTransformer extends StreamEventTransformer<List<int>, List<int>> {
- final bool writeEnd;
- _ChunkedTransformer({this.writeEnd: true});
-
void handleData(List<int> data, EventSink<List<int>> sink) {
- _addChunk(data, sink.add);
+ sink.add(_chunkHeader(data.length));
+ if (data.length > 0) sink.add(data);
+ sink.add(_chunkFooter);
}
void handleDone(EventSink<List<int>> sink) {
- if (writeEnd) {
- _addChunk([], sink.add);
- }
+ handleData([], sink);
sink.close();
}
- static void _addChunk(List<int> data, void add(List<int> data)) {
- add(_chunkHeader(data.length));
- if (data.length > 0) add(data);
- add(_chunkFooter);
- }
-
static List<int> _chunkHeader(int length) {
const hexDigits = const [0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46];
@@ -786,77 +791,42 @@ class _ChunkedTransformer extends StreamEventTransformer<List<int>, List<int>> {
}
-// Transformer that invokes [_onDone] when completed.
-class _DoneTransformer implements StreamTransformer<List<int>, List<int>> {
- final StreamController<List<int>> _controller
- = new StreamController<List<int>>();
- final Function _onDone;
+// Transformer that validates the content length.
+class _ContentLengthValidator
+ extends StreamEventTransformer<List<int>, List<int>> {
+ final int expectedContentLength;
+ int _bytesWritten = 0;
- _DoneTransformer(this._onDone);
+ _ContentLengthValidator(int this.expectedContentLength);
- Stream<List<int>> bind(Stream<List<int>> stream) {
- var subscription = stream.listen(
- _controller.add,
- onError: _controller.addError,
- onDone: () {
- _onDone();
- _controller.close();
- });
- return _controller.stream;
+ void handleData(List<int> data, EventSink<List<int>> sink) {
+ _bytesWritten += data.length;
+ if (_bytesWritten > expectedContentLength) {
+ sink.addError(new AsyncError(new HttpException(
+ "Content size exceeds specified contentLength. "
+ "$_bytesWritten bytes written while expected "
+ "$expectedContentLength. "
+ "[${new String.fromCharCodes(data)}]")));
+ sink.close();
+ } else {
+ sink.add(data);
+ }
}
-}
-
-// Transformer that validates the data written.
-class _DataValidatorTransformer
- implements StreamTransformer<List<int>, List<int>> {
- final StreamController<List<int>> _controller =
- new StreamController<List<int>>();
- int _bytesWritten = 0;
- int expectedTransferLength;
-
- Stream<List<int>> bind(Stream<List<int>> stream) {
- var subscription;
- subscription = stream.listen(
- (data) {
- if (expectedTransferLength != null) {
- _bytesWritten += data.length;
- if (_bytesWritten > expectedTransferLength) {
- subscription.cancel();
- _controller.addError(new HttpException(
- "Content size exceeds specified contentLength. "
- "$_bytesWritten bytes written while expected "
- "$expectedTransferLength. "
- "[${new String.fromCharCodes(data)}]"));
- _controller.close();
- return;
- }
- }
- _controller.add(data);
- },
- onError: (error) {
- _controller.addError(error);
- _controller.close();
- },
- onDone: () {
- if (expectedTransferLength != null) {
- if (_bytesWritten < expectedTransferLength) {
- _controller.addError(new HttpException(
- "Content size below specified contentLength. "
- " $_bytesWritten bytes written while expected "
- "$expectedTransferLength."));
- }
- }
- _controller.close();
- },
- unsubscribeOnError: true);
- return _controller.stream;
+ void handleDone(EventSink<List<int>> sink) {
+ if (_bytesWritten < expectedContentLength) {
+ sink.addError(new AsyncError(new HttpException(
+ "Content size below specified contentLength. "
+ " $_bytesWritten bytes written while expected "
+ "$expectedContentLength.")));
+ }
+ sink.close();
}
}
+
// Extends StreamConsumer as this is an internal type, only used to pipe to.
class _HttpOutgoing implements StreamConsumer<List<int>, dynamic> {
- final _DataValidatorTransformer _validator = new _DataValidatorTransformer();
Function _onStream;
final Completer _consumeCompleter = new Completer();
@@ -865,12 +835,8 @@ class _HttpOutgoing implements StreamConsumer<List<int>, dynamic> {
return _consumeCompleter.future;
}
- void setTransferLength(int transferLength) {
- _validator.expectedTransferLength = transferLength;
- }
-
Future consume(Stream<List<int>> stream) {
- _onStream(stream.transform(_validator))
+ _onStream(stream)
.then((_) => _consumeCompleter.complete(),
onError: _consumeCompleter.completeError);
// Use .then to ensure a Future branch.
« 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