diff --git a/AUTHORS b/AUTHORS index 26cc1e9641..fd1df224fe 100644 --- a/AUTHORS +++ b/AUTHORS @@ -21,3 +21,4 @@ Yusuke Konishi Fredrik Simón Ali Bitek Tetsuhiro Ueda +Dan Field diff --git a/packages/flutter/lib/src/foundation/consolidate_response.dart b/packages/flutter/lib/src/foundation/consolidate_response.dart index 183117ed9e..cd6f1589fd 100644 --- a/packages/flutter/lib/src/foundation/consolidate_response.dart +++ b/packages/flutter/lib/src/foundation/consolidate_response.dart @@ -10,39 +10,24 @@ import 'dart:typed_data'; /// /// The future returned will forward all errors emitted by [response]. Future consolidateHttpClientResponseBytes(HttpClientResponse response) { - // dart:io guarantees that [contentLength] is -1 if the the header is missing or - // invalid. This could still happen if a mocked response object does not fully - // implement the interface. - assert(response.contentLength != null); + // response.contentLength is not trustworthy when GZIP is involved + // or other cases where an intermediate transformer has been applied + // to the stream. final Completer completer = new Completer.sync(); - if (response.contentLength == -1) { - final List> chunks = >[]; - int contentLength = 0; - response.listen((List chunk) { - chunks.add(chunk); - contentLength += chunk.length; - }, onDone: () { - final Uint8List bytes = new Uint8List(contentLength); - int offset = 0; - for (List chunk in chunks) { - bytes.setRange(offset, offset + chunk.length, chunk); - offset += chunk.length; - } - completer.complete(bytes); - }, onError: completer.completeError, cancelOnError: true); - } else { - // If the response has a content length, then allocate a buffer of the correct size. - final Uint8List bytes = new Uint8List(response.contentLength); + final List> chunks = >[]; + int contentLength = 0; + response.listen((List chunk) { + chunks.add(chunk); + contentLength += chunk.length; + }, onDone: () { + final Uint8List bytes = new Uint8List(contentLength); int offset = 0; - response.listen((List chunk) { + for (List chunk in chunks) { bytes.setRange(offset, offset + chunk.length, chunk); offset += chunk.length; - }, - onError: completer.completeError, - onDone: () { - completer.complete(bytes); - }, - cancelOnError: true); - } + } + completer.complete(bytes); + }, onError: completer.completeError, cancelOnError: true); + return completer.future; } diff --git a/packages/flutter/test/foundation/consolidate_response_test.dart b/packages/flutter/test/foundation/consolidate_response_test.dart index 184d9a29ef..c967197800 100644 --- a/packages/flutter/test/foundation/consolidate_response_test.dart +++ b/packages/flutter/test/foundation/consolidate_response_test.dart @@ -24,21 +24,40 @@ void main() { final void Function() onDone = invocation.namedArguments[#onDone]; final bool cancelOnError = invocation.namedArguments[#cancelOnError]; - return new Stream>.fromIterable(>[chunkOne, chunkTwo]) - .listen(onData, onDone: onDone, onError: onError, cancelOnError: cancelOnError); + return new Stream>.fromIterable( + >[chunkOne, chunkTwo]).listen( + onData, + onDone: onDone, + onError: onError, + cancelOnError: cancelOnError, + ); }); }); - test('Converts an HttpClientResponse with contentLength to bytes', () async { - when(response.contentLength).thenReturn(chunkOne.length + chunkTwo.length); - final List bytes = await consolidateHttpClientResponseBytes(response); + test('Converts an HttpClientResponse with contentLength to bytes', + () async { + when(response.contentLength) + .thenReturn(chunkOne.length + chunkTwo.length); + final List bytes = + await consolidateHttpClientResponseBytes(response); expect(bytes, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); }); - test('Converts an HttpClientResponse without contentLength to bytes', () async { + test('Converts a compressed HttpClientResponse with contentLength to bytes', + () async { + when(response.contentLength).thenReturn(chunkOne.length); + final List bytes = + await consolidateHttpClientResponseBytes(response); + + expect(bytes, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + }); + + test('Converts an HttpClientResponse without contentLength to bytes', + () async { when(response.contentLength).thenReturn(-1); - final List bytes = await consolidateHttpClientResponseBytes(response); + final List bytes = + await consolidateHttpClientResponseBytes(response); expect(bytes, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); }); @@ -55,12 +74,19 @@ void main() { final void Function() onDone = invocation.namedArguments[#onDone]; final bool cancelOnError = invocation.namedArguments[#cancelOnError]; - return new Stream>.fromFuture(new Future>.error(new Exception('Test Error'))) - .listen(onData, onDone: onDone, onError: onError, cancelOnError: cancelOnError); + return new Stream>.fromFuture( + new Future>.error(new Exception('Test Error'))) + .listen( + onData, + onDone: onDone, + onError: onError, + cancelOnError: cancelOnError, + ); }); when(response.contentLength).thenReturn(-1); - expect(consolidateHttpClientResponseBytes(response), throwsA(const isInstanceOf())); + expect(consolidateHttpClientResponseBytes(response), + throwsA(const isInstanceOf())); }); }); }