Tests for _HttpClient.shouldCopyHeadersOnRedirect.

TESTED=test change
Change-Id: I3fb27e21eaab05fc812b2b888dfea3b29e0c5648
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/230600
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Brian Quinlan <bquinlan@google.com>
diff --git a/sdk/lib/_http/http_impl.dart b/sdk/lib/_http/http_impl.dart
index 80ee3b5..312ca7c 100644
--- a/sdk/lib/_http/http_impl.dart
+++ b/sdk/lib/_http/http_impl.dart
@@ -2723,7 +2723,8 @@
             subdomain.host.endsWith("." + domain.host)));
   }
 
-  static bool _shouldCopyHeaderOnRedirect(
+  // Only visible for testing.
+  static bool shouldCopyHeaderOnRedirect(
       String headerKey, Uri originalUrl, Uri redirectUri) {
     if (_isSubdomain(redirectUri, originalUrl)) {
       return true;
@@ -2754,7 +2755,7 @@
       for (var header in previous.headers._headers.keys) {
         if (request.headers[header] == null &&
             (!isRedirect ||
-                _shouldCopyHeaderOnRedirect(header, resolved, previous.uri))) {
+                shouldCopyHeaderOnRedirect(header, resolved, previous.uri))) {
           request.headers.set(header, previous.headers[header]!);
         }
       }
diff --git a/tests/standalone/io/http_redirect_test.dart b/tests/standalone/io/http_redirect_test.dart
index 1a19222..7ccf54d 100644
--- a/tests/standalone/io/http_redirect_test.dart
+++ b/tests/standalone/io/http_redirect_test.dart
@@ -6,6 +6,7 @@
 import "package:expect/expect.dart";
 import "dart:async";
 import "dart:io";
+import "dart:mirrors";
 
 Future<HttpServer> setupServer({Uri? targetServer}) {
   final completer = new Completer<HttpServer>();
@@ -343,6 +344,95 @@
   });
 }
 
