Initial (Prototype) Implementation of HTTPS image redirects.
Uses the ResourceRequest pointer in URLLoaderThrottle to redirect image
subresources on a page to a compressed version of the same resource.
This initial implementation will return a 200 when it successfully swaps
compressed version and a 307 when it needs to fall back onto the
original uncompressed resource. It currently only handles those 2
scenarios.
This is a prototype implementation and is behind a flag.
Bug: 960513
Change-Id: I1d47e9f958a92e1ffb970d6852cdcab2acd9e698
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1606220
Commit-Queue: Sean Harrison <harrisonsean@chromium.org>
Reviewed-by: Tarun Bansal <tbansal@chromium.org>
Reviewed-by: John Abd-El-Malek <jam@chromium.org>
Reviewed-by: Robert Ogden <robertogden@chromium.org>
Cr-Commit-Position: refs/heads/master@{#661421}
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn
index 2bcc4d5..cc5a38a2 100644
--- a/chrome/renderer/BUILD.gn
+++ b/chrome/renderer/BUILD.gn
@@ -100,6 +100,14 @@
"sandbox_status_extension_android.h",
"security_interstitials/security_interstitial_page_controller.cc",
"security_interstitials/security_interstitial_page_controller.h",
+ "subresource_redirect/subresource_redirect_params.cc",
+ "subresource_redirect/subresource_redirect_params.h",
+ "subresource_redirect/subresource_redirect_switches.cc",
+ "subresource_redirect/subresource_redirect_switches.h",
+ "subresource_redirect/subresource_redirect_url_loader_throttle.cc",
+ "subresource_redirect/subresource_redirect_url_loader_throttle.h",
+ "subresource_redirect/subresource_redirect_util.cc",
+ "subresource_redirect/subresource_redirect_util.h",
"supervised_user/supervised_user_error_page_controller.cc",
"supervised_user/supervised_user_error_page_controller.h",
"tts_dispatcher.cc",
diff --git a/chrome/renderer/subresource_redirect/DEPS b/chrome/renderer/subresource_redirect/DEPS
new file mode 100644
index 0000000..a7c8042
--- /dev/null
+++ b/chrome/renderer/subresource_redirect/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+components/base32",
+]
diff --git a/chrome/renderer/subresource_redirect/OWNERS b/chrome/renderer/subresource_redirect/OWNERS
new file mode 100644
index 0000000..b3958dc
--- /dev/null
+++ b/chrome/renderer/subresource_redirect/OWNERS
@@ -0,0 +1,4 @@
+tbansal@chromium.org
+robertogden@chromium.org
+
+# COMPONENT: Internals>Network>DataProxy
\ No newline at end of file
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_params.cc b/chrome/renderer/subresource_redirect/subresource_redirect_params.cc
new file mode 100644
index 0000000..f4e9602
--- /dev/null
+++ b/chrome/renderer/subresource_redirect/subresource_redirect_params.cc
@@ -0,0 +1,17 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/renderer/subresource_redirect/subresource_redirect_params.h"
+
+#include "base/command_line.h"
+#include "chrome/renderer/subresource_redirect/subresource_redirect_switches.h"
+
+namespace subresource_redirect {
+
+bool ShouldForceEnableSubresourceRedirect() {
+ return base::CommandLine::ForCurrentProcess()->HasSwitch(
+ subresource_redirect::kEnableSubresourceRedirect);
+}
+
+} // namespace subresource_redirect
\ No newline at end of file
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_params.h b/chrome/renderer/subresource_redirect/subresource_redirect_params.h
new file mode 100644
index 0000000..c4012d7c
--- /dev/null
+++ b/chrome/renderer/subresource_redirect/subresource_redirect_params.h
@@ -0,0 +1,16 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_PARAMS_H_
+#define CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_PARAMS_H_
+
+namespace subresource_redirect {
+
+// Returns true if Subresource Redirect is forced to be enabled from the
+// command line.
+bool ShouldForceEnableSubresourceRedirect();
+
+} // namespace subresource_redirect
+
+#endif // CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_PARAMS_H_
\ No newline at end of file
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_switches.cc b/chrome/renderer/subresource_redirect/subresource_redirect_switches.cc
new file mode 100644
index 0000000..be27e68
--- /dev/null
+++ b/chrome/renderer/subresource_redirect/subresource_redirect_switches.cc
@@ -0,0 +1,13 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/renderer/subresource_redirect/subresource_redirect_switches.h"
+
+namespace subresource_redirect {
+
+// Feature flag to enable HTTPS subresource internal redirects to compressed
+// versions.
+const char kEnableSubresourceRedirect[] = "enable-subresource-redirect";
+
+} // namespace subresource_redirect
\ No newline at end of file
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_switches.h b/chrome/renderer/subresource_redirect/subresource_redirect_switches.h
new file mode 100644
index 0000000..faf45c8
--- /dev/null
+++ b/chrome/renderer/subresource_redirect/subresource_redirect_switches.h
@@ -0,0 +1,17 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_SWITCHES_H_
+#define CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_SWITCHES_H_
+
+namespace subresource_redirect {
+
+// All switches in alphabetical order. The switches should be documented
+// alongside the definition of their values in the .cc file.
+
+extern const char kEnableSubresourceRedirect[];
+
+} // namespace subresource_redirect
+
+#endif // CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_SWITCHES_H_
\ No newline at end of file
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.cc b/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.cc
new file mode 100644
index 0000000..facef78
--- /dev/null
+++ b/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.cc
@@ -0,0 +1,33 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.h"
+
+#include "chrome/renderer/subresource_redirect/subresource_redirect_util.h"
+#include "content/public/common/resource_type.h"
+#include "url/gurl.h"
+
+namespace subresource_redirect {
+
+SubresourceRedirectURLLoaderThrottle::SubresourceRedirectURLLoaderThrottle() =
+ default;
+SubresourceRedirectURLLoaderThrottle::~SubresourceRedirectURLLoaderThrottle() =
+ default;
+
+void SubresourceRedirectURLLoaderThrottle::WillStartRequest(
+ network::ResourceRequest* request,
+ bool* defer) {
+ if (request->resource_type != static_cast<int>(content::ResourceType::kImage))
+ return;
+
+ if (!request->url.SchemeIs(url::kHttpsScheme))
+ return;
+
+ request->url = GetSubresourceURLForURL(request->url);
+ *defer = false;
+}
+
+void SubresourceRedirectURLLoaderThrottle::DetachFromCurrentSequence() {}
+
+} // namespace subresource_redirect
\ No newline at end of file
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.h b/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.h
new file mode 100644
index 0000000..b9b12433
--- /dev/null
+++ b/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.h
@@ -0,0 +1,27 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_URL_LOADER_THROTTLE_H_
+#define CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_URL_LOADER_THROTTLE_H_
+
+#include "content/public/common/url_loader_throttle.h"
+
+namespace subresource_redirect {
+
+// This class handles internal redirects for subresouces on HTTPS sites to
+// compressed versions of subresources.
+class SubresourceRedirectURLLoaderThrottle : public content::URLLoaderThrottle {
+ public:
+ SubresourceRedirectURLLoaderThrottle();
+ ~SubresourceRedirectURLLoaderThrottle() override;
+
+ // content::URLLoaderThrottle:
+ void WillStartRequest(network::ResourceRequest* request,
+ bool* defer) override;
+ void DetachFromCurrentSequence() override;
+};
+
+} // namespace subresource_redirect
+
+#endif // CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_URL_LOADER_THROTTLE_H_
\ No newline at end of file
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle_unittest.cc b/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle_unittest.cc
new file mode 100644
index 0000000..810ea8d
--- /dev/null
+++ b/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle_unittest.cc
@@ -0,0 +1,71 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.h"
+
+#include "chrome/renderer/subresource_redirect/subresource_redirect_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace subresource_redirect {
+
+namespace {
+
+TEST(SubresourceRedirectURLLoaderThrottleTest, TestGetSubresourceURL) {
+ struct TestCase {
+ GURL original_url;
+ int resource_type;
+ GURL redirected_subresource_url;
+ };
+
+ const TestCase kTestCases[]{
+ {
+ GURL("https://www.test.com/test.jpg"),
+ static_cast<int>(content::ResourceType::kImage),
+ GetSubresourceURLForURL(GURL("https://www.test.com/test.jpg")),
+ },
+ {
+ GURL("https://www.test.com/test.jpg#test"),
+ static_cast<int>(content::ResourceType::kImage),
+ GetSubresourceURLForURL(GURL("https://www.test.com/test.jpg#test")),
+ },
+ {
+ GURL("https://www.test.com/test.css"),
+ static_cast<int>(content::ResourceType::kStylesheet),
+ GURL("https://www.test.com/test.css"),
+ },
+ {
+ GURL("http://www.insecure.com/test.jpg"),
+ static_cast<int>(content::ResourceType::kImage),
+ GURL("http://www.insecure.com/test.jpg"),
+ },
+ };
+
+ for (const TestCase& test_case : kTestCases) {
+ network::ResourceRequest request;
+ request.url = test_case.original_url;
+ request.resource_type = test_case.resource_type;
+ bool defer = false;
+
+ SubresourceRedirectURLLoaderThrottle throttle;
+ throttle.WillStartRequest(&request, &defer);
+
+ EXPECT_FALSE(defer);
+ EXPECT_EQ(request.url, test_case.redirected_subresource_url);
+ }
+}
+
+TEST(SubresourceRedirectURLLoaderThrottleTest, DeferOverridenToFalse) {
+ SubresourceRedirectURLLoaderThrottle throttle;
+
+ network::ResourceRequest request;
+ request.url = GURL("https://www.test.com/test.jpg");
+ request.resource_type = static_cast<int>(content::ResourceType::kImage);
+ bool defer = true;
+
+ throttle.WillStartRequest(&request, &defer);
+ EXPECT_FALSE(defer);
+}
+
+} // namespace
+} // namespace subresource_redirect
\ No newline at end of file
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_util.cc b/chrome/renderer/subresource_redirect/subresource_redirect_util.cc
new file mode 100644
index 0000000..92316250
--- /dev/null
+++ b/chrome/renderer/subresource_redirect/subresource_redirect_util.cc
@@ -0,0 +1,51 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/renderer/subresource_redirect/subresource_redirect_util.h"
+
+#include <string>
+
+#include "base/strings/safe_sprintf.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "components/base32/base32.h"
+#include "crypto/sha2.h"
+#include "net/base/escape.h"
+#include "net/base/url_util.h"
+#include "url/gurl.h"
+
+namespace subresource_redirect {
+
+GURL GetSubresourceURLForURL(const GURL& original_url) {
+ DCHECK(original_url.is_valid());
+
+ std::string fragment;
+ if (original_url.has_ref()) {
+ fragment = "#" + original_url.ref();
+ }
+
+ std::string origin_hash = base::ToLowerASCII(base32::Base32Encode(
+ crypto::SHA256HashString(
+ original_url.scheme() + "://" + original_url.host() + ":" +
+ base::NumberToString(original_url.EffectiveIntPort())),
+ base32::Base32EncodePolicy::OMIT_PADDING));
+ GURL subresource_host("https://litepages.googlezip.net/");
+ // TODO(harrisonsean): Add experiment (x=) param.
+ GURL compressed_url(
+ subresource_host.scheme() + "://" + origin_hash + "." +
+ subresource_host.host() +
+ (subresource_host.has_port() ? (":" + subresource_host.port()) : "") +
+ "/sr?u=" +
+ // Strip out the fragment so that it is not sent to the server.
+ net::EscapeQueryParamValue(original_url.GetAsReferrer().spec(),
+ true /* use_plus */) +
+ "&t=image" + fragment);
+
+ DCHECK(compressed_url.is_valid());
+ DCHECK_EQ(subresource_host.scheme(), compressed_url.scheme());
+ return compressed_url;
+}
+
+} // namespace subresource_redirect
\ No newline at end of file
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_util.h b/chrome/renderer/subresource_redirect/subresource_redirect_util.h
new file mode 100644
index 0000000..20ce031d
--- /dev/null
+++ b/chrome/renderer/subresource_redirect/subresource_redirect_util.h
@@ -0,0 +1,18 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_UTIL_H_
+#define CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_UTIL_H_
+
+#include "url/gurl.h"
+
+namespace subresource_redirect {
+
+// Gets the new URL for the compressed version of the image resource to enable
+// internal redirects.
+GURL GetSubresourceURLForURL(const GURL& original_url);
+
+} // namespace subresource_redirect
+
+#endif // CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_UTIL_H_
\ No newline at end of file
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_util_unittest.cc b/chrome/renderer/subresource_redirect/subresource_redirect_util_unittest.cc
new file mode 100644
index 0000000..e0df1d6
--- /dev/null
+++ b/chrome/renderer/subresource_redirect/subresource_redirect_util_unittest.cc
@@ -0,0 +1,74 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/renderer/subresource_redirect/subresource_redirect_util.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace subresource_redirect {
+
+TEST(SubresourceRedirectURL, ProperlyChangesURL) {
+ EXPECT_EQ(GetSubresourceURLForURL(GURL("https://www.test.com/test.jpg")),
+ GURL("https://"
+ "jy6r5d5zc3n6juvq35nveinxzyuk4n4wndppyli5x5ycmrza36fa."
+ "litepages.googlezip.net/"
+ "sr?u=https%3A%2F%2Fwww.test.com%2Ftest.jpg&t=image"));
+}
+
+TEST(SubresourceRedirectURL, ProperlyHandlesFragment) {
+ EXPECT_EQ(GetSubresourceURLForURL(GURL("https://www.test.com/test.jpg#test")),
+ GURL("https://"
+ "jy6r5d5zc3n6juvq35nveinxzyuk4n4wndppyli5x5ycmrza36fa."
+ "litepages.googlezip.net/"
+ "sr?u=https%3A%2F%2Fwww.test.com%2Ftest.jpg&t=image#test"));
+}
+
+TEST(SubresourceRedirectURL, ProperlyHandlesSetPort) {
+ EXPECT_EQ(GetSubresourceURLForURL(GURL("https://www.test.com:4444/test.jpg")),
+ GURL("https://flm6clfkawcjb2bw5cnrrcdf4fkoliileuljcc23lahdet75ouqq."
+ "litepages.googlezip.net/sr?u=https%3A%2F%2Fwww.test."
+ "com%3A4444%2Ftest.jpg&t=image"));
+}
+
+TEST(SubresourceRedirectURL, ProperlyHandlesQueryParams) {
+ EXPECT_EQ(GetSubresourceURLForURL(
+ GURL("https://www.test.com/test.jpg?color=yellow")),
+ GURL("https://"
+ "jy6r5d5zc3n6juvq35nveinxzyuk4n4wndppyli5x5ycmrza36fa."
+ "litepages.googlezip.net/"
+ "sr?u=https%3A%2F%2Fwww.test.com%2Ftest.jpg%3Fcolor%3Dyellow&"
+ "t=image"));
+}
+
+TEST(SubresourceRedirectURL, ProperlyHandlesMultipleQueryParams) {
+ EXPECT_EQ(GetSubresourceURLForURL(
+ GURL("https://www.test.com/test.jpg?color=yellow&name=test")),
+ GURL("https://"
+ "jy6r5d5zc3n6juvq35nveinxzyuk4n4wndppyli5x5ycmrza36fa."
+ "litepages.googlezip.net/"
+ "sr?u=https%3A%2F%2Fwww.test.com%2Ftest.jpg%3Fcolor%3Dyellow%"
+ "26name%3Dtest&t=image"));
+}
+
+TEST(SubresourceRedirectURL, ProperlyHandlesQueryParamsWithFragments) {
+ EXPECT_EQ(GetSubresourceURLForURL(
+ GURL("https://www.test.com/test.jpg?color=yellow#test")),
+ GURL("https://"
+ "jy6r5d5zc3n6juvq35nveinxzyuk4n4wndppyli5x5ycmrza36fa."
+ "litepages.googlezip.net/"
+ "sr?u=https%3A%2F%2Fwww.test.com%2Ftest.jpg%3Fcolor%3Dyellow&"
+ "t=image#test"));
+}
+
+// Currently redirects are not supported for HTTP subresources, but there is
+// potential to add them in the future.
+TEST(SubresourceRedirectURL, ProperlyChangesHTTPURL) {
+ EXPECT_EQ(
+ GetSubresourceURLForURL(GURL("http://www.test.com/test.jpg")),
+ GURL("https://bc6pgqmtr6nlicooao2zh77svcptncpwfolwlgbrop6gqnr6ck3q."
+ "litepages.googlezip.net/sr?u=http%3A%2F%2Fwww.test.com%2Ftest."
+ "jpg&t=image"));
+}
+
+} // namespace subresource_redirect
\ No newline at end of file
diff --git a/chrome/renderer/url_loader_throttle_provider_impl.cc b/chrome/renderer/url_loader_throttle_provider_impl.cc
index a549b15..692d318 100644
--- a/chrome/renderer/url_loader_throttle_provider_impl.cc
+++ b/chrome/renderer/url_loader_throttle_provider_impl.cc
@@ -17,6 +17,8 @@
#include "chrome/renderer/chrome_render_thread_observer.h"
#include "chrome/renderer/prerender/prerender_dispatcher.h"
#include "chrome/renderer/prerender/prerender_helper.h"
+#include "chrome/renderer/subresource_redirect/subresource_redirect_params.h"
+#include "chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.h"
#include "components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.h"
@@ -274,6 +276,12 @@
->chromeos_listener()));
#endif // defined(OS_CHROMEOS)
+ if (subresource_redirect::ShouldForceEnableSubresourceRedirect()) {
+ throttles.push_back(
+ std::make_unique<
+ subresource_redirect::SubresourceRedirectURLLoaderThrottle>());
+ }
+
return throttles;
}
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 2fd5b33..986fc8ef7 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3055,6 +3055,8 @@
"../renderer/page_load_metrics/page_timing_metrics_sender_unittest.cc",
"../renderer/plugins/plugin_uma_unittest.cc",
"../renderer/prerender/prerender_dispatcher_unittest.cc",
+ "../renderer/subresource_redirect/subresource_redirect_url_loader_throttle_unittest.cc",
+ "../renderer/subresource_redirect/subresource_redirect_util_unittest.cc",
"../renderer/v8_unwinder_unittest.cc",
"../test/base/chrome_render_view_test.cc",
"../test/base/chrome_render_view_test.h",
diff --git a/content/common/throttling_url_loader.cc b/content/common/throttling_url_loader.cc
index de3cff5..a2c6e14 100644
--- a/content/common/throttling_url_loader.cc
+++ b/content/common/throttling_url_loader.cc
@@ -276,7 +276,13 @@
int32_t intra_priority_value) {
if (!url_loader_) {
if (!loader_completed_) {
- DCHECK_EQ(DEFERRED_START, deferred_stage_);
+ // Only check |deferred_stage_| if this resource has not been redirected
+ // by a throttle.
+ if (throttle_will_start_redirect_url_.is_empty() &&
+ throttle_will_redirect_redirect_url_.is_empty()) {
+ DCHECK_EQ(DEFERRED_START, deferred_stage_);
+ }
+
priority_info_ =
std::make_unique<PriorityInfo>(priority, intra_priority_value);
}
diff --git a/content/public/common/url_loader_throttle.h b/content/public/common/url_loader_throttle.h
index 35fc259..f6a7094 100644
--- a/content/public/common/url_loader_throttle.h
+++ b/content/public/common/url_loader_throttle.h
@@ -113,8 +113,7 @@
// asynchronously touching the pointer in defer case is not valid)
// When |request->url| is modified it will make an internal redirect, which
// might have some side-effects: drop upload streams data might be dropped,
- // redirect count may be reached, and cross-origin redirect are not supported
- // (at least until we have the demand).
+ // redirect count may be reached.
//
// Implementations should be aware that throttling can happen multiple times
// for the same |request|, even after one instance of the same throttle