Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: web request support proxying websocket (#21308) #21754

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions filenames.gni
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,9 @@ filenames = {
"shell/browser/net/cert_verifier_client.h",
"shell/browser/net/proxying_url_loader_factory.cc",
"shell/browser/net/proxying_url_loader_factory.h",
"shell/browser/net/proxying_websocket.cc",
"shell/browser/net/proxying_websocket.h",
"shell/browser/api/atom_web_request_api.h",
"shell/browser/net/network_context_service_factory.cc",
"shell/browser/net/network_context_service_factory.h",
"shell/browser/net/network_context_service.cc",
Expand Down
22 changes: 21 additions & 1 deletion shell/browser/api/atom_api_web_request_ns.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
#include "gin/wrappable.h"
#include "native_mate/dictionary.h"
#include "native_mate/handle.h"
#include "shell/browser/net/proxying_url_loader_factory.h"
#include "shell/browser/api/atom_web_request_api.h"

namespace content {
class BrowserContext;
Expand All @@ -25,6 +25,26 @@ namespace electron {

namespace api {

class RequestIDGenerator
: public base::RefCountedThreadSafe<RequestIDGenerator> {
public:
RequestIDGenerator() = default;
int64_t Generate() {
// DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
return ++id_;
}

private:
friend class base::RefCountedThreadSafe<RequestIDGenerator>;
~RequestIDGenerator() {}

// Although this initialization can be done in a thread other than the IO
// thread, we expect at least one memory barrier before actually calling
// Generate in the IO thread, so we don't protect the variable with a lock.
int64_t id_ = 0;
DISALLOW_COPY_AND_ASSIGN(RequestIDGenerator);
};

class WebRequestNS : public gin::Wrappable<WebRequestNS>, public WebRequestAPI {
public:
static gin::WrapperInfo kWrapperInfo;
Expand Down
52 changes: 52 additions & 0 deletions shell/browser/api/atom_web_request_api.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#ifndef SHELL_BROWSER_API_ATOM_WEB_REQUEST_API_H_
#define SHELL_BROWSER_API_ATOM_WEB_REQUEST_API_H_

#include "extensions/browser/api/web_request/web_request_info.h"
#include "services/network/public/cpp/resource_request.h"

namespace electron {

// Defines the interface for WebRequest API, implemented by api::WebRequestNS.
class WebRequestAPI {
public:
virtual ~WebRequestAPI() {}

using BeforeSendHeadersCallback =
base::OnceCallback<void(const std::set<std::string>& removed_headers,
const std::set<std::string>& set_headers,
int error_code)>;

virtual bool HasListener() const = 0;
virtual int OnBeforeRequest(extensions::WebRequestInfo* info,
const network::ResourceRequest& request,
net::CompletionOnceCallback callback,
GURL* new_url) = 0;
virtual int OnBeforeSendHeaders(extensions::WebRequestInfo* info,
const network::ResourceRequest& request,
BeforeSendHeadersCallback callback,
net::HttpRequestHeaders* headers) = 0;
virtual int OnHeadersReceived(
extensions::WebRequestInfo* info,
const network::ResourceRequest& request,
net::CompletionOnceCallback callback,
const net::HttpResponseHeaders* original_response_headers,
scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
GURL* allowed_unsafe_redirect_url) = 0;
virtual void OnSendHeaders(extensions::WebRequestInfo* info,
const network::ResourceRequest& request,
const net::HttpRequestHeaders& headers) = 0;
virtual void OnBeforeRedirect(extensions::WebRequestInfo* info,
const network::ResourceRequest& request,
const GURL& new_location) = 0;
virtual void OnResponseStarted(extensions::WebRequestInfo* info,
const network::ResourceRequest& request) = 0;
virtual void OnErrorOccurred(extensions::WebRequestInfo* info,
const network::ResourceRequest& request,
int net_error) = 0;
virtual void OnCompleted(extensions::WebRequestInfo* info,
const network::ResourceRequest& request,
int net_error) = 0;
virtual void OnRequestWillBeDestroyed(extensions::WebRequestInfo* info) = 0;
};
} // namespace electron
#endif // SHELL_BROWSER_API_ATOM_WEB_REQUEST_API_H_
44 changes: 41 additions & 3 deletions shell/browser/atom_browser_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
#include "shell/browser/net/network_context_service.h"
#include "shell/browser/net/network_context_service_factory.h"
#include "shell/browser/net/proxying_url_loader_factory.h"
#include "shell/browser/net/proxying_websocket.h"
#include "shell/browser/net/system_network_context_manager.h"
#include "shell/browser/notifications/notification_presenter.h"
#include "shell/browser/notifications/platform_notification_service.h"
Expand Down Expand Up @@ -183,6 +184,7 @@ void AtomBrowserClient::SetApplicationLocale(const std::string& locale) {
AtomBrowserClient::AtomBrowserClient() {
DCHECK(!g_browser_client);
g_browser_client = this;
request_id_generator_ = base::MakeRefCounted<api::RequestIDGenerator>();
}

AtomBrowserClient::~AtomBrowserClient() {
Expand Down Expand Up @@ -982,6 +984,42 @@ void AtomBrowserClient::RegisterNonNetworkSubresourceURLLoaderFactories(
}
}

bool AtomBrowserClient::WillInterceptWebSocket(
content::RenderFrameHost* frame) {
if (!frame) {
return false;
}
v8::Isolate* isolate = v8::Isolate::GetCurrent();
auto* browser_context = frame->GetProcess()->GetBrowserContext();
auto web_request = api::WebRequestNS::FromOrCreate(isolate, browser_context);

// NOTE: Some unit test environments do not initialize
// BrowserContextKeyedAPI factories for e.g. WebRequest.
if (!web_request.get())
return false;

return true;
}

void AtomBrowserClient::CreateWebSocket(
content::RenderFrameHost* frame,
WebSocketFactory factory,
const GURL& url,
const GURL& site_for_cookies,
const base::Optional<std::string>& user_agent,
mojo::PendingRemote<network::mojom::WebSocketHandshakeClient>
handshake_client) {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
auto* browser_context = frame->GetProcess()->GetBrowserContext();
auto web_request = api::WebRequestNS::FromOrCreate(isolate, browser_context);
DCHECK(web_request.get());
ProxyingWebSocket::StartProxying(
web_request.get(), std::move(factory), url, site_for_cookies, user_agent,
std::move(handshake_client), true, frame->GetProcess()->GetID(),
frame->GetRoutingID(), frame->GetLastCommittedOrigin(),
frame->GetProcess()->GetBrowserContext(), request_id_generator_);
}

bool AtomBrowserClient::WillCreateURLLoaderFactory(
content::BrowserContext* browser_context,
content::RenderFrameHost* frame_host,
Expand Down Expand Up @@ -1009,9 +1047,9 @@ bool AtomBrowserClient::WillCreateURLLoaderFactory(
header_client_receiver = header_client->InitWithNewPipeAndPassReceiver();

new ProxyingURLLoaderFactory(
web_request.get(), protocol->intercept_handlers(), render_process_id,
std::move(proxied_receiver), std::move(target_factory_info),
std::move(header_client_receiver), type);
request_id_generator_, web_request.get(), protocol->intercept_handlers(),
render_process_id, std::move(proxied_receiver),
std::move(target_factory_info), std::move(header_client_receiver), type);

if (bypass_redirect_checks)
*bypass_redirect_checks = true;
Expand Down
13 changes: 13 additions & 0 deletions shell/browser/atom_browser_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/render_process_host_observer.h"
#include "electron/buildflags/buildflags.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "net/ssl/client_cert_identity.h"
#include "services/network/public/mojom/websocket.mojom.h"
#include "shell/browser/api/atom_api_web_request_ns.h"

namespace content {
class QuotaPermissionContext;
Expand Down Expand Up @@ -170,6 +173,15 @@ class AtomBrowserClient : public content::ContentBrowserClient,
int render_process_id,
int render_frame_id,
NonNetworkURLLoaderFactoryMap* factories) override;
void CreateWebSocket(
content::RenderFrameHost* frame,
WebSocketFactory factory,
const GURL& url,
const GURL& site_for_cookies,
const base::Optional<std::string>& user_agent,
mojo::PendingRemote<network::mojom::WebSocketHandshakeClient>
handshake_client) override;
bool WillInterceptWebSocket(content::RenderFrameHost*) override;
bool WillCreateURLLoaderFactory(
content::BrowserContext* browser_context,
content::RenderFrameHost* frame,
Expand Down Expand Up @@ -219,6 +231,7 @@ class AtomBrowserClient : public content::ContentBrowserClient,
const content::ChildProcessTerminationInfo& info) override;

private:
scoped_refptr<electron::api::RequestIDGenerator> request_id_generator_;
struct ProcessPreferences {
bool sandbox = false;
bool native_window_open = false;
Expand Down
13 changes: 4 additions & 9 deletions shell/browser/net/proxying_url_loader_factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,6 @@
#include "shell/common/options_switches.h"

namespace electron {

namespace {

int64_t g_request_id = 0;

} // namespace

ProxyingURLLoaderFactory::InProgressRequest::FollowRedirectParams::
FollowRedirectParams() = default;
ProxyingURLLoaderFactory::InProgressRequest::FollowRedirectParams::
Expand Down Expand Up @@ -665,6 +658,7 @@ void ProxyingURLLoaderFactory::InProgressRequest::OnRequestError(
}

ProxyingURLLoaderFactory::ProxyingURLLoaderFactory(
scoped_refptr<api::RequestIDGenerator> request_id_generator,
WebRequestAPI* web_request_api,
const HandlersMap& intercepted_handlers,
int render_process_id,
Expand All @@ -673,7 +667,8 @@ ProxyingURLLoaderFactory::ProxyingURLLoaderFactory(
mojo::PendingReceiver<network::mojom::TrustedURLLoaderHeaderClient>
header_client_receiver,
content::ContentBrowserClient::URLLoaderFactoryType loader_factory_type)
: web_request_api_(web_request_api),
: request_id_generator_(request_id_generator),
web_request_api_(web_request_api),
intercepted_handlers_(intercepted_handlers),
render_process_id_(render_process_id),
loader_factory_type_(loader_factory_type) {
Expand Down Expand Up @@ -751,7 +746,7 @@ void ProxyingURLLoaderFactory::CreateLoaderAndStart(
// 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 = ++g_request_id;
const uint64_t web_request_id = request_id_generator_->Generate();

// Notes: Chromium assumes that requests with zero-ID would never use the
// "extraHeaders" code path, however in Electron requests started from
Expand Down
49 changes: 4 additions & 45 deletions shell/browser/net/proxying_url_loader_factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,53 +19,11 @@
#include "services/network/public/cpp/resource_response.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/url_loader.mojom.h"
#include "shell/browser/api/atom_api_web_request_ns.h"
#include "shell/browser/api/atom_web_request_api.h"
#include "shell/browser/net/atom_url_loader_factory.h"

namespace electron {

// Defines the interface for WebRequest API, implemented by api::WebRequestNS.
class WebRequestAPI {
public:
virtual ~WebRequestAPI() {}

using BeforeSendHeadersCallback =
base::OnceCallback<void(const std::set<std::string>& removed_headers,
const std::set<std::string>& set_headers,
int error_code)>;

virtual bool HasListener() const = 0;
virtual int OnBeforeRequest(extensions::WebRequestInfo* info,
const network::ResourceRequest& request,
net::CompletionOnceCallback callback,
GURL* new_url) = 0;
virtual int OnBeforeSendHeaders(extensions::WebRequestInfo* info,
const network::ResourceRequest& request,
BeforeSendHeadersCallback callback,
net::HttpRequestHeaders* headers) = 0;
virtual int OnHeadersReceived(
extensions::WebRequestInfo* info,
const network::ResourceRequest& request,
net::CompletionOnceCallback callback,
const net::HttpResponseHeaders* original_response_headers,
scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
GURL* allowed_unsafe_redirect_url) = 0;
virtual void OnSendHeaders(extensions::WebRequestInfo* info,
const network::ResourceRequest& request,
const net::HttpRequestHeaders& headers) = 0;
virtual void OnBeforeRedirect(extensions::WebRequestInfo* info,
const network::ResourceRequest& request,
const GURL& new_location) = 0;
virtual void OnResponseStarted(extensions::WebRequestInfo* info,
const network::ResourceRequest& request) = 0;
virtual void OnErrorOccurred(extensions::WebRequestInfo* info,
const network::ResourceRequest& request,
int net_error) = 0;
virtual void OnCompleted(extensions::WebRequestInfo* info,
const network::ResourceRequest& request,
int net_error) = 0;
virtual void OnRequestWillBeDestroyed(extensions::WebRequestInfo* info) = 0;
};

// This class is responsible for following tasks when NetworkService is enabled:
// 1. handling intercepted protocols;
// 2. implementing webRequest module;
Expand Down Expand Up @@ -200,6 +158,7 @@ class ProxyingURLLoaderFactory
};

ProxyingURLLoaderFactory(
scoped_refptr<api::RequestIDGenerator> request_id_generator,
WebRequestAPI* web_request_api,
const HandlersMap& intercepted_handlers,
int render_process_id,
Expand Down Expand Up @@ -238,7 +197,7 @@ class ProxyingURLLoaderFactory
void MaybeDeleteThis();

bool ShouldIgnoreConnectionsLimit(const network::ResourceRequest& request);

scoped_refptr<api::RequestIDGenerator> request_id_generator_;
// Passed from api::WebRequestNS.
WebRequestAPI* web_request_api_;

Expand Down