123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928 |
- // Copyright (c) 2019 GitHub, Inc.
- // Use of this source code is governed by the MIT license that can be
- // found in the LICENSE file.
- #include "shell/browser/net/proxying_url_loader_factory.h"
- #include <memory>
- #include <utility>
- #include "base/command_line.h"
- #include "base/functional/bind.h"
- #include "base/functional/callback_helpers.h"
- #include "base/strings/string_split.h"
- #include "content/public/browser/browser_context.h"
- #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/network/public/cpp/features.h"
- #include "services/network/public/mojom/early_hints.mojom.h"
- #include "services/network/public/mojom/url_response_head.mojom.h"
- #include "shell/browser/net/asar/asar_url_loader.h"
- #include "shell/common/options_switches.h"
- #include "third_party/abseil-cpp/absl/strings/str_format.h"
- #include "url/origin.h"
- namespace electron {
- ProxyingURLLoaderFactory::InProgressRequest::FollowRedirectParams::
- FollowRedirectParams() = default;
- ProxyingURLLoaderFactory::InProgressRequest::FollowRedirectParams::
- ~FollowRedirectParams() = default;
- ProxyingURLLoaderFactory::InProgressRequest::InProgressRequest(
- ProxyingURLLoaderFactory* factory,
- uint64_t web_request_id,
- int32_t frame_routing_id,
- int32_t network_service_request_id,
- uint32_t options,
- const network::ResourceRequest& request,
- const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
- mojo::PendingReceiver<network::mojom::URLLoader> loader_receiver,
- mojo::PendingRemote<network::mojom::URLLoaderClient> client)
- : factory_(factory),
- request_(request),
- original_initiator_(request.request_initiator),
- request_id_(web_request_id),
- network_service_request_id_(network_service_request_id),
- frame_routing_id_(frame_routing_id),
- options_(options),
- traffic_annotation_(traffic_annotation),
- proxied_loader_receiver_(this, std::move(loader_receiver)),
- target_client_(std::move(client)),
- current_response_(network::mojom::URLResponseHead::New()),
- // Always use "extraHeaders" mode to be compatible with old APIs, except
- // when the |request_id_| is zero, which is not supported in Chromium and
- // only happens in Electron when the request is started from net module.
- has_any_extra_headers_listeners_(network_service_request_id != 0) {
- // If there is a client error, clean up the request.
- target_client_.set_disconnect_handler(base::BindOnce(
- &ProxyingURLLoaderFactory::InProgressRequest::OnRequestError,
- weak_factory_.GetWeakPtr(),
- network::URLLoaderCompletionStatus(net::ERR_ABORTED)));
- proxied_loader_receiver_.set_disconnect_handler(base::BindOnce(
- &ProxyingURLLoaderFactory::InProgressRequest::OnRequestError,
- weak_factory_.GetWeakPtr(),
- network::URLLoaderCompletionStatus(net::ERR_ABORTED)));
- }
- ProxyingURLLoaderFactory::InProgressRequest::InProgressRequest(
- ProxyingURLLoaderFactory* factory,
- uint64_t request_id,
- int32_t frame_routing_id,
- const network::ResourceRequest& request)
- : factory_(factory),
- request_(request),
- original_initiator_(request.request_initiator),
- request_id_(request_id),
- frame_routing_id_(frame_routing_id),
- proxied_loader_receiver_(this),
- for_cors_preflight_(true),
- has_any_extra_headers_listeners_(true) {}
- ProxyingURLLoaderFactory::InProgressRequest::~InProgressRequest() {
- // This is important to ensure that no outstanding blocking requests continue
- // to reference state owned by this object.
- if (info_) {
- factory_->web_request_api()->OnRequestWillBeDestroyed(&info_.value());
- }
- if (on_before_send_headers_callback_) {
- std::move(on_before_send_headers_callback_)
- .Run(net::ERR_ABORTED, std::nullopt);
- }
- if (on_headers_received_callback_) {
- std::move(on_headers_received_callback_)
- .Run(net::ERR_ABORTED, std::nullopt, std::nullopt);
- }
- }
- void ProxyingURLLoaderFactory::InProgressRequest::Restart() {
- UpdateRequestInfo();
- RestartInternal();
- }
- void ProxyingURLLoaderFactory::InProgressRequest::UpdateRequestInfo() {
- // Derive a new WebRequestInfo value any time |Restart()| is called, because
- // the details in |request_| may have changed e.g. if we've been redirected.
- // |request_initiator| can be modified on redirects, but we keep the original
- // for |initiator| in the event. See also
- // https://developer.chrome.com/extensions/webRequest#event-onBeforeRequest.
- network::ResourceRequest request_for_info = request_;
- request_for_info.request_initiator = original_initiator_;
- info_.emplace(extensions::WebRequestInfoInitParams(
- request_id_, factory_->render_process_id_, frame_routing_id_,
- factory_->navigation_ui_data_ ? factory_->navigation_ui_data_->DeepCopy()
- : nullptr,
- request_for_info, false,
- !(options_ & network::mojom::kURLLoadOptionSynchronous),
- factory_->IsForServiceWorkerScript(), factory_->navigation_id_));
- current_request_uses_header_client_ =
- factory_->url_loader_header_client_receiver_.is_bound() &&
- (for_cors_preflight_ || network_service_request_id_ != 0) &&
- has_any_extra_headers_listeners_;
- }
- void ProxyingURLLoaderFactory::InProgressRequest::RestartInternal() {
- DCHECK_EQ(info_->url, request_.url)
- << "UpdateRequestInfo must have been called first";
- // If the header client will be used, we start the request immediately, and
- // OnBeforeSendHeaders and OnSendHeaders will be handled there. Otherwise,
- // send these events before the request starts.
- base::RepeatingCallback<void(int)> continuation;
- if (current_request_uses_header_client_) {
- continuation = base::BindRepeating(
- &InProgressRequest::ContinueToStartRequest, weak_factory_.GetWeakPtr());
- } else if (for_cors_preflight_) {
- // In this case we do nothing because extensions should see nothing.
- return;
- } else {
- continuation =
- base::BindRepeating(&InProgressRequest::ContinueToBeforeSendHeaders,
- weak_factory_.GetWeakPtr());
- }
- redirect_url_ = GURL();
- int result = factory_->web_request_api()->OnBeforeRequest(
- &info_.value(), request_, continuation, &redirect_url_);
- if (result == net::ERR_BLOCKED_BY_CLIENT) {
- // The request was cancelled synchronously. Dispatch an error notification
- // and terminate the request.
- network::URLLoaderCompletionStatus status(result);
- OnRequestError(status);
- return;
- }
- if (result == net::ERR_IO_PENDING) {
- // One or more listeners is blocking, so the request must be paused until
- // they respond. |continuation| above will be invoked asynchronously to
- // continue or cancel the request.
- //
- // We pause the receiver here to prevent further client message processing.
- if (proxied_client_receiver_.is_bound())
- proxied_client_receiver_.Pause();
- // Pause the header client, since we want to wait until OnBeforeRequest has
- // finished before processing any future events.
- if (header_client_receiver_.is_bound())
- header_client_receiver_.Pause();
- return;
- }
- DCHECK_EQ(net::OK, result);
- continuation.Run(net::OK);
- }
- void ProxyingURLLoaderFactory::InProgressRequest::FollowRedirect(
- const std::vector<std::string>& removed_headers,
- const net::HttpRequestHeaders& modified_headers,
- const net::HttpRequestHeaders& modified_cors_exempt_headers,
- const std::optional<GURL>& new_url) {
- if (new_url)
- request_.url = new_url.value();
- for (const std::string& header : removed_headers)
- request_.headers.RemoveHeader(header);
- request_.headers.MergeFrom(modified_headers);
- // Call this before checking |current_request_uses_header_client_| as it
- // calculates it.
- UpdateRequestInfo();
- if (target_loader_.is_bound()) {
- // If header_client_ is used, then we have to call FollowRedirect now as
- // that's what triggers the network service calling back to
- // OnBeforeSendHeaders(). Otherwise, don't call FollowRedirect now. Wait for
- // the onBeforeSendHeaders callback(s) to run as these may modify request
- // headers and if so we'll pass these modifications to FollowRedirect.
- if (current_request_uses_header_client_) {
- target_loader_->FollowRedirect(removed_headers, modified_headers,
- modified_cors_exempt_headers, new_url);
- } else {
- auto params = std::make_unique<FollowRedirectParams>();
- params->removed_headers = removed_headers;
- params->modified_headers = modified_headers;
- params->modified_cors_exempt_headers = modified_cors_exempt_headers;
- params->new_url = new_url;
- pending_follow_redirect_params_ = std::move(params);
- }
- }
- RestartInternal();
- }
- void ProxyingURLLoaderFactory::InProgressRequest::SetPriority(
- net::RequestPriority priority,
- int32_t intra_priority_value) {
- if (target_loader_.is_bound())
- target_loader_->SetPriority(priority, intra_priority_value);
- }
- void ProxyingURLLoaderFactory::InProgressRequest::OnReceiveEarlyHints(
- network::mojom::EarlyHintsPtr early_hints) {
- target_client_->OnReceiveEarlyHints(std::move(early_hints));
- }
- void ProxyingURLLoaderFactory::InProgressRequest::OnReceiveResponse(
- network::mojom::URLResponseHeadPtr head,
- mojo::ScopedDataPipeConsumerHandle body,
- std::optional<mojo_base::BigBuffer> cached_metadata) {
- current_body_ = std::move(body);
- current_cached_metadata_ = std::move(cached_metadata);
- if (current_request_uses_header_client_) {
- // Use the headers we got from OnHeadersReceived as that'll contain
- // Set-Cookie if it existed.
- auto saved_headers = current_response_->headers;
- current_response_ = std::move(head);
- current_response_->headers = saved_headers;
- ContinueToResponseStarted(net::OK);
- } else {
- current_response_ = std::move(head);
- HandleResponseOrRedirectHeaders(
- base::BindOnce(&InProgressRequest::ContinueToResponseStarted,
- weak_factory_.GetWeakPtr()));
- }
- }
- void ProxyingURLLoaderFactory::InProgressRequest::OnReceiveRedirect(
- const net::RedirectInfo& redirect_info,
- network::mojom::URLResponseHeadPtr head) {
- // Note: In Electron we don't check IsRedirectSafe.
- if (current_request_uses_header_client_) {
- // Use the headers we got from OnHeadersReceived as that'll contain
- // Set-Cookie if it existed.
- auto saved_headers = current_response_->headers;
- current_response_ = std::move(head);
- // If this redirect is from an HSTS upgrade, OnHeadersReceived will not be
- // called before OnReceiveRedirect, so make sure the saved headers exist
- // before setting them.
- if (saved_headers)
- current_response_->headers = saved_headers;
- ContinueToBeforeRedirect(redirect_info, net::OK);
- } else {
- current_response_ = std::move(head);
- HandleResponseOrRedirectHeaders(
- base::BindOnce(&InProgressRequest::ContinueToBeforeRedirect,
- weak_factory_.GetWeakPtr(), redirect_info));
- }
- }
- void ProxyingURLLoaderFactory::InProgressRequest::OnUploadProgress(
- int64_t current_position,
- int64_t total_size,
- OnUploadProgressCallback callback) {
- target_client_->OnUploadProgress(current_position, total_size,
- std::move(callback));
- }
- void ProxyingURLLoaderFactory::InProgressRequest::OnTransferSizeUpdated(
- int32_t transfer_size_diff) {
- target_client_->OnTransferSizeUpdated(transfer_size_diff);
- }
- void ProxyingURLLoaderFactory::InProgressRequest::OnComplete(
- const network::URLLoaderCompletionStatus& status) {
- if (status.error_code != net::OK) {
- OnRequestError(status);
- return;
- }
- target_client_->OnComplete(status);
- factory_->web_request_api()->OnCompleted(&info_.value(), request_,
- status.error_code);
- // Deletes |this|.
- 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) {
- // When CORS is involved there may be multiple network::URLLoader associated
- // with this InProgressRequest, because CorsURLLoader may create a new
- // network::URLLoader for the same request id in redirect handling - see
- // CorsURLLoader::FollowRedirect. In such a case the old network::URLLoader
- // is going to be detached fairly soon, so we don't need to take care of it.
- // We need this explicit reset to avoid a DCHECK failure in mojo::Receiver.
- header_client_receiver_.reset();
- header_client_receiver_.Bind(std::move(receiver));
- if (for_cors_preflight_) {
- // In this case we don't have |target_loader_| and
- // |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,
- weak_factory_.GetWeakPtr(),
- network::URLLoaderCompletionStatus(net::ERR_FAILED)));
- }
- }
- void ProxyingURLLoaderFactory::InProgressRequest::OnBeforeSendHeaders(
- const net::HttpRequestHeaders& headers,
- OnBeforeSendHeadersCallback callback) {
- if (!current_request_uses_header_client_) {
- std::move(callback).Run(net::OK, std::nullopt);
- return;
- }
- request_.headers = headers;
- on_before_send_headers_callback_ = std::move(callback);
- ContinueToBeforeSendHeaders(net::OK);
- }
- void ProxyingURLLoaderFactory::InProgressRequest::OnHeadersReceived(
- const std::string& headers,
- const net::IPEndPoint& remote_endpoint,
- OnHeadersReceivedCallback callback) {
- if (!current_request_uses_header_client_) {
- std::move(callback).Run(net::OK, std::nullopt, GURL());
- if (for_cors_preflight_) {
- // CORS preflight is supported only when "extraHeaders" is specified.
- // Deletes |this|.
- factory_->RemoveRequest(network_service_request_id_, request_id_);
- }
- return;
- }
- on_headers_received_callback_ = std::move(callback);
- current_response_ = network::mojom::URLResponseHead::New();
- current_response_->headers =
- base::MakeRefCounted<net::HttpResponseHeaders>(headers);
- current_response_->remote_endpoint = remote_endpoint;
- HandleResponseOrRedirectHeaders(
- base::BindOnce(&InProgressRequest::ContinueToHandleOverrideHeaders,
- weak_factory_.GetWeakPtr()));
- }
- 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 = absl::StrFormat(
- "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(
- int error_code) {
- if (error_code != net::OK) {
- OnRequestError(network::URLLoaderCompletionStatus(error_code));
- return;
- }
- if (!current_request_uses_header_client_ && !redirect_url_.is_empty()) {
- if (for_cors_preflight_) {
- // CORS preflight doesn't support redirect.
- OnRequestError(network::URLLoaderCompletionStatus(net::ERR_FAILED));
- return;
- }
- HandleBeforeRequestRedirect();
- return;
- }
- if (proxied_client_receiver_.is_bound())
- proxied_client_receiver_.Resume();
- auto continuation = base::BindRepeating(
- &InProgressRequest::ContinueToSendHeaders, weak_factory_.GetWeakPtr());
- // Note: In Electron onBeforeSendHeaders is called for all protocols.
- int result = factory_->web_request_api()->OnBeforeSendHeaders(
- &info_.value(), request_, continuation, &request_.headers);
- if (result == net::ERR_BLOCKED_BY_CLIENT) {
- // The request was cancelled synchronously. Dispatch an error notification
- // and terminate the request.
- OnRequestError(network::URLLoaderCompletionStatus(result));
- return;
- }
- if (result == net::ERR_IO_PENDING) {
- // One or more listeners is blocking, so the request must be paused until
- // they respond. |continuation| above will be invoked asynchronously to
- // continue or cancel the request.
- //
- // We pause the receiver here to prevent further client message processing.
- if (proxied_client_receiver_.is_bound())
- proxied_client_receiver_.Resume();
- return;
- }
- DCHECK_EQ(net::OK, result);
- ContinueToSendHeaders(std::set<std::string>(), std::set<std::string>(),
- 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(
- target_loader_.BindNewPipeAndPassReceiver(),
- 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,
- int error_code) {
- if (error_code != net::OK) {
- OnRequestError(network::URLLoaderCompletionStatus(error_code));
- return;
- }
- if (current_request_uses_header_client_) {
- DCHECK(on_before_send_headers_callback_);
- std::move(on_before_send_headers_callback_)
- .Run(error_code, request_.headers);
- } else if (pending_follow_redirect_params_) {
- pending_follow_redirect_params_->removed_headers.insert(
- pending_follow_redirect_params_->removed_headers.end(),
- removed_headers.begin(), removed_headers.end());
- for (auto& set_header : set_headers) {
- auto header = request_.headers.GetHeader(set_header);
- if (header) {
- pending_follow_redirect_params_->modified_headers.SetHeader(
- set_header, header.value());
- } else {
- NOTREACHED();
- }
- }
- if (target_loader_.is_bound()) {
- target_loader_->FollowRedirect(
- pending_follow_redirect_params_->removed_headers,
- pending_follow_redirect_params_->modified_headers,
- pending_follow_redirect_params_->modified_cors_exempt_headers,
- pending_follow_redirect_params_->new_url);
- }
- pending_follow_redirect_params_.reset();
- }
- if (proxied_client_receiver_.is_bound())
- proxied_client_receiver_.Resume();
- // Note: In Electron onSendHeaders is called for all protocols.
- factory_->web_request_api()->OnSendHeaders(&info_.value(), request_,
- request_.headers);
- if (!current_request_uses_header_client_)
- ContinueToStartRequest(net::OK);
- }
- void ProxyingURLLoaderFactory::InProgressRequest::
- ContinueToHandleOverrideHeaders(int error_code) {
- if (error_code != net::OK) {
- OnRequestError(network::URLLoaderCompletionStatus(error_code));
- return;
- }
- DCHECK(on_headers_received_callback_);
- std::optional<std::string> headers;
- if (override_headers_) {
- headers = override_headers_->raw_headers();
- if (current_request_uses_header_client_) {
- // Make sure to update current_response_, since when OnReceiveResponse
- // is called we will not use its headers as it might be missing the
- // Set-Cookie line (as that gets stripped over IPC).
- current_response_->headers = override_headers_;
- }
- }
- if (for_cors_preflight_ && !redirect_url_.is_empty()) {
- OnRequestError(network::URLLoaderCompletionStatus(net::ERR_FAILED));
- return;
- }
- std::move(on_headers_received_callback_).Run(net::OK, headers, redirect_url_);
- override_headers_ = nullptr;
- if (for_cors_preflight_) {
- // If this is for CORS preflight, there is no associated client.
- info_->AddResponseInfoFromResourceResponse(*current_response_);
- // Do not finish proxied preflight requests that require proxy auth.
- // The request is not finished yet, give control back to network service
- // which will start authentication process.
- if (info_->response_code == net::HTTP_PROXY_AUTHENTICATION_REQUIRED)
- return;
- // We notify the completion here, and delete |this|.
- factory_->web_request_api()->OnResponseStarted(&info_.value(), request_);
- factory_->web_request_api()->OnCompleted(&info_.value(), request_, net::OK);
- factory_->RemoveRequest(network_service_request_id_, request_id_);
- return;
- }
- if (proxied_client_receiver_.is_bound())
- proxied_client_receiver_.Resume();
- }
- void ProxyingURLLoaderFactory::InProgressRequest::ContinueToResponseStarted(
- int error_code) {
- DCHECK(!for_cors_preflight_);
- if (error_code != net::OK) {
- OnRequestError(network::URLLoaderCompletionStatus(error_code));
- return;
- }
- DCHECK(!current_request_uses_header_client_ || !override_headers_);
- if (override_headers_)
- current_response_->headers = override_headers_;
- std::string redirect_location;
- if (override_headers_ && override_headers_->IsRedirect(&redirect_location)) {
- // The response headers may have been overridden by an |onHeadersReceived|
- // handler and may have been changed to a redirect. We handle that here
- // instead of acting like regular request completion.
- //
- // Note that we can't actually change how the Network Service handles the
- // original request at this point, so our "redirect" is really just
- // generating an artificial |onBeforeRedirect| event and starting a new
- // request to the Network Service. Our client shouldn't know the difference.
- GURL new_url(redirect_location);
- net::RedirectInfo redirect_info;
- redirect_info.status_code = override_headers_->response_code();
- redirect_info.new_method = request_.method;
- redirect_info.new_url = new_url;
- redirect_info.new_site_for_cookies = net::SiteForCookies::FromUrl(new_url);
- // These will get re-bound if a new request is initiated by
- // |FollowRedirect()|.
- proxied_client_receiver_.reset();
- header_client_receiver_.reset();
- target_loader_.reset();
- ContinueToBeforeRedirect(redirect_info, net::OK);
- return;
- }
- info_->AddResponseInfoFromResourceResponse(*current_response_);
- proxied_client_receiver_.Resume();
- factory_->web_request_api()->OnResponseStarted(&info_.value(), request_);
- target_client_->OnReceiveResponse(current_response_.Clone(),
- std::move(current_body_),
- std::move(current_cached_metadata_));
- }
- void ProxyingURLLoaderFactory::InProgressRequest::ContinueToBeforeRedirect(
- const net::RedirectInfo& redirect_info,
- int error_code) {
- if (error_code != net::OK) {
- OnRequestError(network::URLLoaderCompletionStatus(error_code));
- return;
- }
- info_->AddResponseInfoFromResourceResponse(*current_response_);
- if (proxied_client_receiver_.is_bound())
- proxied_client_receiver_.Resume();
- factory_->web_request_api()->OnBeforeRedirect(&info_.value(), request_,
- redirect_info.new_url);
- target_client_->OnReceiveRedirect(redirect_info, current_response_.Clone());
- request_.url = redirect_info.new_url;
- request_.method = redirect_info.new_method;
- request_.site_for_cookies = redirect_info.new_site_for_cookies;
- request_.referrer = GURL(redirect_info.new_referrer);
- request_.referrer_policy = redirect_info.new_referrer_policy;
- // The request method can be changed to "GET". In this case we need to
- // reset the request body manually.
- if (request_.method == net::HttpRequestHeaders::kGetMethod)
- request_.request_body = nullptr;
- }
- void ProxyingURLLoaderFactory::InProgressRequest::
- HandleResponseOrRedirectHeaders(net::CompletionOnceCallback continuation) {
- override_headers_ = nullptr;
- redirect_url_ = GURL();
- info_->AddResponseInfoFromResourceResponse(*current_response_);
- auto callback_pair = base::SplitOnceCallback(std::move(continuation));
- DCHECK(info_.has_value());
- int result = factory_->web_request_api()->OnHeadersReceived(
- &info_.value(), request_, std::move(callback_pair.first),
- current_response_->headers.get(), &override_headers_, &redirect_url_);
- if (result == net::ERR_BLOCKED_BY_CLIENT) {
- OnRequestError(network::URLLoaderCompletionStatus(result));
- return;
- }
- if (result == net::ERR_IO_PENDING) {
- // One or more listeners is blocking, so the request must be paused until
- // they respond. |continuation| above will be invoked asynchronously to
- // continue or cancel the request.
- //
- // We pause the receiver here to prevent further client message processing.
- if (proxied_client_receiver_.is_bound())
- proxied_client_receiver_.Pause();
- return;
- }
- DCHECK_EQ(net::OK, result);
- std::move(callback_pair.second).Run(net::OK);
- }
- void ProxyingURLLoaderFactory::InProgressRequest::OnRequestError(
- const network::URLLoaderCompletionStatus& status) {
- if (target_client_)
- target_client_->OnComplete(status);
- factory_->web_request_api()->OnErrorOccurred(&info_.value(), request_,
- status.error_code);
- // Deletes |this|.
- factory_->RemoveRequest(network_service_request_id_, request_id_);
- }
- ProxyingURLLoaderFactory::ProxyingURLLoaderFactory(
- WebRequestAPI* web_request_api,
- const HandlersMap& intercepted_handlers,
- int render_process_id,
- int frame_routing_id,
- uint64_t* request_id_generator,
- std::unique_ptr<extensions::ExtensionNavigationUIData> navigation_ui_data,
- std::optional<int64_t> navigation_id,
- mojo::PendingReceiver<network::mojom::URLLoaderFactory> loader_request,
- mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory_remote,
- mojo::PendingReceiver<network::mojom::TrustedURLLoaderHeaderClient>
- header_client_receiver,
- content::ContentBrowserClient::URLLoaderFactoryType loader_factory_type)
- : web_request_api_(web_request_api),
- intercepted_handlers_(intercepted_handlers),
- render_process_id_(render_process_id),
- frame_routing_id_(frame_routing_id),
- request_id_generator_(request_id_generator),
- navigation_ui_data_(std::move(navigation_ui_data)),
- navigation_id_(std::move(navigation_id)),
- loader_factory_type_(loader_factory_type) {
- target_factory_.Bind(std::move(target_factory_remote));
- target_factory_.set_disconnect_handler(base::BindOnce(
- &ProxyingURLLoaderFactory::OnTargetFactoryError, base::Unretained(this)));
- proxy_receivers_.Add(this, std::move(loader_request));
- proxy_receivers_.set_disconnect_handler(base::BindRepeating(
- &ProxyingURLLoaderFactory::OnProxyBindingError, base::Unretained(this)));
- if (header_client_receiver)
- url_loader_header_client_receiver_.Bind(std::move(header_client_receiver));
- ignore_connections_limit_domains_ = base::SplitString(
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- switches::kIgnoreConnectionsLimit),
- ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
- }
- bool ProxyingURLLoaderFactory::ShouldIgnoreConnectionsLimit(
- const network::ResourceRequest& request) {
- for (const auto& domain : ignore_connections_limit_domains_) {
- if (request.url.DomainIs(domain)) {
- return true;
- }
- }
- return false;
- }
- void ProxyingURLLoaderFactory::CreateLoaderAndStart(
- mojo::PendingReceiver<network::mojom::URLLoader> loader,
- int32_t request_id,
- uint32_t options,
- const network::ResourceRequest& original_request,
- mojo::PendingRemote<network::mojom::URLLoaderClient> client,
- const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
- // Take a copy so we can mutate the request.
- network::ResourceRequest request = original_request;
- if (ShouldIgnoreConnectionsLimit(request)) {
- request.priority = net::RequestPriority::MAXIMUM_PRIORITY;
- request.load_flags |= net::LOAD_IGNORE_LIMITS;
- }
- // Check if user has intercepted this scheme.
- bool bypass_custom_protocol_handlers =
- options & kBypassCustomProtocolHandlers;
- if (!bypass_custom_protocol_handlers) {
- auto it = intercepted_handlers_->find(request.url.scheme_piece());
- if (it != intercepted_handlers_->end()) {
- mojo::PendingRemote<network::mojom::URLLoaderFactory> loader_remote;
- this->Clone(loader_remote.InitWithNewPipeAndPassReceiver());
- // <scheme, <type, handler>>
- it->second.second.Run(
- request,
- base::BindOnce(&ElectronURLLoaderFactory::StartLoading,
- std::move(loader), request_id, options, request,
- std::move(client), traffic_annotation,
- std::move(loader_remote), it->second.first));
- return;
- }
- }
- // The loader of ServiceWorker forbids loading scripts from file:// URLs, and
- // Chromium does not provide a way to override this behavior. So in order to
- // make ServiceWorker work with file:// URLs, we have to intercept its
- // requests here.
- if (IsForServiceWorkerScript() && request.url.SchemeIsFile()) {
- asar::CreateAsarURLLoader(
- request, std::move(loader), std::move(client),
- base::MakeRefCounted<net::HttpResponseHeaders>(""));
- return;
- }
- if (!web_request_api()->HasListener()) {
- // Pass-through to the original factory.
- target_factory_->CreateLoaderAndStart(std::move(loader), request_id,
- options, request, std::move(client),
- traffic_annotation);
- return;
- }
- // The request ID doesn't really matter. It just needs to be unique
- // per-BrowserContext so extensions can make sense of it. Note that
- // |network_service_request_id_| by contrast is not necessarily unique, so we
- // don't use it for identity here.
- const uint64_t web_request_id = ++(*request_id_generator_);
- // Notes: Chromium assumes that requests with zero-ID would never use the
- // "extraHeaders" code path, however in Electron requests started from
- // the net module would have zero-ID because they do not have renderer process
- // associated.
- if (request_id)
- network_request_id_to_web_request_id_.emplace(request_id, web_request_id);
- auto result = requests_.emplace(
- web_request_id,
- std::make_unique<InProgressRequest>(
- this, web_request_id, frame_routing_id_, request_id, options, request,
- traffic_annotation, std::move(loader), std::move(client)));
- result.first->second->Restart();
- }
- void ProxyingURLLoaderFactory::Clone(
- mojo::PendingReceiver<network::mojom::URLLoaderFactory> loader_receiver) {
- proxy_receivers_.Add(this, std::move(loader_receiver));
- }
- void ProxyingURLLoaderFactory::OnLoaderCreated(
- int32_t request_id,
- mojo::PendingReceiver<network::mojom::TrustedHeaderClient> receiver) {
- auto it = network_request_id_to_web_request_id_.find(request_id);
- if (it == network_request_id_to_web_request_id_.end())
- return;
- auto request_it = requests_.find(it->second);
- DCHECK(request_it != requests_.end());
- request_it->second->OnLoaderCreated(std::move(receiver));
- }
- 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();
- MaybeDeleteThis();
- }
- void ProxyingURLLoaderFactory::OnProxyBindingError() {
- if (proxy_receivers_.empty())
- target_factory_.reset();
- MaybeDeleteThis();
- }
- void ProxyingURLLoaderFactory::RemoveRequest(int32_t network_service_request_id,
- uint64_t request_id) {
- network_request_id_to_web_request_id_.erase(network_service_request_id);
- requests_.erase(request_id);
- MaybeDeleteThis();
- }
- void ProxyingURLLoaderFactory::MaybeDeleteThis() {
- // Even if all URLLoaderFactory pipes connected to this object have been
- // closed it has to stay alive until all active requests have completed.
- if (target_factory_.is_bound() || !requests_.empty() ||
- !proxy_receivers_.empty())
- return;
- delete this;
- }
- } // namespace electron
|