Skip to content

Commit 9dbbafb

Browse files
authored
Add tests for compressed responses (#823)
1 parent d56141d commit 9dbbafb

File tree

5 files changed

+174
-0
lines changed

5 files changed

+174
-0
lines changed

pkgs/http_client_conformance_tests/lib/http_client_conformance_tests.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'package:http/http.dart';
66

7+
import 'src/compressed_response_body_tests.dart';
78
import 'src/redirect_tests.dart';
89
import 'src/request_body_streamed_tests.dart';
910
import 'src/request_body_tests.dart';
@@ -13,6 +14,8 @@ import 'src/response_body_tests.dart';
1314
import 'src/response_headers_tests.dart';
1415
import 'src/server_errors_test.dart';
1516

17+
export 'src/compressed_response_body_tests.dart'
18+
show testCompressedResponseBody;
1619
export 'src/redirect_tests.dart' show testRedirect;
1720
export 'src/request_body_streamed_tests.dart' show testRequestBodyStreamed;
1821
export 'src/request_body_tests.dart' show testRequestBody;
@@ -51,4 +54,5 @@ void testAll(Client client,
5154
testResponseHeaders(client);
5255
testRedirect(client, redirectAlwaysAllowed: redirectAlwaysAllowed);
5356
testServerErrors(client);
57+
testCompressedResponseBody(client);
5458
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
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.
4+
5+
import 'dart:async';
6+
import 'dart:io';
7+
8+
import 'package:stream_channel/stream_channel.dart';
9+
10+
/// Starts an HTTP server that responds with "Hello World!"
11+
///
12+
/// Channel protocol:
13+
/// On Startup:
14+
/// - send port
15+
/// On Request Received:
16+
/// - send headers as Map<String, List<String>>
17+
/// When Receive Anything:
18+
/// - exit
19+
void hybridMain(StreamChannel<Object?> channel) async {
20+
final server = (await HttpServer.bind('localhost', 0))
21+
..listen((request) async {
22+
const message = 'Hello World!';
23+
late List<int> contents;
24+
25+
await request.drain<void>();
26+
27+
final headers = <String, List<String>>{};
28+
request.headers.forEach((field, value) {
29+
headers[field] = value;
30+
});
31+
channel.sink.add(headers);
32+
33+
request.response.headers.set('Access-Control-Allow-Origin', '*');
34+
request.response.headers.set('Content-Type', 'text/plain');
35+
36+
if (request.requestedUri.pathSegments.isNotEmpty) {
37+
if (request.requestedUri.pathSegments.last == 'gzip') {
38+
request.response.headers.set('Content-Encoding', 'gzip');
39+
contents = gzip.encode(message.codeUnits);
40+
}
41+
if (request.requestedUri.pathSegments.last == 'deflate') {
42+
request.response.headers.set('Content-Encoding', 'deflate');
43+
contents = zlib.encode(message.codeUnits);
44+
}
45+
if (request.requestedUri.pathSegments.last == 'upper') {
46+
request.response.headers.set('Content-Encoding', 'upper');
47+
contents = message.toUpperCase().codeUnits;
48+
}
49+
}
50+
51+
if (request.requestedUri.queryParameters.containsKey('length')) {
52+
request.response.contentLength = contents.length;
53+
}
54+
request.response.add(contents);
55+
await request.response.close();
56+
});
57+
58+
channel.sink.add(server.port);
59+
await channel
60+
.stream.first; // Any writes indicates that the server should exit.
61+
unawaited(server.close());
62+
}

pkgs/http_client_conformance_tests/lib/src/compressed_response_body_server_vm.dart

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkgs/http_client_conformance_tests/lib/src/compressed_response_body_server_web.dart

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
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.
4+
5+
import 'package:async/async.dart';
6+
import 'package:http/http.dart';
7+
import 'package:stream_channel/stream_channel.dart';
8+
import 'package:test/test.dart';
9+
10+
import 'compressed_response_body_server_vm.dart'
11+
if (dart.library.html) 'compressed_response_body_server_web.dart';
12+
13+
/// Tests that the [Client] correctly implements HTTP responses with compressed
14+
/// bodies.
15+
///
16+
/// If the response is encoded using a recognized 'Content-Encoding' then the
17+
/// [Client] must decode it. Otherwise it must return the content unchanged.
18+
///
19+
/// The 'Content-Encoding' and 'Content-Length' headers may be absent for
20+
/// responses with a 'Content-Encoding' and, if present, their values are
21+
/// undefined.
22+
///
23+
/// The value of `StreamedResponse.contentLength` is not defined for responses
24+
/// with a 'Content-Encoding' header.
25+
void testCompressedResponseBody(Client client) async {
26+
group('response body', () {
27+
late final String host;
28+
late final StreamChannel<Object?> httpServerChannel;
29+
late final StreamQueue<Object?> httpServerQueue;
30+
const message = 'Hello World!';
31+
32+
setUpAll(() async {
33+
httpServerChannel = await startServer();
34+
httpServerQueue = StreamQueue(httpServerChannel.stream);
35+
host = 'localhost:${await httpServerQueue.next}';
36+
});
37+
tearDownAll(() => httpServerChannel.sink.add(null));
38+
39+
test('gzip: small response with content length', () async {
40+
// Test a supported content encoding.
41+
final response = await client.get(Uri.http(host, '/gzip'));
42+
final requestHeaders = await httpServerQueue.next as Map;
43+
44+
expect((requestHeaders['accept-encoding'] as List).join(', '),
45+
contains('gzip'));
46+
expect(response.body, message);
47+
expect(response.bodyBytes, message.codeUnits);
48+
expect(response.contentLength, message.length);
49+
expect(response.headers['content-type'], 'text/plain');
50+
expect(response.isRedirect, isFalse);
51+
expect(response.reasonPhrase, 'OK');
52+
expect(response.request!.method, 'GET');
53+
expect(response.statusCode, 200);
54+
});
55+
56+
test('gzip: small response streamed with content length', () async {
57+
// Test a supported content encoding.
58+
final request = Request('GET', Uri.http(host, '/gzip', {'length': ''}));
59+
final response = await client.send(request);
60+
final requestHeaders = await httpServerQueue.next as Map;
61+
62+
expect((requestHeaders['accept-encoding'] as List).join(', '),
63+
contains('gzip'));
64+
expect(await response.stream.bytesToString(), message);
65+
expect(response.headers['content-type'], 'text/plain');
66+
expect(response.isRedirect, isFalse);
67+
expect(response.reasonPhrase, 'OK');
68+
expect(response.request!.method, 'GET');
69+
expect(response.statusCode, 200);
70+
});
71+
72+
test('upper: small response streamed with content length', () async {
73+
// Test an unsupported content encoding.
74+
final request = Request('GET', Uri.http(host, '/upper', {'length': ''}));
75+
final response = await client.send(request);
76+
await httpServerQueue.next;
77+
78+
expect(await response.stream.bytesToString(), message.toUpperCase());
79+
expect(response.headers['content-type'], 'text/plain');
80+
expect(response.isRedirect, isFalse);
81+
expect(response.reasonPhrase, 'OK');
82+
expect(response.request!.method, 'GET');
83+
expect(response.statusCode, 200);
84+
});
85+
});
86+
}

0 commit comments

Comments
 (0)