Skip to content

Commit 9f1416e

Browse files
authored
A browser tests for request headers. (#715)
1 parent 557d9a3 commit 9f1416e

File tree

3 files changed

+84
-60
lines changed

3 files changed

+84
-60
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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 captures the request headers.
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+
late HttpServer server;
21+
22+
server = (await HttpServer.bind('localhost', 0))
23+
..listen((request) async {
24+
request.response.headers.set('Access-Control-Allow-Origin', '*');
25+
if (request.method == 'OPTIONS') {
26+
// Handle a CORS preflight request:
27+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#preflighted_requests
28+
request.response.headers
29+
..set('Access-Control-Allow-Methods', 'GET')
30+
..set('Access-Control-Allow-Headers', '*');
31+
} else {
32+
final headers = <String, List<String>>{};
33+
request.headers.forEach((field, value) {
34+
headers[field] = value;
35+
});
36+
channel.sink.add(headers);
37+
}
38+
unawaited(request.response.close());
39+
});
40+
41+
channel.sink.add(server.port);
42+
await channel
43+
.stream.first; // Any writes indicates that the server should exit.
44+
unawaited(server.close());
45+
}

pkgs/http_client_conformance_tests/lib/src/request_headers_tests.dart

Lines changed: 38 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2,92 +2,70 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
import 'dart:async';
6-
import 'dart:io';
7-
5+
import 'package:async/async.dart';
86
import 'package:http/http.dart';
7+
import 'package:stream_channel/stream_channel.dart';
98
import 'package:test/test.dart';
109

10+
import 'utils.dart';
11+
1112
/// Tests that the [Client] correctly sends headers in the request.
1213
void testRequestHeaders(Client client) async {
1314
group('client headers', () {
15+
late final String host;
16+
late final StreamChannel<Object?> httpServerChannel;
17+
late final StreamQueue<Object?> httpServerQueue;
18+
19+
setUpAll(() async {
20+
httpServerChannel = await startServer('request_headers_server.dart');
21+
httpServerQueue = StreamQueue(httpServerChannel.stream);
22+
host = 'localhost:${await httpServerQueue.next}';
23+
});
24+
tearDownAll(() => httpServerChannel.sink.add(null));
25+
1426
test('single header', () async {
15-
late HttpHeaders requestHeaders;
16-
final server = (await HttpServer.bind('localhost', 0))
17-
..listen((request) async {
18-
await request.drain<void>();
19-
requestHeaders = request.headers;
20-
unawaited(request.response.close());
21-
});
22-
await client.get(Uri.http('localhost:${server.port}', ''),
23-
headers: {'foo': 'bar'});
24-
expect(requestHeaders['foo'], ['bar']);
25-
await server.close();
27+
await client.get(Uri.http(host, ''), headers: {'foo': 'bar'});
28+
29+
final headers = await httpServerQueue.next as Map;
30+
expect(headers['foo'], ['bar']);
2631
});
2732

2833
test('UPPER case header', () async {
29-
late HttpHeaders requestHeaders;
30-
final server = (await HttpServer.bind('localhost', 0))
31-
..listen((request) async {
32-
await request.drain<void>();
33-
requestHeaders = request.headers;
34-
unawaited(request.response.close());
35-
});
36-
await client.get(Uri.http('localhost:${server.port}', ''),
37-
headers: {'FOO': 'BAR'});
34+
await client.get(Uri.http(host, ''), headers: {'FOO': 'BAR'});
35+
36+
final headers = await httpServerQueue.next as Map;
3837
// RFC 2616 14.44 states that header field names are case-insensive.
3938
// http.Client canonicalizes field names into lower case.
40-
expect(requestHeaders['foo'], ['BAR']);
41-
await server.close();
39+
expect(headers['foo'], ['BAR']);
4240
});
4341

4442
test('test headers different only in case', () async {
45-
// RFC 2616 14.44 states that header field names are case-insensive.
46-
late HttpHeaders requestHeaders;
47-
final server = (await HttpServer.bind('localhost', 0))
48-
..listen((request) async {
49-
await request.drain<void>();
50-
requestHeaders = request.headers;
51-
unawaited(request.response.close());
52-
});
53-
await client.get(Uri.http('localhost:${server.port}', ''),
54-
headers: {'foo': 'bar', 'Foo': 'Bar'});
55-
expect(requestHeaders['foo']!.single, isIn(['bar', 'Bar']));
56-
await server.close();
43+
await client
44+
.get(Uri.http(host, ''), headers: {'foo': 'bar', 'Foo': 'Bar'});
45+
46+
final headers = await httpServerQueue.next as Map;
47+
// ignore: avoid_dynamic_calls
48+
expect(headers['foo']!.single, isIn(['bar', 'Bar']));
5749
});
5850

5951
test('multiple headers', () async {
60-
late HttpHeaders requestHeaders;
61-
final server = (await HttpServer.bind('localhost', 0))
62-
..listen((request) async {
63-
await request.drain<void>();
64-
requestHeaders = request.headers;
65-
unawaited(request.response.close());
66-
});
6752
// The `http.Client` API does not offer a way of sending the name field
6853
// more than once.
69-
await client.get(Uri.http('localhost:${server.port}', ''),
70-
headers: {'fruit': 'apple', 'color': 'red'});
71-
expect(requestHeaders['fruit'], ['apple']);
72-
expect(requestHeaders['color'], ['red']);
73-
await server.close();
54+
await client
55+
.get(Uri.http(host, ''), headers: {'fruit': 'apple', 'color': 'red'});
56+
57+
final headers = await httpServerQueue.next as Map;
58+
expect(headers['fruit'], ['apple']);
59+
expect(headers['color'], ['red']);
7460
});
7561

7662
test('multiple values per header', () async {
77-
late HttpHeaders requestHeaders;
78-
final server = (await HttpServer.bind('localhost', 0))
79-
..listen((request) async {
80-
await request.drain<void>();
81-
requestHeaders = request.headers;
82-
unawaited(request.response.close());
83-
});
8463
// The `http.Client` API does not offer a way of sending the same field
8564
// more than once.
86-
await client.get(Uri.http('localhost:${server.port}', ''),
87-
headers: {'list': 'apple, orange'});
65+
await client.get(Uri.http(host, ''), headers: {'list': 'apple, orange'});
8866

89-
expect(requestHeaders['list'], ['apple, orange']);
90-
await server.close();
67+
final headers = await httpServerQueue.next as Map;
68+
expect(headers['list'], ['apple, orange']);
9169
});
9270
});
9371
}

pkgs/http_client_conformance_tests/test/browser_client_test.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ void main() {
1818
testRequestBodyStreamed(client, canStreamRequestBody: false);
1919
testResponseBody(client, canStreamResponseBody: false);
2020
testResponseBodyStreamed(client, canStreamResponseBody: false);
21+
testRequestHeaders(client);
2122
});
2223
}

0 commit comments

Comments
 (0)