| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/public/test/url_loader_interceptor.h" |
| |
| #include <string> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/containers/unique_ptr_adapters.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/path_service.h" |
| #include "base/run_loop.h" |
| #include "base/strings/strcat.h" |
| #include "base/synchronization/lock.h" |
| #include "base/test/bind.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "build/build_config.h" |
| #include "content/browser/loader/navigation_url_loader_impl.h" |
| #include "content/browser/renderer_host/render_frame_host_impl.h" |
| #include "content/browser/service_worker/embedded_worker_instance.h" |
| #include "content/browser/storage_partition_impl.h" |
| #include "content/browser/url_loader_factory_getter.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/test/mock_render_process_host.h" |
| #include "mojo/public/cpp/bindings/receiver.h" |
| #include "mojo/public/cpp/bindings/receiver_set.h" |
| #include "net/http/http_util.h" |
| #include "net/test/embedded_test_server/request_handler_util.h" |
| #include "services/network/public/cpp/features.h" |
| #include "services/network/public/cpp/parsed_headers.h" |
| #include "services/network/public/mojom/early_hints.mojom.h" |
| #include "services/network/public/mojom/url_loader.mojom.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| base::FilePath GetDataFilePath(const std::string& relative_path) { |
| base::FilePath root_path; |
| CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &root_path)); |
| return root_path.AppendASCII(relative_path); |
| } |
| |
| static std::string ReadFile(const base::FilePath& path) { |
| std::string contents; |
| CHECK(base::ReadFileToString(path, &contents)); |
| return contents; |
| } |
| |
| } // namespace |
| |
| // Part of URLLoaderInterceptor which lives on the IO thread. Outlives |
| // URLLoaderInterceptor. |
| class URLLoaderInterceptor::IOState |
| : public base::RefCountedThreadSafe<URLLoaderInterceptor::IOState, |
| BrowserThread::DeleteOnIOThread> { |
| public: |
| explicit IOState(URLLoaderInterceptor* parent) : parent_(parent) {} |
| |
| IOState(const IOState&) = delete; |
| IOState& operator=(const IOState&) = delete; |
| |
| void Initialize( |
| const URLLoaderCompletionStatusCallback& completion_status_callback, |
| base::OnceClosure closure); |
| |
| // Called when a RenderProcessHostWrapper's binding has an error. |
| void RenderProcessHostWrapperBindingError(RenderProcessHostWrapper* wrapper); |
| |
| // Unsets the parent pointer. Prevents URLLoaderInterceptor::Intercept from |
| // being called. |
| void UnsetParent() { |
| base::AutoLock lock(intercept_lock_); |
| parent_ = nullptr; |
| } |
| |
| void Shutdown(base::OnceClosure closure) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| url_loader_factory_getter_wrappers_.clear(); |
| subresource_wrappers_.clear(); |
| navigation_wrappers_.clear(); |
| |
| URLLoaderFactoryGetter::SetGetNetworkFactoryCallbackForTesting( |
| URLLoaderFactoryGetter::GetNetworkFactoryCallback()); |
| if (closure) |
| std::move(closure).Run(); |
| } |
| |
| // Callback on IO thread whenever a |
| // URLLoaderFactoryGetter::GetNetworkContext is called on an object that |
| // doesn't have a test factory set up. |
| void GetNetworkFactoryCallback( |
| scoped_refptr<URLLoaderFactoryGetter> url_loader_factory_getter); |
| |
| void CreateURLLoaderFactoryForRenderProcessHost( |
| mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver, |
| int process_id, |
| mojo::PendingRemote<network::mojom::URLLoaderFactory> original_factory); |
| |
| bool Intercept(RequestParams* params) { |
| // The lock ensures that |URLLoaderInterceptor| can't be deleted while it |
| // is processing an intercept. Before |URLLoaderInterceptor| is deleted, |
| // parent_ is set to null so that requests can't be intercepted after |
| // |URLLoaderInterceptor| is deleted. |
| base::AutoLock lock(intercept_lock_); |
| if (!parent_) |
| return false; |
| return parent_->Intercept(params); |
| } |
| |
| // Callback on IO thread whenever NavigationURLLoaderImpl needs a |
| // URLLoaderFactory with a network::mojom::TrustedURLLoaderHeaderClient or |
| // for a non-network-service scheme. |
| void InterceptNavigationRequestCallback( |
| mojo::PendingReceiver<network::mojom::URLLoaderFactory>* receiver) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| |
| auto proxied_receiver = std::move(*receiver); |
| mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory; |
| *receiver = target_factory.InitWithNewPipeAndPassReceiver(); |
| |
| navigation_wrappers_.emplace( |
| std::make_unique<URLLoaderFactoryNavigationWrapper>( |
| std::move(proxied_receiver), std::move(target_factory), this)); |
| } |
| |
| URLLoaderCompletionStatusCallback GetCompletionStatusCallback() { |
| return completion_status_callback_; |
| } |
| |
| private: |
| friend struct BrowserThread::DeleteOnThread<BrowserThread::IO>; |
| friend class base::DeleteHelper<IOState>; |
| |
| ~IOState() {} |
| |
| // This lock guarantees that when URLLoaderInterceptor is destroyed, |
| // no intercept callbacks will be called. |
| base::Lock intercept_lock_; |
| raw_ptr<URLLoaderInterceptor> parent_ GUARDED_BY(intercept_lock_); |
| |
| URLLoaderCompletionStatusCallback completion_status_callback_; |
| |
| // For intercepting requests via network service. There is one per |
| // StoragePartition. Only accessed on IO thread. |
| std::set<std::unique_ptr<URLLoaderFactoryGetterWrapper>> |
| url_loader_factory_getter_wrappers_; |
| // For intercepting requests via network service. There is one per factory |
| // created via RenderProcessHost::CreateURLLoaderFactory. Only accessed on IO |
| // thread. |
| std::set<std::unique_ptr<RenderProcessHostWrapper>, base::UniquePtrComparator> |
| subresource_wrappers_; |
| std::set<std::unique_ptr<URLLoaderFactoryNavigationWrapper>> |
| navigation_wrappers_; |
| }; |
| |
| class URLLoaderClientInterceptor : public network::mojom::URLLoaderClient { |
| public: |
| explicit URLLoaderClientInterceptor( |
| base::OnceCallback<network::mojom::URLLoaderFactory*()> factory_getter, |
| URLLoaderInterceptor::RequestParams params, |
| const URLLoaderInterceptor::URLLoaderCompletionStatusCallback& |
| completion_status_callback) |
| : original_client_(std::move(params.client)), |
| completion_status_callback_(std::move(completion_status_callback)), |
| request_url_(params.url_request.url) { |
| std::move(factory_getter) |
| .Run() |
| ->CreateLoaderAndStart( |
| std::move(params.receiver), params.request_id, params.options, |
| std::move(params.url_request), |
| delegating_client_receiver_.BindNewPipeAndPassRemote(), |
| params.traffic_annotation); |
| } |
| |
| void OnReceiveEarlyHints(network::mojom::EarlyHintsPtr early_hints) override { |
| original_client_->OnReceiveEarlyHints(std::move(early_hints)); |
| } |
| |
| void OnReceiveResponse(network::mojom::URLResponseHeadPtr head, |
| mojo::ScopedDataPipeConsumerHandle body) override { |
| original_client_->OnReceiveResponse(std::move(head), std::move(body)); |
| } |
| |
| void OnReceiveRedirect(const net::RedirectInfo& redirect_info, |
| network::mojom::URLResponseHeadPtr head) override { |
| original_client_->OnReceiveRedirect(redirect_info, std::move(head)); |
| } |
| |
| void OnUploadProgress(int64_t current_position, |
| int64_t total_size, |
| base::OnceCallback<void()> callback) override { |
| original_client_->OnUploadProgress(current_position, total_size, |
| std::move(callback)); |
| } |
| |
| void OnReceiveCachedMetadata(mojo_base::BigBuffer data) override { |
| original_client_->OnReceiveCachedMetadata(std::move(data)); |
| } |
| |
| void OnTransferSizeUpdated(int32_t transfer_size_diff) override { |
| original_client_->OnTransferSizeUpdated(transfer_size_diff); |
| } |
| |
| void OnStartLoadingResponseBody( |
| mojo::ScopedDataPipeConsumerHandle body) override { |
| original_client_->OnStartLoadingResponseBody(std::move(body)); |
| } |
| |
| void OnComplete(const network::URLLoaderCompletionStatus& status) override { |
| if (!completion_status_callback_.is_null()) |
| completion_status_callback_.Run(request_url_, status); |
| original_client_->OnComplete(status); |
| } |
| |
| private: |
| mojo::Remote<network::mojom::URLLoaderClient> original_client_; |
| mojo::Receiver<network::mojom::URLLoaderClient> delegating_client_receiver_{ |
| this}; |
| URLLoaderInterceptor::URLLoaderCompletionStatusCallback |
| completion_status_callback_; |
| GURL request_url_; |
| }; |
| |
| class URLLoaderInterceptor::Interceptor |
| : public network::mojom::URLLoaderFactory { |
| public: |
| using ProcessIdGetter = base::RepeatingCallback<int()>; |
| using OriginalFactoryGetter = |
| base::RepeatingCallback<network::mojom::URLLoaderFactory*()>; |
| |
| Interceptor(URLLoaderInterceptor::IOState* parent, |
| ProcessIdGetter process_id_getter, |
| OriginalFactoryGetter original_factory_getter) |
| : parent_(parent), |
| process_id_getter_(std::move(process_id_getter)), |
| original_factory_getter_(std::move(original_factory_getter)) { |
| receivers_.set_disconnect_handler(base::BindRepeating( |
| &Interceptor::OnConnectionError, base::Unretained(this))); |
| } |
| |
| Interceptor(const Interceptor&) = delete; |
| Interceptor& operator=(const Interceptor&) = delete; |
| |
| ~Interceptor() override {} |
| |
| void BindReceiver( |
| mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) { |
| receivers_.Add(this, std::move(receiver)); |
| } |
| |
| void SetConnectionErrorHandler(base::OnceClosure handler) { |
| error_handler_ = std::move(handler); |
| } |
| |
| private: |
| // network::mojom::URLLoaderFactory implementation: |
| void CreateLoaderAndStart( |
| mojo::PendingReceiver<network::mojom::URLLoader> receiver, |
| int32_t request_id, |
| uint32_t options, |
| const network::ResourceRequest& url_request, |
| mojo::PendingRemote<network::mojom::URLLoaderClient> client, |
| const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) |
| override { |
| RequestParams params; |
| params.process_id = process_id_getter_.Run(); |
| params.receiver = std::move(receiver); |
| params.request_id = request_id; |
| params.options = options; |
| params.url_request = std::move(url_request); |
| params.client.Bind(std::move(client)); |
| params.traffic_annotation = traffic_annotation; |
| |
| if (parent_->Intercept(¶ms)) |
| return; |
| |
| url_loader_client_interceptors_.push_back( |
| std::make_unique<URLLoaderClientInterceptor>( |
| original_factory_getter_, std::move(params), |
| parent_->GetCompletionStatusCallback())); |
| } |
| |
| void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) |
| override { |
| BindReceiver(std::move(receiver)); |
| } |
| |
| void OnConnectionError() { |
| if (receivers_.empty() && error_handler_) |
| std::move(error_handler_).Run(); |
| } |
| |
| raw_ptr<URLLoaderInterceptor::IOState> parent_; |
| ProcessIdGetter process_id_getter_; |
| OriginalFactoryGetter original_factory_getter_; |
| mojo::ReceiverSet<network::mojom::URLLoaderFactory> receivers_; |
| base::OnceClosure error_handler_; |
| std::vector<std::unique_ptr<URLLoaderClientInterceptor>> |
| url_loader_client_interceptors_; |
| }; |
| |
| // This class intercepts calls to each StoragePartition's URLLoaderFactoryGetter |
| // so that it can intercept frame requests. |
| class URLLoaderInterceptor::URLLoaderFactoryGetterWrapper { |
| public: |
| URLLoaderFactoryGetterWrapper( |
| scoped_refptr<URLLoaderFactoryGetter> url_loader_factory_getter, |
| URLLoaderInterceptor::IOState* parent) |
| : url_loader_factory_getter_(std::move(url_loader_factory_getter)) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| frame_interceptor_ = std::make_unique<Interceptor>( |
| parent, base::BindRepeating([]() { return 0; }), |
| base::BindLambdaForTesting([=]() -> network::mojom::URLLoaderFactory* { |
| return url_loader_factory_getter_ |
| ->original_network_factory_for_testing() |
| ->get(); |
| })); |
| url_loader_factory_getter_->SetNetworkFactoryForTesting( |
| frame_interceptor_.get()); |
| } |
| |
| ~URLLoaderFactoryGetterWrapper() { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| url_loader_factory_getter_->SetNetworkFactoryForTesting(nullptr); |
| } |
| |
| private: |
| std::unique_ptr<Interceptor> frame_interceptor_; |
| scoped_refptr<URLLoaderFactoryGetter> url_loader_factory_getter_; |
| }; |
| |
| class URLLoaderInterceptor::URLLoaderFactoryNavigationWrapper { |
| public: |
| URLLoaderFactoryNavigationWrapper( |
| mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver, |
| mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory, |
| URLLoaderInterceptor::IOState* parent) |
| : target_factory_(std::move(target_factory)) { |
| interceptor_ = std::make_unique<Interceptor>( |
| parent, base::BindRepeating([]() { return 0; }), |
| base::BindLambdaForTesting([=]() -> network::mojom::URLLoaderFactory* { |
| return this->target_factory_.get(); |
| })); |
| interceptor_->BindReceiver(std::move(receiver)); |
| } |
| |
| private: |
| std::unique_ptr<Interceptor> interceptor_; |
| mojo::Remote<network::mojom::URLLoaderFactory> target_factory_; |
| }; |
| |
| // This class intercepts calls to |
| // StoragePartition::GetURLLoaderFactoryForBrowserProcess. |
| class URLLoaderInterceptor::BrowserProcessWrapper { |
| public: |
| BrowserProcessWrapper( |
| mojo::PendingReceiver<network::mojom::URLLoaderFactory> factory_receiver, |
| URLLoaderInterceptor::IOState* parent, |
| mojo::PendingRemote<network::mojom::URLLoaderFactory> original_factory) |
| : interceptor_( |
| parent, |
| base::BindRepeating([]() { return 0; }), |
| base::BindRepeating(&BrowserProcessWrapper::GetOriginalFactory, |
| base::Unretained(this))), |
| original_factory_(std::move(original_factory)) { |
| interceptor_.BindReceiver(std::move(factory_receiver)); |
| } |
| |
| BrowserProcessWrapper(const BrowserProcessWrapper&) = delete; |
| BrowserProcessWrapper& operator=(const BrowserProcessWrapper&) = delete; |
| |
| ~BrowserProcessWrapper() {} |
| |
| private: |
| network::mojom::URLLoaderFactory* GetOriginalFactory() { |
| return original_factory_.get(); |
| } |
| |
| Interceptor interceptor_; |
| mojo::Remote<network::mojom::URLLoaderFactory> original_factory_; |
| }; |
| |
| // This class is used (e.g. sent in a RenderFrame commit message, or used to |
| // fetch a worker's main script) so it can intercept requests that normally |
| // would be handled by the network service factory created via |
| // RenderProcessHost::CreateURLLoaderFactory. |
| class URLLoaderInterceptor::RenderProcessHostWrapper { |
| public: |
| RenderProcessHostWrapper( |
| mojo::PendingReceiver<network::mojom::URLLoaderFactory> factory_receiver, |
| int process_id, |
| URLLoaderInterceptor::IOState* parent, |
| mojo::PendingRemote<network::mojom::URLLoaderFactory> original_factory) |
| : interceptor_( |
| parent, |
| base::BindRepeating([](int process_id) { return process_id; }, |
| process_id), |
| base::BindRepeating(&RenderProcessHostWrapper::GetOriginalFactory, |
| base::Unretained(this))), |
| original_factory_(std::move(original_factory)) { |
| interceptor_.BindReceiver(std::move(factory_receiver)); |
| interceptor_.SetConnectionErrorHandler(base::BindOnce( |
| &URLLoaderInterceptor::IOState::RenderProcessHostWrapperBindingError, |
| base::Unretained(parent), this)); |
| } |
| |
| RenderProcessHostWrapper(const RenderProcessHostWrapper&) = delete; |
| RenderProcessHostWrapper& operator=(const RenderProcessHostWrapper&) = delete; |
| |
| ~RenderProcessHostWrapper() {} |
| |
| private: |
| network::mojom::URLLoaderFactory* GetOriginalFactory() { |
| return original_factory_.get(); |
| } |
| |
| Interceptor interceptor_; |
| mojo::Remote<network::mojom::URLLoaderFactory> original_factory_; |
| }; |
| |
| URLLoaderInterceptor::RequestParams::RequestParams() = default; |
| URLLoaderInterceptor::RequestParams::~RequestParams() = default; |
| URLLoaderInterceptor::RequestParams::RequestParams(RequestParams&& other) = |
| default; |
| URLLoaderInterceptor::RequestParams& URLLoaderInterceptor::RequestParams:: |
| operator=(RequestParams&& other) = default; |
| |
| URLLoaderInterceptor::URLLoaderInterceptor( |
| InterceptCallback intercept_callback, |
| const URLLoaderCompletionStatusCallback& completion_status_callback, |
| base::OnceClosure ready_callback) |
| : callback_(std::move(intercept_callback)), |
| io_thread_(base::MakeRefCounted<IOState>(this)) { |
| DCHECK(!BrowserThread::IsThreadInitialized(BrowserThread::UI) || |
| BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| use_runloop_ = !ready_callback; |
| RenderProcessHostImpl::SetNetworkFactoryForTesting(base::BindRepeating( |
| &URLLoaderInterceptor::CreateURLLoaderFactoryForRenderProcessHost, |
| base::Unretained(this))); |
| MockRenderProcessHost::SetNetworkFactory(base::BindRepeating( |
| &URLLoaderInterceptor::CreateURLLoaderFactoryForRenderProcessHost, |
| base::Unretained(this))); |
| |
| StoragePartitionImpl:: |
| SetGetURLLoaderFactoryForBrowserProcessCallbackForTesting( |
| base::BindRepeating( |
| &URLLoaderInterceptor::GetURLLoaderFactoryForBrowserProcess, |
| base::Unretained(this))); |
| |
| NavigationURLLoaderImpl::SetURLLoaderFactoryInterceptorForTesting( |
| base::BindRepeating( |
| &URLLoaderInterceptor::InterceptNavigationRequestCallback, |
| base::Unretained(this))); |
| |
| if (BrowserThread::IsThreadInitialized(BrowserThread::IO)) { |
| if (use_runloop_) { |
| base::RunLoop run_loop; |
| GetIOThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce(&URLLoaderInterceptor::IOState::Initialize, io_thread_, |
| std::move(completion_status_callback), |
| run_loop.QuitClosure())); |
| run_loop.Run(); |
| } else { |
| base::OnceClosure wrapped_callback = base::BindOnce( |
| [](base::OnceClosure callback) { |
| GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(callback)); |
| }, |
| std::move(ready_callback)); |
| |
| GetIOThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce(&URLLoaderInterceptor::IOState::Initialize, io_thread_, |
| std::move(completion_status_callback), |
| std::move(wrapped_callback))); |
| } |
| } else { |
| io_thread_->Initialize(std::move(completion_status_callback), |
| std::move(ready_callback)); |
| } |
| } |
| |
| URLLoaderInterceptor::~URLLoaderInterceptor() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| io_thread_->UnsetParent(); |
| |
| RenderProcessHostImpl::SetNetworkFactoryForTesting( |
| RenderProcessHostImpl::CreateNetworkFactoryCallback()); |
| |
| StoragePartitionImpl:: |
| SetGetURLLoaderFactoryForBrowserProcessCallbackForTesting( |
| StoragePartitionImpl::CreateNetworkFactoryCallback()); |
| |
| NavigationURLLoaderImpl::SetURLLoaderFactoryInterceptorForTesting( |
| NavigationURLLoaderImpl::URLLoaderFactoryInterceptor()); |
| |
| MockRenderProcessHost::SetNetworkFactory( |
| MockRenderProcessHost::CreateNetworkFactoryCallback()); |
| |
| if (use_runloop_) { |
| base::RunLoop run_loop; |
| GetIOThreadTaskRunner({})->PostTask( |
| FROM_HERE, base::BindOnce(&URLLoaderInterceptor::IOState::Shutdown, |
| io_thread_, run_loop.QuitClosure())); |
| run_loop.Run(); |
| } else { |
| GetIOThreadTaskRunner({})->PostTask( |
| FROM_HERE, base::BindOnce(&URLLoaderInterceptor::IOState::Shutdown, |
| io_thread_, base::OnceClosure())); |
| } |
| } |
| |
| const GURL& URLLoaderInterceptor::GetLastRequestURL() { |
| base::AutoLock lock(last_request_lock_); |
| return last_request_url_; |
| } |
| |
| const net::HttpRequestHeaders& URLLoaderInterceptor::GetLastRequestHeaders() { |
| base::AutoLock lock(last_request_lock_); |
| return last_request_headers_; |
| } |
| |
| void URLLoaderInterceptor::SetLastRequestURL(const GURL& url) { |
| base::AutoLock lock(last_request_lock_); |
| last_request_url_ = url; |
| } |
| |
| void URLLoaderInterceptor::SetLastRequestHeaders( |
| const net::HttpRequestHeaders& headers) { |
| base::AutoLock lock(last_request_lock_); |
| last_request_headers_ = headers; |
| } |
| |
| // static |
| std::unique_ptr<URLLoaderInterceptor> |
| URLLoaderInterceptor::ServeFilesFromDirectoryAtOrigin( |
| const std::string& relative_base_path, |
| const GURL& origin, |
| base::RepeatingCallback<void(const GURL&)> callback) { |
| return std::make_unique<URLLoaderInterceptor>(base::BindLambdaForTesting( |
| [=](content::URLLoaderInterceptor::RequestParams* params) -> bool { |
| // Ignore requests for other origins. |
| if (params->url_request.url.DeprecatedGetOriginAsURL() != |
| origin.DeprecatedGetOriginAsURL()) |
| return false; |
| |
| // Remove the leading slash from the url path, so that it can be |
| // treated as a relative path by base::FilePath::AppendASCII. |
| auto path = base::TrimString(params->url_request.url.path_piece(), "/", |
| base::TRIM_LEADING); |
| |
| // URLLoaderInterceptor insists that all files exist unless |
| // explicitly said to be failing. Many browsertests fetch |
| // nonessential urls like favicons, so just ignore missing files |
| // entirely, to behave more like net::test::EmbeddedTestServer. |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| auto full_path = GetDataFilePath(relative_base_path).AppendASCII(path); |
| if (!base::PathExists(full_path)) |
| return false; |
| |
| callback.Run(params->url_request.url); |
| content::URLLoaderInterceptor::WriteResponse( |
| full_path, params->client.get(), /*headers=*/nullptr, |
| /*ssl_info=*/absl::nullopt, /*url=*/params->url_request.url); |
| return true; |
| })); |
| } |
| |
| void URLLoaderInterceptor::WriteResponse( |
| base::StringPiece headers, |
| base::StringPiece body, |
| network::mojom::URLLoaderClient* client, |
| absl::optional<net::SSLInfo> ssl_info, |
| absl::optional<GURL> url) { |
| net::HttpResponseInfo info; |
| info.headers = base::MakeRefCounted<net::HttpResponseHeaders>( |
| net::HttpUtil::AssembleRawHeaders(headers)); |
| auto response = network::mojom::URLResponseHead::New(); |
| response->headers = info.headers; |
| response->headers->GetMimeType(&response->mime_type); |
| if (url.has_value()) { |
| response->parsed_headers = |
| network::PopulateParsedHeaders(response->headers.get(), *url); |
| } |
| response->ssl_info = std::move(ssl_info); |
| client->OnReceiveResponse(std::move(response), |
| mojo::ScopedDataPipeConsumerHandle()); |
| |
| CHECK_EQ(WriteResponseBody(body, client), MOJO_RESULT_OK); |
| } |
| |
| void URLLoaderInterceptor::WriteResponse( |
| const std::string& relative_path, |
| network::mojom::URLLoaderClient* client, |
| const std::string* headers, |
| absl::optional<net::SSLInfo> ssl_info, |
| absl::optional<GURL> url) { |
| return WriteResponse(GetDataFilePath(relative_path), client, headers, |
| std::move(ssl_info), std::move(url)); |
| } |
| |
| void URLLoaderInterceptor::WriteResponse( |
| const base::FilePath& file_path, |
| network::mojom::URLLoaderClient* client, |
| const std::string* headers, |
| absl::optional<net::SSLInfo> ssl_info, |
| absl::optional<GURL> url) { |
| base::ScopedAllowBlockingForTesting allow_io; |
| std::string headers_str; |
| if (headers) { |
| headers_str = *headers; |
| } else { |
| base::FilePath headers_path( |
| file_path.AddExtension(net::test_server::kMockHttpHeadersExtension)); |
| if (base::PathExists(headers_path)) { |
| headers_str = ReadFile(headers_path); |
| } else { |
| headers_str = "HTTP/1.0 200 OK\nContent-type: " + |
| net::test_server::GetContentType(file_path) + "\n\n"; |
| } |
| } |
| WriteResponse(headers_str, ReadFile(file_path), client, std::move(ssl_info), |
| std::move(url)); |
| } |
| |
| MojoResult URLLoaderInterceptor::WriteResponseBody( |
| base::StringPiece body, |
| network::mojom::URLLoaderClient* client) { |
| mojo::ScopedDataPipeProducerHandle producer_handle; |
| mojo::ScopedDataPipeConsumerHandle consumer_handle; |
| |
| MojoCreateDataPipeOptions options; |
| options.struct_size = sizeof(MojoCreateDataPipeOptions); |
| options.flags = MOJO_CREATE_DATA_PIPE_FLAG_NONE; |
| options.element_num_bytes = 1; |
| options.capacity_num_bytes = body.size(); |
| |
| MojoResult result = |
| CreateDataPipe(&options, producer_handle, consumer_handle); |
| if (result != MOJO_RESULT_OK) { |
| return result; |
| } |
| |
| uint32_t bytes_written = body.size(); |
| result = producer_handle->WriteData(body.data(), &bytes_written, |
| MOJO_WRITE_DATA_FLAG_ALL_OR_NONE); |
| if (result != MOJO_RESULT_OK) { |
| return result; |
| } |
| |
| client->OnStartLoadingResponseBody(std::move(consumer_handle)); |
| |
| network::URLLoaderCompletionStatus status; |
| status.decoded_body_length = body.size(); |
| status.error_code = net::OK; |
| client->OnComplete(status); |
| |
| return MOJO_RESULT_OK; |
| } |
| |
| void URLLoaderInterceptor::CreateURLLoaderFactoryForRenderProcessHost( |
| mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver, |
| int process_id, |
| mojo::PendingRemote<network::mojom::URLLoaderFactory> original_factory) { |
| if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { |
| GetIOThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| &URLLoaderInterceptor::CreateURLLoaderFactoryForRenderProcessHost, |
| base::Unretained(this), std::move(receiver), process_id, |
| std::move(original_factory))); |
| return; |
| } |
| io_thread_->CreateURLLoaderFactoryForRenderProcessHost( |
| std::move(receiver), process_id, std::move(original_factory)); |
| } |
| |
| mojo::PendingRemote<network::mojom::URLLoaderFactory> |
| URLLoaderInterceptor::GetURLLoaderFactoryForBrowserProcess( |
| mojo::PendingRemote<network::mojom::URLLoaderFactory> original_factory) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| mojo::PendingRemote<network::mojom::URLLoaderFactory> loader_factory; |
| browser_process_interceptors_.emplace(std::make_unique<BrowserProcessWrapper>( |
| loader_factory.InitWithNewPipeAndPassReceiver(), io_thread_.get(), |
| std::move(original_factory))); |
| return loader_factory; |
| } |
| |
| void URLLoaderInterceptor::InterceptNavigationRequestCallback( |
| mojo::PendingReceiver<network::mojom::URLLoaderFactory>* receiver) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| auto proxied_receiver = std::move(*receiver); |
| mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory; |
| *receiver = target_factory.InitWithNewPipeAndPassReceiver(); |
| |
| navigation_wrappers_.emplace( |
| std::make_unique<URLLoaderFactoryNavigationWrapper>( |
| std::move(proxied_receiver), std::move(target_factory), |
| io_thread_.get())); |
| } |
| |
| bool URLLoaderInterceptor::Intercept(RequestParams* params) { |
| if (callback_.Run(params)) { |
| // Only set the last request url and headers if the request was actually |
| // processed by the interceptor. |
| SetLastRequestURL(params->url_request.url); |
| SetLastRequestHeaders(params->url_request.headers); |
| return true; |
| } |
| |
| // mock.failed.request is a special request whereby the query indicates what |
| // error code to respond with. |
| if (params->url_request.url.DomainIs("mock.failed.request")) { |
| std::string query = params->url_request.url.query(); |
| std::string error_code = query.substr(query.find("=") + 1); |
| |
| int error = 0; |
| base::StringToInt(error_code, &error); |
| network::URLLoaderCompletionStatus status; |
| status.error_code = error; |
| params->client->OnComplete(status); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void URLLoaderInterceptor::IOState::RenderProcessHostWrapperBindingError( |
| RenderProcessHostWrapper* wrapper) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| auto it = subresource_wrappers_.find(wrapper); |
| DCHECK(it != subresource_wrappers_.end()); |
| subresource_wrappers_.erase(it); |
| } |
| |
| void URLLoaderInterceptor::IOState::Initialize( |
| const URLLoaderCompletionStatusCallback& completion_status_callback, |
| base::OnceClosure closure) { |
| completion_status_callback_ = std::move(completion_status_callback); |
| URLLoaderFactoryGetter::SetGetNetworkFactoryCallbackForTesting( |
| base::BindRepeating( |
| &URLLoaderInterceptor::IOState::GetNetworkFactoryCallback, |
| base::Unretained(this))); |
| |
| if (closure) |
| std::move(closure).Run(); |
| } |
| |
| void URLLoaderInterceptor::IOState::GetNetworkFactoryCallback( |
| scoped_refptr<URLLoaderFactoryGetter> url_loader_factory_getter) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| url_loader_factory_getter_wrappers_.emplace( |
| std::make_unique<URLLoaderFactoryGetterWrapper>(url_loader_factory_getter, |
| this)); |
| } |
| |
| void URLLoaderInterceptor::IOState::CreateURLLoaderFactoryForRenderProcessHost( |
| mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver, |
| int process_id, |
| mojo::PendingRemote<network::mojom::URLLoaderFactory> original_factory) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| subresource_wrappers_.emplace(std::make_unique<RenderProcessHostWrapper>( |
| std::move(receiver), process_id, this, std::move(original_factory))); |
| } |
| |
| // static |
| std::unique_ptr<content::URLLoaderInterceptor> |
| URLLoaderInterceptor::SetupRequestFailForURL(const GURL& url, |
| net::Error error, |
| base::OnceClosure ready_callback) { |
| return std::make_unique<content::URLLoaderInterceptor>( |
| base::BindRepeating( |
| [](const GURL& url, net::Error error, |
| content::URLLoaderInterceptor::RequestParams* params) { |
| if (params->url_request.url != url) |
| return false; |
| params->client->OnComplete( |
| network::URLLoaderCompletionStatus(error)); |
| return true; |
| }, |
| url, error), |
| URLLoaderCompletionStatusCallback(), std::move(ready_callback)); |
| } |
| |
| } // namespace content |