+void testShouldCopyHeadersOnRedirect() {
+  final clientClass = reflect(HttpClient()).type;
+  final fnName = Symbol("shouldCopyHeaderOnRedirect");
+
+  shouldCopyHeaderOnRedirect(
+          String headerKey, Uri originalUrl, Uri redirectUri) =>
+      clientClass.invoke(
+          fnName, [headerKey, originalUrl, redirectUri]).reflectee as bool;
+
+  checkShouldCopyHeader(
+      String headerKey, String originalUrl, String redirectUri, bool expected) {
+    if (shouldCopyHeaderOnRedirect(
+            headerKey, Uri.parse(originalUrl), Uri.parse(redirectUri)) !=
+        expected) {
+      Expect.fail(
+          "shouldCopyHeaderOnRedirect($headerKey, $originalUrl, $redirectUri) => ${!expected}");
+    }
+  }
+
+  // Redirect on localhost.
+  checkShouldCopyHeader(
+      "authorization", "http://localhost", "http://localhost/foo", true);
+  checkShouldCopyHeader(
+      "cat", "http://localhost", "http://localhost/foo", true);
+
+  // Redirect to same IP address.
+  checkShouldCopyHeader("authorization", "http://192.168.20.20",
+      "http://192.168.20.20/foo", true);
+  checkShouldCopyHeader(
+      "cat", "http://192.168.20.20", "http://192.168.20.20/foo", true);
+
+  // Redirect to different IP address.
+  checkShouldCopyHeader(
+      "authorization", "http://192.168.20.20", "http://192.168.20.99", false);
+  checkShouldCopyHeader(
+      "cat", "http://192.168.20.20", "http://192.168.20.99", true);
+
+  // Redirect to same domain.
+  checkShouldCopyHeader(
+      "authorization", "http://foo.com", "http://foo.com/foo", true);
+  checkShouldCopyHeader("cat", "http://foo.com", "http://foo.com/foo", true);
+
+  // Redirect to same domain with explicit ports.
+  checkShouldCopyHeader(
+      "authorization", "http://foo.com", "http://foo.com:80/foo", true);
+  checkShouldCopyHeader("cat", "http://foo.com", "http://foo.com:80/foo", true);
+
+  // Redirect to subdomain.
+  checkShouldCopyHeader(
+      "authorization", "https://foo.com", "https://www.foo.com", true);
+  checkShouldCopyHeader("cat", "https://foo.com", "https://www.foo.com", true);
+
+  // Redirect to different domain.
+  checkShouldCopyHeader(
+      "authorization", "https://foo.com", "https://wwwfoo.com", false);
+  checkShouldCopyHeader("cat", "https://foo.com", "https://wwwfoo.com", true);
+
+  // Redirect to different port.
+  checkShouldCopyHeader(
+      "authorization", "http://foo.com", "http://foo.com:81", false);
+  checkShouldCopyHeader("cat", "http://foo.com", "http://foo.com:81", true);
+
+  // Redirect from secure to insecure.
+  checkShouldCopyHeader(
+      "authorization", "https://foo.com", "http://foo.com", false);
+  checkShouldCopyHeader("cat", "https://foo.com", "http://foo.com", true);
+
+  // Redirect from secure to insecure, same port.
+  checkShouldCopyHeader(
+      "authorization", "https://foo.com:8888", "http://foo.com:8888", false);
+  checkShouldCopyHeader(
+      "cat", "https://foo.com:8888", "http://foo.com:8888", true);
+
+  // Redirect from insecure to secure.
+  checkShouldCopyHeader(
+      "authorization", "http://foo.com", "https://foo.com", false);
+  checkShouldCopyHeader("cat", "http://foo.com", "https://foo.com", true);
+
+  // Redirect to subdomain, different port.
+  checkShouldCopyHeader(
+      "authorization", "https://foo.com:80", "https://www.foo.com:81", false);
+  checkShouldCopyHeader(
+      "cat", "https://foo.com:80", "https://www.foo.com:81", true);
+
+  // Different header casting:
+  checkShouldCopyHeader(
+      "AuThOrIzAtiOn", "https://foo.com", "https://bar.com", false);
+}
+
 void testCrossDomainAutoRedirectWithHeaders() {
   setupTargetServer().then((targetServer) {
     setupServer(
@@ -517,6 +607,7 @@
   testManualRedirectWithHeaders();
   testAutoRedirect();
   testAutoRedirectWithHeaders();
+  testShouldCopyHeadersOnRedirect();
   testCrossDomainAutoRedirectWithHeaders();
   testAutoRedirect301POST();
   testAutoRedirect303POST();
diff --git a/tests/standalone_2/io/http_redirect_test.dart b/tests/standalone_2/io/http_redirect_test.dart
index e851045..1d4fde8 100644
--- a/tests/standalone_2/io/http_redirect_test.dart
+++ b/tests/standalone_2/io/http_redirect_test.dart
@@ -8,6 +8,7 @@
 import "package:expect/expect.dart";
 import "dart:async";
 import "dart:io";
+import "dart:mirrors";
 
 Future<HttpServer> setupServer({Uri targetServer}) {
   Completer completer = new Completer<HttpServer>();
@@ -345,6 +346,95 @@
   });
 }
 
+void testShouldCopyHeadersOnRedirect() {
+  final clientClass = reflect(HttpClient()).type;
+  final fnName = Symbol("shouldCopyHeaderOnRedirect");
+
+  shouldCopyHeaderOnRedirect(
+          String headerKey, Uri originalUrl, Uri redirectUri) =>
+      clientClass.invoke(
+          fnName, [headerKey, originalUrl, redirectUri]).reflectee as bool;
+
+  checkShouldCopyHeader(
+      String headerKey, String originalUrl, String redirectUri, bool expected) {
+    if (shouldCopyHeaderOnRedirect(
+            headerKey, Uri.parse(originalUrl), Uri.parse(redirectUri)) !=
+        expected) {
+      Expect.fail(
+          "shouldCopyHeaderOnRedirect($headerKey, $originalUrl, $redirectUri) => ${!expected}");
+    }
+  }
+
+  // Redirect on localhost.
+  checkShouldCopyHeader(
+      "authorization", "http://localhost", "http://localhost/foo", true);
+  checkShouldCopyHeader(
+      "cat", "http://localhost", "http://localhost/foo", true);
+
+  // Redirect to same IP address.
+  checkShouldCopyHeader("authorization", "http://192.168.20.20",
+      "http://192.168.20.20/foo", true);
+  checkShouldCopyHeader(
+      "cat", "http://192.168.20.20", "http://192.168.20.20/foo", true);
+
+  // Redirect to different IP address.
+  checkShouldCopyHeader(
+      "authorization", "http://192.168.20.20", "http://192.168.20.99", false);
+  checkShouldCopyHeader(
+      "cat", "http://192.168.20.20", "http://192.168.20.99", true);
+
+  // Redirect to same domain.
+  checkShouldCopyHeader(
+      "authorization", "http://foo.com", "http://foo.com/foo", true);
+  checkShouldCopyHeader("cat", "http://foo.com", "http://foo.com/foo", true);
+
+  // Redirect to same domain with explicit ports.
+  checkShouldCopyHeader(
+      "authorization", "http://foo.com", "http://foo.com:80/foo", true);
+  checkShouldCopyHeader("cat", "http://foo.com", "http://foo.com:80/foo", true);
+
+  // Redirect to subdomain.
+  checkShouldCopyHeader(
+      "authorization", "https://foo.com", "https://www.foo.com", true);
+  checkShouldCopyHeader("cat", "https://foo.com", "https://www.foo.com", true);
+
+  // Redirect to different domain.
+  checkShouldCopyHeader(
+      "authorization", "https://foo.com", "https://wwwfoo.com", false);
+  checkShouldCopyHeader("cat", "https://foo.com", "https://wwwfoo.com", true);
+
+  // Redirect to different port.
+  checkShouldCopyHeader(
+      "authorization", "http://foo.com", "http://foo.com:81", false);
+  checkShouldCopyHeader("cat", "http://foo.com", "http://foo.com:81", true);
+
+  // Redirect from secure to insecure.
+  checkShouldCopyHeader(
+      "authorization", "https://foo.com", "http://foo.com", false);
+  checkShouldCopyHeader("cat", "https://foo.com", "http://foo.com", true);
+
+  // Redirect from secure to insecure, same port.
+  checkShouldCopyHeader(
+      "authorization", "https://foo.com:8888", "http://foo.com:8888", false);
+  checkShouldCopyHeader(
+      "cat", "https://foo.com:8888", "http://foo.com:8888", true);
+
+  // Redirect from insecure to secure.
+  checkShouldCopyHeader(
+      "authorization", "http://foo.com", "https://foo.com", false);
+  checkShouldCopyHeader("cat", "http://foo.com", "https://foo.com", true);
+
+  // Redirect to subdomain, different port.
+  checkShouldCopyHeader(
+      "authorization", "https://foo.com:80", "https://www.foo.com:81", false);
+  checkShouldCopyHeader(
+      "cat", "https://foo.com:80", "https://www.foo.com:81", true);
+
+  // Different header casting:
+  checkShouldCopyHeader(
+      "AuThOrIzAtiOn", "https://foo.com", "https://bar.com", false);
+}
+
 void testCrossDomainAutoRedirectWithHeaders() {
   setupTargetServer().then((targetServer) {
     setupServer(
@@ -519,6 +609,7 @@
   testManualRedirectWithHeaders();
   testAutoRedirect();
   testAutoRedirectWithHeaders();
+  testShouldCopyHeadersOnRedirect();
   testCrossDomainAutoRedirectWithHeaders();
   testAutoRedirect301POST();
   testAutoRedirect303POST();