From c3bb73a711a0b02d8d31fdb9197041117c9bca55 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 16 Aug 2019 10:19:05 +0900 Subject: [PATCH] feat: migrate webRequest module to NetworkService (Part 6) (#19752) * Implement OnBeforeSendHeaders * Pass the request * Handle simple listeners * Handle response listeners * Read responses from listener --- shell/browser/api/atom_api_web_request_ns.cc | 239 +++++++++++++++--- shell/browser/api/atom_api_web_request_ns.h | 38 ++- .../net/proxying_url_loader_factory.cc | 20 +- .../browser/net/proxying_url_loader_factory.h | 24 +- shell/common/gin_converters/net_converter.cc | 31 ++- shell/common/gin_converters/net_converter.h | 9 + 6 files changed, 297 insertions(+), 64 deletions(-) diff --git a/shell/browser/api/atom_api_web_request_ns.cc b/shell/browser/api/atom_api_web_request_ns.cc index 745c253b1fa0..ba7224283740 100644 --- a/shell/browser/api/atom_api_web_request_ns.cc +++ b/shell/browser/api/atom_api_web_request_ns.cc @@ -8,12 +8,16 @@ #include #include +#include "base/stl_util.h" #include "gin/converter.h" #include "gin/dictionary.h" #include "gin/object_template_builder.h" #include "shell/browser/api/atom_api_session.h" +#include "shell/browser/api/atom_api_web_contents.h" #include "shell/browser/atom_browser_context.h" #include "shell/common/gin_converters/callback_converter_gin_adapter.h" +#include "shell/common/gin_converters/gurl_converter.h" +#include "shell/common/gin_converters/net_converter.h" #include "shell/common/gin_converters/std_converter.h" #include "shell/common/gin_converters/value_converter_gin_adapter.h" @@ -32,6 +36,40 @@ struct Converter { } }; +template <> +struct Converter { + static v8::Local ToV8(v8::Isolate* isolate, + content::ResourceType type) { + const char* result; + switch (type) { + case content::ResourceType::kMainFrame: + result = "mainFrame"; + break; + case content::ResourceType::kSubFrame: + result = "subFrame"; + break; + case content::ResourceType::kStylesheet: + result = "stylesheet"; + break; + case content::ResourceType::kScript: + result = "script"; + break; + case content::ResourceType::kImage: + result = "image"; + break; + case content::ResourceType::kObject: + result = "object"; + break; + case content::ResourceType::kXhr: + result = "xhr"; + break; + default: + result = "other"; + } + return StringToV8(isolate, result); + } +}; + } // namespace gin namespace electron { @@ -49,18 +87,106 @@ struct UserData : public base::SupportsUserData::Data { }; // Test whether the URL of |request| matches |patterns|. -bool MatchesFilterCondition(extensions::WebRequestInfo* request, +bool MatchesFilterCondition(extensions::WebRequestInfo* info, const std::set& patterns) { if (patterns.empty()) return true; for (const auto& pattern : patterns) { - if (pattern.MatchesURL(request->url)) + if (pattern.MatchesURL(info->url)) return true; } return false; } +// Overloaded by multiple types to fill the |details| object. +void ToDictionary(gin::Dictionary* details, extensions::WebRequestInfo* info) { + details->Set("id", info->id); + details->Set("url", info->url); + details->Set("method", info->method); + details->Set("timestamp", base::Time::Now().ToDoubleT() * 1000); + if (!info->response_ip.empty()) + details->Set("ip", info->response_ip); + if (info->type.has_value()) + details->Set("resourceType", info->type.value()); + else + details->Set("resourceType", base::StringPiece("other")); + if (info->response_headers) { + details->Set("fromCache", info->response_from_cache); + details->Set("responseHeaders", info->response_headers.get()); + details->Set("statusLine", info->response_headers->GetStatusLine()); + details->Set("statusCode", info->response_headers->response_code()); + } + + auto* web_contents = content::WebContents::FromRenderFrameHost( + content::RenderFrameHost::FromID(info->render_process_id, + info->frame_id)); + int32_t id = api::WebContents::GetIDFromWrappedClass(web_contents); + // id must be greater than zero. + if (id > 0) + details->Set("webContentsId", id); +} + +void ToDictionary(gin::Dictionary* details, + const network::ResourceRequest& request) { + details->Set("referrer", request.referrer); +} + +void ToDictionary(gin::Dictionary* details, + const net::HttpRequestHeaders& headers) { + details->Set("requestHeaders", headers); +} + +void ToDictionary(gin::Dictionary* details, const GURL& location) { + details->Set("redirectURL", location); +} + +void ToDictionary(gin::Dictionary* details, int net_error) { + details->Set("error", net::ErrorToString(net_error)); +} + +// Helper function to fill |details| with arbitrary |args|. +template +void FillDetails(gin::Dictionary* details, Arg arg) { + ToDictionary(details, arg); +} + +template +void FillDetails(gin::Dictionary* details, Arg arg, Args... args) { + ToDictionary(details, arg); + FillDetails(details, args...); +} + +// Fill the native types with the result from the response object. +void ReadFromResponse(v8::Isolate* isolate, + gin::Dictionary* response, + GURL* new_location) { + response->Get("redirectURL", new_location); +} + +void ReadFromResponse(v8::Isolate* isolate, + gin::Dictionary* response, + net::HttpRequestHeaders* headers) { + headers->Clear(); + gin::ConvertFromV8(isolate, gin::ConvertToV8(isolate, *response), headers); +} + +void ReadFromResponse(v8::Isolate* isolate, + gin::Dictionary* response, + const std::pair*, + const std::string>& headers) { + std::string status_line; + if (!response->Get("statusLine", &status_line)) + status_line = headers.second; + v8::Local value; + if (response->Get("responseHeaders", &value)) { + *headers.first = new net::HttpResponseHeaders(""); + (*headers.first)->ReplaceStatusLine(status_line); + gin::Converter::FromV8(isolate, value, + (*headers.first).get()); + } +} + } // namespace gin::WrapperInfo WebRequestNS::kWrapperInfo = {gin::kEmbedderNativeGin}; @@ -113,54 +239,70 @@ const char* WebRequestNS::GetTypeName() { return "WebRequest"; } -int WebRequestNS::OnBeforeRequest(extensions::WebRequestInfo* request, +int WebRequestNS::OnBeforeRequest(extensions::WebRequestInfo* info, + const network::ResourceRequest& request, net::CompletionOnceCallback callback, GURL* new_url) { - return HandleResponseEvent(kOnBeforeRequest, request, std::move(callback), - new_url); + return HandleResponseEvent(kOnBeforeRequest, info, std::move(callback), + new_url, request); } -int WebRequestNS::OnBeforeSendHeaders(extensions::WebRequestInfo* request, +int WebRequestNS::OnBeforeSendHeaders(extensions::WebRequestInfo* info, + const network::ResourceRequest& request, BeforeSendHeadersCallback callback, net::HttpRequestHeaders* headers) { - // TODO(zcbenz): Figure out how to handle this generally. - return net::OK; + return HandleResponseEvent( + kOnBeforeSendHeaders, info, + base::BindOnce(std::move(callback), std::set(), + std::set()), + headers, request); } int WebRequestNS::OnHeadersReceived( - extensions::WebRequestInfo* request, + extensions::WebRequestInfo* info, + const network::ResourceRequest& request, net::CompletionOnceCallback callback, const net::HttpResponseHeaders* original_response_headers, scoped_refptr* override_response_headers, GURL* allowed_unsafe_redirect_url) { - return HandleResponseEvent(kOnHeadersReceived, request, std::move(callback), - original_response_headers, - override_response_headers, - allowed_unsafe_redirect_url); + return HandleResponseEvent( + kOnHeadersReceived, info, std::move(callback), + std::make_pair(override_response_headers, + original_response_headers->GetStatusLine()), + request); } -void WebRequestNS::OnSendHeaders(extensions::WebRequestInfo* request, +void WebRequestNS::OnSendHeaders(extensions::WebRequestInfo* info, + const network::ResourceRequest& request, const net::HttpRequestHeaders& headers) { - HandleSimpleEvent(kOnSendHeaders, request, headers); + HandleSimpleEvent(kOnSendHeaders, info, request, headers); } -void WebRequestNS::OnBeforeRedirect(extensions::WebRequestInfo* request, +void WebRequestNS::OnBeforeRedirect(extensions::WebRequestInfo* info, + const network::ResourceRequest& request, const GURL& new_location) { - HandleSimpleEvent(kOnBeforeRedirect, request, new_location); + HandleSimpleEvent(kOnBeforeRedirect, info, request, new_location); } -void WebRequestNS::OnResponseStarted(extensions::WebRequestInfo* request) { - HandleSimpleEvent(kOnResponseStarted, request); +void WebRequestNS::OnResponseStarted(extensions::WebRequestInfo* info, + const network::ResourceRequest& request) { + HandleSimpleEvent(kOnResponseStarted, info, request); } -void WebRequestNS::OnErrorOccurred(extensions::WebRequestInfo* request, +void WebRequestNS::OnErrorOccurred(extensions::WebRequestInfo* info, + const network::ResourceRequest& request, int net_error) { - HandleSimpleEvent(kOnErrorOccurred, request, net_error); + callbacks_.erase(info->id); + + HandleSimpleEvent(kOnErrorOccurred, info, request, net_error); } -void WebRequestNS::OnCompleted(extensions::WebRequestInfo* request, +void WebRequestNS::OnCompleted(extensions::WebRequestInfo* info, + const network::ResourceRequest& request, int net_error) { - HandleSimpleEvent(kOnCompleted, request, net_error); + callbacks_.erase(info->id); + + HandleSimpleEvent(kOnCompleted, info, request, net_error); } template @@ -199,27 +341,64 @@ void WebRequestNS::SetListener(Event event, template void WebRequestNS::HandleSimpleEvent(SimpleEvent event, - extensions::WebRequestInfo* request, + extensions::WebRequestInfo* request_info, Args... args) { const auto& info = simple_listeners_[event]; - if (!MatchesFilterCondition(request, info.url_patterns)) + if (!MatchesFilterCondition(request_info, info.url_patterns)) return; - // TODO(zcbenz): Invoke the listener. + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::HandleScope handle_scope(isolate); + gin::Dictionary details(isolate, v8::Object::New(isolate)); + FillDetails(&details, request_info, args...); + info.listener.Run(gin::ConvertToV8(isolate, details)); } template int WebRequestNS::HandleResponseEvent(ResponseEvent event, - extensions::WebRequestInfo* request, + extensions::WebRequestInfo* request_info, net::CompletionOnceCallback callback, Out out, Args... args) { const auto& info = response_listeners_[event]; - if (!MatchesFilterCondition(request, info.url_patterns)) + if (!MatchesFilterCondition(request_info, info.url_patterns)) return net::OK; - // TODO(zcbenz): Invoke the listener. - return net::OK; + callbacks_[request_info->id] = std::move(callback); + + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::HandleScope handle_scope(isolate); + gin::Dictionary details(isolate, v8::Object::New(isolate)); + FillDetails(&details, request_info, args...); + + ResponseCallback response = + base::BindOnce(&WebRequestNS::OnListenerResult, + base::Unretained(this), request_info->id, out); + info.listener.Run(gin::ConvertToV8(isolate, details), std::move(response)); + return net::ERR_IO_PENDING; +} + +template +void WebRequestNS::OnListenerResult(uint64_t id, + T out, + v8::Local response) { + if (!base::Contains(callbacks_, id)) + return; + + int result = net::OK; + if (response->IsObject()) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + gin::Dictionary dict(isolate, response.As()); + + bool cancel = false; + dict.Get("cancel", &cancel); + if (cancel) + result = net::ERR_BLOCKED_BY_CLIENT; + else + ReadFromResponse(isolate, &dict, out); + } + + std::move(callbacks_[id]).Run(result); } // static diff --git a/shell/browser/api/atom_api_web_request_ns.h b/shell/browser/api/atom_api_web_request_ns.h index cfd3b23bb311..46f873412ba8 100644 --- a/shell/browser/api/atom_api_web_request_ns.h +++ b/shell/browser/api/atom_api_web_request_ns.h @@ -57,26 +57,35 @@ class WebRequestNS : public gin::Wrappable, public WebRequestAPI { ~WebRequestNS() override; // WebRequestAPI: - int OnBeforeRequest(extensions::WebRequestInfo* request, + int OnBeforeRequest(extensions::WebRequestInfo* info, + const network::ResourceRequest& request, net::CompletionOnceCallback callback, GURL* new_url) override; - int OnBeforeSendHeaders(extensions::WebRequestInfo* request, + int OnBeforeSendHeaders(extensions::WebRequestInfo* info, + const network::ResourceRequest& request, BeforeSendHeadersCallback callback, net::HttpRequestHeaders* headers) override; int OnHeadersReceived( - extensions::WebRequestInfo* request, + extensions::WebRequestInfo* info, + const network::ResourceRequest& request, net::CompletionOnceCallback callback, const net::HttpResponseHeaders* original_response_headers, scoped_refptr* override_response_headers, GURL* allowed_unsafe_redirect_url) override; - void OnSendHeaders(extensions::WebRequestInfo* request, + void OnSendHeaders(extensions::WebRequestInfo* info, + const network::ResourceRequest& request, const net::HttpRequestHeaders& headers) override; - void OnBeforeRedirect(extensions::WebRequestInfo* request, + void OnBeforeRedirect(extensions::WebRequestInfo* info, + const network::ResourceRequest& request, const GURL& new_location) override; - void OnResponseStarted(extensions::WebRequestInfo* request) override; - void OnErrorOccurred(extensions::WebRequestInfo* request, + void OnResponseStarted(extensions::WebRequestInfo* info, + const network::ResourceRequest& request) override; + void OnErrorOccurred(extensions::WebRequestInfo* info, + const network::ResourceRequest& request, int net_error) override; - void OnCompleted(extensions::WebRequestInfo* request, int net_error) override; + void OnCompleted(extensions::WebRequestInfo* info, + const network::ResourceRequest& request, + int net_error) override; enum SimpleEvent { kOnSendHeaders, @@ -91,10 +100,10 @@ class WebRequestNS : public gin::Wrappable, public WebRequestAPI { kOnHeadersReceived, }; - using SimpleListener = base::RepeatingCallback; - using ResponseCallback = base::OnceCallback; + using SimpleListener = base::RepeatingCallback)>; + using ResponseCallback = base::OnceCallback)>; using ResponseListener = - base::RepeatingCallback; + base::RepeatingCallback, ResponseCallback)>; template void SetSimpleListener(gin::Arguments* args); @@ -105,15 +114,18 @@ class WebRequestNS : public gin::Wrappable, public WebRequestAPI { template void HandleSimpleEvent(SimpleEvent event, - extensions::WebRequestInfo* request, + extensions::WebRequestInfo* info, Args... args); template int HandleResponseEvent(ResponseEvent event, - extensions::WebRequestInfo* request, + extensions::WebRequestInfo* info, net::CompletionOnceCallback callback, Out out, Args... args); + template + void OnListenerResult(uint64_t id, T out, v8::Local response); + struct SimpleListenerInfo { std::set url_patterns; SimpleListener listener; diff --git a/shell/browser/net/proxying_url_loader_factory.cc b/shell/browser/net/proxying_url_loader_factory.cc index f63558fabedb..79afbcb75674 100644 --- a/shell/browser/net/proxying_url_loader_factory.cc +++ b/shell/browser/net/proxying_url_loader_factory.cc @@ -105,7 +105,7 @@ void ProxyingURLLoaderFactory::InProgressRequest::RestartInternal() { } redirect_url_ = GURL(); int result = factory_->web_request_api()->OnBeforeRequest( - &info_.value(), continuation, &redirect_url_); + &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. @@ -259,7 +259,8 @@ void ProxyingURLLoaderFactory::InProgressRequest::OnComplete( } target_client_->OnComplete(status); - factory_->web_request_api()->OnCompleted(&info_.value(), status.error_code); + factory_->web_request_api()->OnCompleted(&info_.value(), request_, + status.error_code); // Deletes |this|. factory_->RemoveRequest(network_service_request_id_, request_id_); @@ -318,7 +319,7 @@ void ProxyingURLLoaderFactory::InProgressRequest::ContinueToBeforeSendHeaders( &InProgressRequest::ContinueToSendHeaders, weak_factory_.GetWeakPtr()); // Note: In Electron onBeforeSendHeaders is called for all protocols. int result = factory_->web_request_api()->OnBeforeSendHeaders( - &info_.value(), continuation, &request_.headers); + &info_.value(), request_, continuation, &request_.headers); if (result == net::ERR_BLOCKED_BY_CLIENT) { // The request was cancelled synchronously. Dispatch an error notification @@ -385,7 +386,8 @@ void ProxyingURLLoaderFactory::InProgressRequest::ContinueToSendHeaders( proxied_client_binding_.ResumeIncomingMethodCallProcessing(); // Note: In Electron onSendHeaders is called for all protocols. - factory_->web_request_api()->OnSendHeaders(&info_.value(), request_.headers); + factory_->web_request_api()->OnSendHeaders(&info_.value(), request_, + request_.headers); if (!current_request_uses_header_client_) ContinueToStartRequest(net::OK); @@ -498,7 +500,7 @@ void ProxyingURLLoaderFactory::InProgressRequest::ContinueToResponseStarted( proxied_client_binding_.ResumeIncomingMethodCallProcessing(); - factory_->web_request_api()->OnResponseStarted(&info_.value()); + factory_->web_request_api()->OnResponseStarted(&info_.value(), request_); target_client_->OnReceiveResponse(current_response_); } @@ -515,7 +517,7 @@ void ProxyingURLLoaderFactory::InProgressRequest::ContinueToBeforeRedirect( if (proxied_client_binding_.is_bound()) proxied_client_binding_.ResumeIncomingMethodCallProcessing(); - factory_->web_request_api()->OnBeforeRedirect(&info_.value(), + factory_->web_request_api()->OnBeforeRedirect(&info_.value(), request_, redirect_info.new_url); target_client_->OnReceiveRedirect(redirect_info, current_response_); request_.url = redirect_info.new_url; @@ -610,8 +612,8 @@ void ProxyingURLLoaderFactory::InProgressRequest:: base::AdaptCallbackForRepeating(std::move(continuation)); DCHECK(info_.has_value()); int result = factory_->web_request_api()->OnHeadersReceived( - &info_.value(), copyable_callback, current_response_.headers.get(), - &override_headers_, &redirect_url_); + &info_.value(), request_, copyable_callback, + current_response_.headers.get(), &override_headers_, &redirect_url_); if (result == net::ERR_BLOCKED_BY_CLIENT) { OnRequestError(network::URLLoaderCompletionStatus(result)); return; @@ -636,7 +638,7 @@ void ProxyingURLLoaderFactory::InProgressRequest::OnRequestError( const network::URLLoaderCompletionStatus& status) { if (!request_completed_) { target_client_->OnComplete(status); - factory_->web_request_api()->OnErrorOccurred(&info_.value(), + factory_->web_request_api()->OnErrorOccurred(&info_.value(), request_, status.error_code); } diff --git a/shell/browser/net/proxying_url_loader_factory.h b/shell/browser/net/proxying_url_loader_factory.h index defbcdce13ba..91271a1a599a 100644 --- a/shell/browser/net/proxying_url_loader_factory.h +++ b/shell/browser/net/proxying_url_loader_factory.h @@ -31,26 +31,34 @@ class WebRequestAPI { const std::set& set_headers, int error_code)>; - virtual int OnBeforeRequest(extensions::WebRequestInfo* request, + virtual int OnBeforeRequest(extensions::WebRequestInfo* info, + const network::ResourceRequest& request, net::CompletionOnceCallback callback, GURL* new_url) = 0; - virtual int OnBeforeSendHeaders(extensions::WebRequestInfo* request, + virtual int OnBeforeSendHeaders(extensions::WebRequestInfo* info, + const network::ResourceRequest& request, BeforeSendHeadersCallback callback, net::HttpRequestHeaders* headers) = 0; virtual int OnHeadersReceived( - extensions::WebRequestInfo* request, + extensions::WebRequestInfo* info, + const network::ResourceRequest& request, net::CompletionOnceCallback callback, const net::HttpResponseHeaders* original_response_headers, scoped_refptr* override_response_headers, GURL* allowed_unsafe_redirect_url) = 0; - virtual void OnSendHeaders(extensions::WebRequestInfo* request, + virtual void OnSendHeaders(extensions::WebRequestInfo* info, + const network::ResourceRequest& request, const net::HttpRequestHeaders& headers) = 0; - virtual void OnBeforeRedirect(extensions::WebRequestInfo* request, + virtual void OnBeforeRedirect(extensions::WebRequestInfo* info, + const network::ResourceRequest& request, const GURL& new_location) = 0; - virtual void OnResponseStarted(extensions::WebRequestInfo* request) = 0; - virtual void OnErrorOccurred(extensions::WebRequestInfo* request, + 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* request, + virtual void OnCompleted(extensions::WebRequestInfo* info, + const network::ResourceRequest& request, int net_error) = 0; }; diff --git a/shell/common/gin_converters/net_converter.cc b/shell/common/gin_converters/net_converter.cc index 1172af0024da..9abe06fed07a 100644 --- a/shell/common/gin_converters/net_converter.cc +++ b/shell/common/gin_converters/net_converter.cc @@ -225,6 +225,32 @@ bool Converter::FromV8( return true; } +// static +v8::Local Converter::ToV8( + v8::Isolate* isolate, + const net::HttpRequestHeaders& val) { + gin::Dictionary headers(isolate, v8::Object::New(isolate)); + for (net::HttpRequestHeaders::Iterator it(val); it.GetNext();) + headers.Set(it.name(), it.value()); + return ConvertToV8(isolate, headers); +} + +// static +bool Converter::FromV8(v8::Isolate* isolate, + v8::Local val, + net::HttpRequestHeaders* out) { + base::DictionaryValue dict; + if (!ConvertFromV8(isolate, val, &dict)) + return false; + for (base::DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) { + if (it.value().is_string()) { + std::string value = it.value().GetString(); + out->SetHeader(it.key(), value); + } + } + return true; +} + // static v8::Local Converter::ToV8( v8::Isolate* isolate, @@ -233,10 +259,7 @@ v8::Local Converter::ToV8( dict.Set("method", val.method); dict.Set("url", val.url.spec()); dict.Set("referrer", val.referrer.spec()); - gin::Dictionary headers(isolate, v8::Object::New(isolate)); - for (net::HttpRequestHeaders::Iterator it(val.headers); it.GetNext();) - headers.Set(it.name(), it.value()); - dict.Set("headers", headers); + dict.Set("headers", val.headers); if (val.request_body) { const auto& elements = *val.request_body->elements(); v8::Local arr = v8::Array::New(isolate, elements.size()); diff --git a/shell/common/gin_converters/net_converter.h b/shell/common/gin_converters/net_converter.h index b8038790718a..a4f94e7019ac 100644 --- a/shell/common/gin_converters/net_converter.h +++ b/shell/common/gin_converters/net_converter.h @@ -60,6 +60,15 @@ struct Converter { net::HttpResponseHeaders* out); }; +template <> +struct Converter { + static v8::Local ToV8(v8::Isolate* isolate, + const net::HttpRequestHeaders& headers); + static bool FromV8(v8::Isolate* isolate, + v8::Local val, + net::HttpRequestHeaders* out); +}; + template <> struct Converter { static v8::Local ToV8(v8::Isolate* isolate,