chore: pull ProxyingURLLoaderFactory closer to upstream class it mirrors (#29486)

* chore: pull ProxyingURLLoaderFactory closer to upstream class it mirrors

* chore: add another change which was accepted upstream
This commit is contained in:
David Sanders 2021-06-03 21:18:02 -07:00 committed by GitHub
parent 81c5da5221
commit 8e1176cbc0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 152 additions and 138 deletions

View file

@ -4,8 +4,11 @@
#include "shell/browser/net/proxying_url_loader_factory.h"
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
@ -14,12 +17,15 @@
#include "extensions/browser/extension_navigation_ui_data.h"
#include "net/base/completion_repeating_callback.h"
#include "net/base/load_flags.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_status_code.h"
#include "net/http/http_util.h"
#include "net/url_request/redirect_info.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "services/network/public/cpp/features.h"
#include "shell/browser/net/asar/asar_url_loader.h"
#include "shell/common/options_switches.h"
#include "url/origin.h"
namespace electron {
@ -43,9 +49,9 @@ ProxyingURLLoaderFactory::InProgressRequest::InProgressRequest(
request_(request),
original_initiator_(request.request_initiator),
request_id_(web_request_id),
network_service_request_id_(network_service_request_id),
view_routing_id_(view_routing_id),
frame_routing_id_(frame_routing_id),
network_service_request_id_(network_service_request_id),
options_(options),
traffic_annotation_(traffic_annotation),
proxied_loader_receiver_(this, std::move(loader_receiver)),
@ -159,7 +165,7 @@ void ProxyingURLLoaderFactory::InProgressRequest::RestartInternal() {
// they respond. |continuation| above will be invoked asynchronously to
// continue or cancel the request.
//
// We pause the binding here to prevent further client message processing.
// We pause the receiver here to prevent further client message processing.
if (proxied_client_receiver_.is_bound())
proxied_client_receiver_.Pause();
@ -229,6 +235,11 @@ void ProxyingURLLoaderFactory::InProgressRequest::ResumeReadingBodyFromNet() {
target_loader_->ResumeReadingBodyFromNet();
}
void ProxyingURLLoaderFactory::InProgressRequest::OnReceiveEarlyHints(
network::mojom::EarlyHintsPtr early_hints) {
target_client_->OnReceiveEarlyHints(std::move(early_hints));
}
void ProxyingURLLoaderFactory::InProgressRequest::OnReceiveResponse(
network::mojom::URLResponseHeadPtr head) {
if (current_request_uses_header_client_) {
@ -246,11 +257,6 @@ void ProxyingURLLoaderFactory::InProgressRequest::OnReceiveResponse(
}
}
void ProxyingURLLoaderFactory::InProgressRequest::OnReceiveEarlyHints(
network::mojom::EarlyHintsPtr early_hints) {
target_client_->OnReceiveEarlyHints(std::move(early_hints));
}
void ProxyingURLLoaderFactory::InProgressRequest::OnReceiveRedirect(
const net::RedirectInfo& redirect_info,
network::mojom::URLResponseHeadPtr head) {
@ -313,12 +319,17 @@ void ProxyingURLLoaderFactory::InProgressRequest::OnComplete(
factory_->RemoveRequest(network_service_request_id_, request_id_);
}
bool ProxyingURLLoaderFactory::IsForServiceWorkerScript() const {
return loader_factory_type_ == content::ContentBrowserClient::
URLLoaderFactoryType::kServiceWorkerScript;
}
void ProxyingURLLoaderFactory::InProgressRequest::OnLoaderCreated(
mojo::PendingReceiver<network::mojom::TrustedHeaderClient> receiver) {
header_client_receiver_.Bind(std::move(receiver));
if (for_cors_preflight_) {
// In this case we don't have |target_loader_| and
// |proxied_client_binding_|, and |receiver| is the only connection to the
// |proxied_client_receiver_|, and |receiver| is the only connection to the
// network service, so we observe mojo connection errors.
header_client_receiver_.set_disconnect_handler(base::BindOnce(
&ProxyingURLLoaderFactory::InProgressRequest::OnRequestError,
@ -365,25 +376,57 @@ void ProxyingURLLoaderFactory::InProgressRequest::OnHeadersReceived(
weak_factory_.GetWeakPtr()));
}
void ProxyingURLLoaderFactory::OnLoaderForCorsPreflightCreated(
const network::ResourceRequest& request,
mojo::PendingReceiver<network::mojom::TrustedHeaderClient> receiver) {
// Please note that the URLLoader is now starting, without waiting for
// additional signals from here. The URLLoader will be blocked before
// sending HTTP request headers (TrustedHeaderClient.OnBeforeSendHeaders),
// but the connection set up will be done before that. This is acceptable from
// Web Request API because the extension has already allowed to set up
// a connection to the same URL (i.e., the actual request), and distinguishing
// two connections for the actual request and the preflight request before
// sending request headers is very difficult.
const uint64_t web_request_id = ++(*request_id_generator_);
void ProxyingURLLoaderFactory::InProgressRequest::
HandleBeforeRequestRedirect() {
// The extension requested a redirect. Close the connection with the current
// URLLoader and inform the URLLoaderClient the WebRequest API generated a
// redirect. To load |redirect_url_|, a new URLLoader will be recreated
// after receiving FollowRedirect().
auto result = requests_.insert(std::make_pair(
web_request_id, std::make_unique<InProgressRequest>(
this, web_request_id, frame_routing_id_, request)));
// Forgetting to close the connection with the current URLLoader caused
// bugs. The latter doesn't know anything about the redirect. Continuing
// the load with it gives unexpected results. See
// https://crbug.com/882661#c72.
proxied_client_receiver_.reset();
header_client_receiver_.reset();
target_loader_.reset();
result.first->second->OnLoaderCreated(std::move(receiver));
result.first->second->Restart();
constexpr int kInternalRedirectStatusCode = net::HTTP_TEMPORARY_REDIRECT;
net::RedirectInfo redirect_info;
redirect_info.status_code = kInternalRedirectStatusCode;
redirect_info.new_method = request_.method;
redirect_info.new_url = redirect_url_;
redirect_info.new_site_for_cookies =
net::SiteForCookies::FromUrl(redirect_url_);
auto head = network::mojom::URLResponseHead::New();
std::string headers = base::StringPrintf(
"HTTP/1.1 %i Internal Redirect\n"
"Location: %s\n"
"Non-Authoritative-Reason: WebRequest API\n\n",
kInternalRedirectStatusCode, redirect_url_.spec().c_str());
// Cross-origin requests need to modify the Origin header to 'null'. Since
// CorsURLLoader sets |request_initiator| to the Origin request header in
// NetworkService, we need to modify |request_initiator| here to craft the
// Origin header indirectly.
// Following checks implement the step 10 of "4.4. HTTP-redirect fetch",
// https://fetch.spec.whatwg.org/#http-redirect-fetch
if (request_.request_initiator &&
(!url::Origin::Create(redirect_url_)
.IsSameOriginWith(url::Origin::Create(request_.url)) &&
!request_.request_initiator->IsSameOriginWith(
url::Origin::Create(request_.url)))) {
// Reset the initiator to pretend tainted origin flag of the spec is set.
request_.request_initiator = url::Origin();
}
head->headers = base::MakeRefCounted<net::HttpResponseHeaders>(
net::HttpUtil::AssembleRawHeaders(headers));
head->encoded_data_length = 0;
current_response_ = std::move(head);
ContinueToBeforeRedirect(redirect_info, net::OK);
}
void ProxyingURLLoaderFactory::InProgressRequest::ContinueToBeforeSendHeaders(
@ -435,6 +478,50 @@ void ProxyingURLLoaderFactory::InProgressRequest::ContinueToBeforeSendHeaders(
net::OK);
}
void ProxyingURLLoaderFactory::InProgressRequest::ContinueToStartRequest(
int error_code) {
if (error_code != net::OK) {
OnRequestError(network::URLLoaderCompletionStatus(error_code));
return;
}
if (current_request_uses_header_client_ && !redirect_url_.is_empty()) {
HandleBeforeRequestRedirect();
return;
}
if (proxied_client_receiver_.is_bound())
proxied_client_receiver_.Resume();
if (header_client_receiver_.is_bound())
header_client_receiver_.Resume();
if (for_cors_preflight_) {
// For CORS preflight requests, we have already started the request in
// the network service. We did block the request by blocking
// |header_client_receiver_|, which we unblocked right above.
return;
}
if (!target_loader_.is_bound() && factory_->target_factory_.is_bound()) {
// No extensions have cancelled us up to this point, so it's now OK to
// initiate the real network request.
uint32_t options = options_;
// Even if this request does not use the header client, future redirects
// might, so we need to set the option on the loader.
if (has_any_extra_headers_listeners_)
options |= network::mojom::kURLLoadOptionUseHeaderClient;
factory_->target_factory_->CreateLoaderAndStart(
mojo::MakeRequest(&target_loader_), network_service_request_id_,
options, request_, proxied_client_receiver_.BindNewPipeAndPassRemote(),
traffic_annotation_);
}
// From here the lifecycle of this request is driven by subsequent events on
// either |proxied_loader_receiver_|, |proxied_client_receiver_|, or
// |header_client_receiver_|.
}
void ProxyingURLLoaderFactory::InProgressRequest::ContinueToSendHeaders(
const std::set<std::string>& removed_headers,
const std::set<std::string>& set_headers,
@ -485,50 +572,6 @@ void ProxyingURLLoaderFactory::InProgressRequest::ContinueToSendHeaders(
ContinueToStartRequest(net::OK);
}
void ProxyingURLLoaderFactory::InProgressRequest::ContinueToStartRequest(
int error_code) {
if (error_code != net::OK) {
OnRequestError(network::URLLoaderCompletionStatus(error_code));
return;
}
if (current_request_uses_header_client_ && !redirect_url_.is_empty()) {
HandleBeforeRequestRedirect();
return;
}
if (proxied_client_receiver_.is_bound())
proxied_client_receiver_.Resume();
if (header_client_receiver_.is_bound())
header_client_receiver_.Resume();
if (for_cors_preflight_) {
// For CORS preflight requests, we have already started the request in
// the network service. We did block the request by blocking
// |header_client_receiver_|, which we unblocked right above.
return;
}
if (!target_loader_.is_bound() && factory_->target_factory_.is_bound()) {
// No extensions have cancelled us up to this point, so it's now OK to
// initiate the real network request.
uint32_t options = options_;
// Even if this request does not use the header client, future redirects
// might, so we need to set the option on the loader.
if (has_any_extra_headers_listeners_)
options |= network::mojom::kURLLoadOptionUseHeaderClient;
factory_->target_factory_->CreateLoaderAndStart(
mojo::MakeRequest(&target_loader_), network_service_request_id_,
options, request_, proxied_client_receiver_.BindNewPipeAndPassRemote(),
traffic_annotation_);
}
// From here the lifecycle of this request is driven by subsequent events on
// either |proxy_loader_binding_|, |proxy_client_receiver_|, or
// |header_client_receiver_|.
}
void ProxyingURLLoaderFactory::InProgressRequest::
ContinueToHandleOverrideHeaders(int error_code) {
if (error_code != net::OK) {
@ -648,59 +691,6 @@ void ProxyingURLLoaderFactory::InProgressRequest::ContinueToBeforeRedirect(
request_.request_body = nullptr;
}
void ProxyingURLLoaderFactory::InProgressRequest::
HandleBeforeRequestRedirect() {
// The extension requested a redirect. Close the connection with the current
// URLLoader and inform the URLLoaderClient the WebRequest API generated a
// redirect. To load |redirect_url_|, a new URLLoader will be recreated
// after receiving FollowRedirect().
// Forgetting to close the connection with the current URLLoader caused
// bugs. The latter doesn't know anything about the redirect. Continuing
// the load with it gives unexpected results. See
// https://crbug.com/882661#c72.
proxied_client_receiver_.reset();
header_client_receiver_.reset();
target_loader_.reset();
constexpr int kInternalRedirectStatusCode = net::HTTP_TEMPORARY_REDIRECT;
net::RedirectInfo redirect_info;
redirect_info.status_code = kInternalRedirectStatusCode;
redirect_info.new_method = request_.method;
redirect_info.new_url = redirect_url_;
redirect_info.new_site_for_cookies =
net::SiteForCookies::FromUrl(redirect_url_);
auto head = network::mojom::URLResponseHead::New();
std::string headers = base::StringPrintf(
"HTTP/1.1 %i Internal Redirect\n"
"Location: %s\n"
"Non-Authoritative-Reason: WebRequest API\n\n",
kInternalRedirectStatusCode, redirect_url_.spec().c_str());
// Cross-origin requests need to modify the Origin header to 'null'. Since
// CorsURLLoader sets |request_initiator| to the Origin request header in
// NetworkService, we need to modify |request_initiator| here to craft the
// Origin header indirectly.
// Following checks implement the step 10 of "4.4. HTTP-redirect fetch",
// https://fetch.spec.whatwg.org/#http-redirect-fetch
if (request_.request_initiator &&
(!url::Origin::Create(redirect_url_)
.IsSameOriginWith(url::Origin::Create(request_.url)) &&
!request_.request_initiator->IsSameOriginWith(
url::Origin::Create(request_.url)))) {
// Reset the initiator to pretend tainted origin flag of the spec is set.
request_.request_initiator = url::Origin();
}
head->headers = base::MakeRefCounted<net::HttpResponseHeaders>(
net::HttpUtil::AssembleRawHeaders(headers));
head->encoded_data_length = 0;
current_response_ = std::move(head);
ContinueToBeforeRedirect(redirect_info, net::OK);
}
void ProxyingURLLoaderFactory::InProgressRequest::
HandleResponseOrRedirectHeaders(net::CompletionOnceCallback continuation) {
override_headers_ = nullptr;
@ -785,8 +775,6 @@ ProxyingURLLoaderFactory::ProxyingURLLoaderFactory(
",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
}
ProxyingURLLoaderFactory::~ProxyingURLLoaderFactory() = default;
bool ProxyingURLLoaderFactory::ShouldIgnoreConnectionsLimit(
const network::ResourceRequest& request) {
for (const auto& domain : ignore_connections_limit_domains_) {
@ -884,11 +872,29 @@ void ProxyingURLLoaderFactory::OnLoaderCreated(
request_it->second->OnLoaderCreated(std::move(receiver));
}
bool ProxyingURLLoaderFactory::IsForServiceWorkerScript() const {
return loader_factory_type_ == content::ContentBrowserClient::
URLLoaderFactoryType::kServiceWorkerScript;
void ProxyingURLLoaderFactory::OnLoaderForCorsPreflightCreated(
const network::ResourceRequest& request,
mojo::PendingReceiver<network::mojom::TrustedHeaderClient> receiver) {
// Please note that the URLLoader is now starting, without waiting for
// additional signals from here. The URLLoader will be blocked before
// sending HTTP request headers (TrustedHeaderClient.OnBeforeSendHeaders),
// but the connection set up will be done before that. This is acceptable from
// Web Request API because the extension has already allowed to set up
// a connection to the same URL (i.e., the actual request), and distinguishing
// two connections for the actual request and the preflight request before
// sending request headers is very difficult.
const uint64_t web_request_id = ++(*request_id_generator_);
auto result = requests_.insert(std::make_pair(
web_request_id, std::make_unique<InProgressRequest>(
this, web_request_id, frame_routing_id_, request)));
result.first->second->OnLoaderCreated(std::move(receiver));
result.first->second->Restart();
}
ProxyingURLLoaderFactory::~ProxyingURLLoaderFactory() = default;
void ProxyingURLLoaderFactory::OnTargetFactoryError() {
target_factory_.reset();
proxy_receivers_.Clear();

View file

@ -5,27 +5,34 @@
#ifndef SHELL_BROWSER_NET_PROXYING_URL_LOADER_FACTORY_H_
#define SHELL_BROWSER_NET_PROXYING_URL_LOADER_FACTORY_H_
#include <cstdint>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/render_frame_host.h"
#include "extensions/browser/api/web_request/web_request_info.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/base/completion_once_callback.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/url_loader.mojom.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "shell/browser/api/electron_api_web_request.h"
#include "shell/browser/net/electron_url_loader_factory.h"
#include "shell/browser/net/web_request_api_interface.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"
namespace electron {
@ -43,7 +50,7 @@ class ProxyingURLLoaderFactory
public network::mojom::URLLoaderClient,
public network::mojom::TrustedHeaderClient {
public:
// For usual requests.
// For usual requests
InProgressRequest(
ProxyingURLLoaderFactory* factory,
int64_t web_request_id,
@ -55,7 +62,7 @@ class ProxyingURLLoaderFactory
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
mojo::PendingReceiver<network::mojom::URLLoader> loader_receiver,
mojo::PendingRemote<network::mojom::URLLoaderClient> client);
// For CORS preflights.
// For CORS preflights
InProgressRequest(ProxyingURLLoaderFactory* factory,
uint64_t request_id,
int32_t frame_routing_id,
@ -76,9 +83,9 @@ class ProxyingURLLoaderFactory
void ResumeReadingBodyFromNet() override;
// network::mojom::URLLoaderClient:
void OnReceiveResponse(network::mojom::URLResponseHeadPtr head) override;
void OnReceiveEarlyHints(
network::mojom::EarlyHintsPtr early_hints) override;
void OnReceiveResponse(network::mojom::URLResponseHeadPtr head) override;
void OnReceiveRedirect(const net::RedirectInfo& redirect_info,
network::mojom::URLResponseHeadPtr head) override;
void OnUploadProgress(int64_t current_position,
@ -114,18 +121,18 @@ class ProxyingURLLoaderFactory
void ContinueToResponseStarted(int error_code);
void ContinueToBeforeRedirect(const net::RedirectInfo& redirect_info,
int error_code);
void HandleBeforeRequestRedirect();
void HandleResponseOrRedirectHeaders(
net::CompletionOnceCallback continuation);
void OnRequestError(const network::URLLoaderCompletionStatus& status);
void HandleBeforeRequestRedirect();
ProxyingURLLoaderFactory* factory_;
ProxyingURLLoaderFactory* const factory_;
network::ResourceRequest request_;
const absl::optional<url::Origin> original_initiator_;
const uint64_t request_id_ = 0;
const int32_t network_service_request_id_ = 0;
const int32_t view_routing_id_ = MSG_ROUTING_NONE;
const int32_t frame_routing_id_ = MSG_ROUTING_NONE;
const int32_t network_service_request_id_ = 0;
const uint32_t options_ = 0;
const net::MutableNetworkTrafficAnnotationTag traffic_annotation_;
mojo::Receiver<network::mojom::URLLoader> proxied_loader_receiver_;
@ -133,14 +140,14 @@ class ProxyingURLLoaderFactory
absl::optional<extensions::WebRequestInfo> info_;
network::mojom::URLResponseHeadPtr current_response_;
scoped_refptr<net::HttpResponseHeaders> override_headers_;
GURL redirect_url_;
mojo::Receiver<network::mojom::URLLoaderClient> proxied_client_receiver_{
this};
network::mojom::URLLoaderPtr target_loader_;
network::mojom::URLResponseHeadPtr current_response_;
scoped_refptr<net::HttpResponseHeaders> override_headers_;
GURL redirect_url_;
const bool for_cors_preflight_ = false;
// If |has_any_extra_headers_listeners_| is set to true, the request will be
@ -192,6 +199,7 @@ class ProxyingURLLoaderFactory
mojo::PendingReceiver<network::mojom::TrustedURLLoaderHeaderClient>
header_client_receiver,
content::ContentBrowserClient::URLLoaderFactoryType loader_factory_type);
~ProxyingURLLoaderFactory() override;
// network::mojom::URLLoaderFactory: