A browser tests for request headers. (#715)
diff --git a/pkgs/http_client_conformance_tests/lib/src/request_headers_server.dart b/pkgs/http_client_conformance_tests/lib/src/request_headers_server.dart
new file mode 100644
index 0000000..c443ad0
--- /dev/null
+++ b/pkgs/http_client_conformance_tests/lib/src/request_headers_server.dart
@@ -0,0 +1,45 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:stream_channel/stream_channel.dart';
+
+/// Starts an HTTP server that captures the request headers.
+///
+/// Channel protocol:
+/// On Startup:
+/// - send port
+/// On Request Received:
+/// - send headers as Map<String, List<String>>
+/// When Receive Anything:
+/// - exit
+void hybridMain(StreamChannel<Object?> channel) async {
+ late HttpServer server;
+
+ server = (await HttpServer.bind('localhost', 0))
+ ..listen((request) async {
+ request.response.headers.set('Access-Control-Allow-Origin', '*');
+ if (request.method == 'OPTIONS') {
+ // Handle a CORS preflight request:
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#preflighted_requests
+ request.response.headers
+ ..set('Access-Control-Allow-Methods', 'GET')
+ ..set('Access-Control-Allow-Headers', '*');
+ } else {
+ final headers = <String, List<String>>{};
+ request.headers.forEach((field, value) {
+ headers[field] = value;
+ });
+ channel.sink.add(headers);
+ }
+ unawaited(request.response.close());
+ });
+
+ channel.sink.add(server.port);
+ await channel
+ .stream.first; // Any writes indicates that the server should exit.
+ unawaited(server.close());
+}
diff --git a/pkgs/http_client_conformance_tests/lib/src/request_headers_tests.dart b/pkgs/http_client_conformance_tests/lib/src/request_headers_tests.dart
index d5a6820..ee5f4ed 100644
--- a/pkgs/http_client_conformance_tests/lib/src/request_headers_tests.dart
+++ b/pkgs/http_client_conformance_tests/lib/src/request_headers_tests.dart
@@ -2,92 +2,70 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'dart:async';
-import 'dart:io';
-
+import 'package:async/async.dart';
import 'package:http/http.dart';
+import 'package:stream_channel/stream_channel.dart';
import 'package:test/test.dart';
+import 'utils.dart';
+
/// Tests that the [Client] correctly sends headers in the request.
void testRequestHeaders(Client client) async {
group('client headers', () {
+ late final String host;
+ late final StreamChannel<Object?> httpServerChannel;
+ late final StreamQueue<Object?> httpServerQueue;
+
+ setUpAll(() async {
+ httpServerChannel = await startServer('request_headers_server.dart');
+ httpServerQueue = StreamQueue(httpServerChannel.stream);
+ host = 'localhost:${await httpServerQueue.next}';
+ });
+ tearDownAll(() => httpServerChannel.sink.add(null));
+
test('single header', () async {
- late HttpHeaders requestHeaders;
- final server = (await HttpServer.bind('localhost', 0))
- ..listen((request) async {
- await request.drain<void>();
- requestHeaders = request.headers;
- unawaited(request.response.close());
- });
- await client.get(Uri.http('localhost:${server.port}', ''),
- headers: {'foo': 'bar'});
- expect(requestHeaders['foo'], ['bar']);
- await server.close();
+ await client.get(Uri.http(host, ''), headers: {'foo': 'bar'});
+
+ final headers = await httpServerQueue.next as Map;
+ expect(headers['foo'], ['bar']);
});
test('UPPER case header', () async {
- late HttpHeaders requestHeaders;
- final server = (await HttpServer.bind('localhost', 0))
- ..listen((request) async {
- await request.drain<void>();
- requestHeaders = request.headers;
- unawaited(request.response.close());
- });
- await client.get(Uri.http('localhost:${server.port}', ''),
- headers: {'FOO': 'BAR'});
+ await client.get(Uri.http(host, ''), headers: {'FOO': 'BAR'});
+
+ final headers = await httpServerQueue.next as Map;
// RFC 2616 14.44 states that header field names are case-insensive.
// http.Client canonicalizes field names into lower case.
- expect(requestHeaders['foo'], ['BAR']);
- await server.close();
+ expect(headers['foo'], ['BAR']);
});
test('test headers different only in case', () async {
- // RFC 2616 14.44 states that header field names are case-insensive.
- late HttpHeaders requestHeaders;
- final server = (await HttpServer.bind('localhost', 0))
- ..listen((request) async {
- await request.drain<void>();
- requestHeaders = request.headers;
- unawaited(request.response.close());
- });
- await client.get(Uri.http('localhost:${server.port}', ''),
- headers: {'foo': 'bar', 'Foo': 'Bar'});
- expect(requestHeaders['foo']!.single, isIn(['bar', 'Bar']));
- await server.close();
+ await client
+ .get(Uri.http(host, ''), headers: {'foo': 'bar', 'Foo': 'Bar'});
+
+ final headers = await httpServerQueue.next as Map;
+ // ignore: avoid_dynamic_calls
+ expect(headers['foo']!.single, isIn(['bar', 'Bar']));
});
test('multiple headers', () async {
- late HttpHeaders requestHeaders;
- final server = (await HttpServer.bind('localhost', 0))
- ..listen((request) async {
- await request.drain<void>();
- requestHeaders = request.headers;
- unawaited(request.response.close());
- });
// The `http.Client` API does not offer a way of sending the name field
// more than once.
- await client.get(Uri.http('localhost:${server.port}', ''),
- headers: {'fruit': 'apple', 'color': 'red'});
- expect(requestHeaders['fruit'], ['apple']);
- expect(requestHeaders['color'], ['red']);
- await server.close();
+ await client
+ .get(Uri.http(host, ''), headers: {'fruit': 'apple', 'color': 'red'});
+
+ final headers = await httpServerQueue.next as Map;
+ expect(headers['fruit'], ['apple']);
+ expect(headers['color'], ['red']);
});
test('multiple values per header', () async {
- late HttpHeaders requestHeaders;
- final server = (await HttpServer.bind('localhost', 0))
- ..listen((request) async {
- await request.drain<void>();
- requestHeaders = request.headers;
- unawaited(request.response.close());
- });
// The `http.Client` API does not offer a way of sending the same field
// more than once.
- await client.get(Uri.http('localhost:${server.port}', ''),
- headers: {'list': 'apple, orange'});
+ await client.get(Uri.http(host, ''), headers: {'list': 'apple, orange'});
- expect(requestHeaders['list'], ['apple, orange']);
- await server.close();
+ final headers = await httpServerQueue.next as Map;
+ expect(headers['list'], ['apple, orange']);
});
});
}
diff --git a/pkgs/http_client_conformance_tests/test/browser_client_test.dart b/pkgs/http_client_conformance_tests/test/browser_client_test.dart
index dfd3c32..496b190 100644
--- a/pkgs/http_client_conformance_tests/test/browser_client_test.dart
+++ b/pkgs/http_client_conformance_tests/test/browser_client_test.dart
@@ -18,5 +18,6 @@
testRequestBodyStreamed(client, canStreamRequestBody: false);
testResponseBody(client, canStreamResponseBody: false);
testResponseBodyStreamed(client, canStreamResponseBody: false);
+ testRequestHeaders(client);
});
}