From aa2ce7cfbfaca5439b86b6ee4ad503691b8ce0ac Mon Sep 17 00:00:00 2001 From: Todd Volkert Date: Thu, 30 May 2019 21:57:35 +0000 Subject: [PATCH] Add `compressionState` getter to the HttpClientResponse API. Bug: dartbug.com/36971 Change-Id: I7dc0b48fe8eddb2f49a73efb9c4c6aba1233179b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/102800 Commit-Queue: Todd Volkert Reviewed-by: Zach Anderson --- CHANGELOG.md | 13 ++++++++-- sdk/lib/_http/http.dart | 49 ++++++++++++++++++++++++++++++++++++ sdk/lib/_http/http_impl.dart | 20 +++++++++++---- 3 files changed, 75 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff13f2f69cdf..5c2f564e3556 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,8 +31,17 @@ class B extends A {}; cookie values to be the empty string (Issue [35804][]) and not stripping double quotes from the value (Issue [33327][]) in accordance with RFC 6265. -[33327]: https://github.com/dart-lang/sdk/issues/33327 -[35804]: https://github.com/dart-lang/sdk/issues/35804 + [33327]: https://github.com/dart-lang/sdk/issues/33327 + [35804]: https://github.com/dart-lang/sdk/issues/35804 + +* The `HttpClientResponse` interface has been extended with the addition of a + new `compressionState` getter, which specifies whether the body of a + response was compressed when it was received and whether it has been + automatically uncompressed via `HttpClient.autoUncompress`. + + * **Breaking change**: For those implementing the `HttpClientResponse` + interface, this is a breaking change, as implementing classes will need to + implement the new getter. ### Dart VM diff --git a/sdk/lib/_http/http.dart b/sdk/lib/_http/http.dart index f1e72be4f990..a77ceba4a648 100644 --- a/sdk/lib/_http/http.dart +++ b/sdk/lib/_http/http.dart @@ -1958,6 +1958,13 @@ abstract class HttpClientResponse implements Stream> { */ int get contentLength; + /// The compression state of the response. + /// + /// This specifies whether the response bytes were compressed when they were + /// received across the wire and whether callers will receive compressed + /// or uncompressed bytes when they listed to this response's byte stream. + HttpClientResponseCompressionState get compressionState; + /** * Gets the persistent connection state returned by the server. * @@ -2037,6 +2044,48 @@ abstract class HttpClientResponse implements Stream> { HttpConnectionInfo get connectionInfo; } +/// Enum that specifies the compression state of the byte stream of an +/// [HttpClientResponse]. +/// +/// The values herein allow callers to answer the following questions as they +/// pertain to an [HttpClientResponse]: +/// +/// * Can the value of the response's `Content-Length` HTTP header be trusted? +/// * Does the caller need to manually decompress the response's byte stream? +/// +/// This enum is accessed via the [HttpClientResponse.compressionState] value. +enum HttpClientResponseCompressionState { + /// The body of the HTTP response was received and remains in an uncompressed + /// state. + /// + /// In this state, the value of the `Content-Length` HTTP header, if + /// specified (non-negative), should match the number of bytes produced by + /// the response's byte stream. + notCompressed, + + /// The body of the HTTP response was originally compressed, but by virtue of + /// the [HttpClient.autoUncompress] configuration option, it has been + /// automatically uncompressed. + /// + /// HTTP headers are not modified, so when a response has been uncompressed + /// in this way, the value of the `Content-Length` HTTP header cannot be + /// trusted, as it will contain the compressed content length, whereas the + /// stream of bytes produced by the response will contain uncompressed bytes. + decompressed, + + /// The body of the HTTP response contains compressed bytes. + /// + /// In this state, the value of the `Content-Length` HTTP header, if + /// specified (non-negative), should match the number of bytes produced by + /// the response's byte stream. + /// + /// If the caller wishes to manually uncompress the body of the response, + /// it should consult the value of the `Content-Encoding` HTTP header to see + /// what type of compression has been applied. See + /// for more information. + compressed, +} + abstract class HttpClientCredentials {} /** diff --git a/sdk/lib/_http/http_impl.dart b/sdk/lib/_http/http_impl.dart index a70ca822afd5..f565489be0a6 100644 --- a/sdk/lib/_http/http_impl.dart +++ b/sdk/lib/_http/http_impl.dart @@ -292,17 +292,28 @@ class _HttpClientResponse extends _HttpInboundMessage // The HttpClientRequest of this response. final _HttpClientRequest _httpRequest; - // Whether this response is configured to auto uncompress. - final bool autoUncompress; + // The compression state of this response. + final HttpClientResponseCompressionState compressionState; _HttpClientResponse( _HttpIncoming _incoming, this._httpRequest, this._httpClient) - : autoUncompress = _httpClient.autoUncompress, + : compressionState = _getCompressionState(_httpClient, _incoming.headers), super(_incoming) { // Set uri for potential exceptions. _incoming.uri = _httpRequest.uri; } + static HttpClientResponseCompressionState _getCompressionState( + _HttpClient httpClient, _HttpHeaders headers) { + if (headers.value(HttpHeaders.contentEncodingHeader) == "gzip") { + return httpClient.autoUncompress + ? HttpClientResponseCompressionState.decompressed + : HttpClientResponseCompressionState.compressed; + } else { + return HttpClientResponseCompressionState.notCompressed; + } + } + int get statusCode => _incoming.statusCode; String get reasonPhrase => _incoming.reasonPhrase; @@ -381,8 +392,7 @@ class _HttpClientResponse extends _HttpInboundMessage return new Stream>.empty().listen(null, onDone: onDone); } Stream> stream = _incoming; - if (autoUncompress && - headers.value(HttpHeaders.contentEncodingHeader) == "gzip") { + if (compressionState == HttpClientResponseCompressionState.decompressed) { stream = stream.transform(gzip.decoder); } return stream.listen(onData,