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);
   });
 }