| // 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. |
| |
| #ifndef CONTENT_PUBLIC_TEST_URL_LOADER_INTERCEPTOR_H_ |
| #define CONTENT_PUBLIC_TEST_URL_LOADER_INTERCEPTOR_H_ |
| |
| #include <memory> |
| #include <set> |
| #include <string> |
| |
| #include "base/callback_helpers.h" |
| #include "base/files/file_path.h" |
| #include "base/macros.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/strings/string_piece.h" |
| #include "mojo/public/cpp/bindings/pending_receiver.h" |
| #include "mojo/public/cpp/bindings/pending_remote.h" |
| #include "mojo/public/cpp/bindings/remote.h" |
| #include "net/base/net_errors.h" |
| #include "net/traffic_annotation/network_traffic_annotation.h" |
| #include "services/network/public/mojom/url_loader.mojom.h" |
| #include "services/network/public/mojom/url_loader_factory.mojom.h" |
| |
| namespace content { |
| |
| // Helper class to intercept URLLoaderFactory calls for tests. |
| // This intercepts: |
| // -frame requests (which start from the browser) |
| // -subresource requests from pages, dedicated workers, and shared workers |
| // -by sending the renderer an intermediate URLLoaderFactory |
| // -subresource requests from service workers and requests of non-installed |
| // service worker scripts |
| // -at EmbeddedWorkerInstance |
| // -requests by the browser |
| // -http(s)://mock.failed.request/foo URLs internally, copying the behavior |
| // of net::URLRequestFailedJob |
| // |
| // Prefer not to use this class. In order of ease of use & simplicity: |
| // -if you need to serve static data, use net::test::EmbeddedTestServer and |
| // serve data from the source tree (e.g. in content/test/data) |
| // -if you need to control the response data at runtime, then use |
| // net::test_server::EmbeddedTestServer::RegisterRequestHandler |
| // -if you need to delay when the server sends the response, use |
| // net::test_server::ControllableHttpResponse |
| // -otherwise, if you need full control over the net::Error and/or want to |
| // inspect and/or modify the C++ structs used by URLoader interface, then use |
| // this helper class |
| // |
| // Notes: |
| // -the callback is called on the UI or IO threads depending on the factory |
| // that was hooked |
| // -this is done to avoid changing message order |
| // -intercepting resource requests for subresources changes message order by |
| // definition (since they would normally go directly from renderer->network |
| // service, but now they're routed through the browser). |
| class URLLoaderInterceptor { |
| public: |
| struct RequestParams { |
| RequestParams(); |
| ~RequestParams(); |
| RequestParams(RequestParams&& other); |
| RequestParams& operator=(RequestParams&& other); |
| // This is the process_id of the process that is making the request (0 for |
| // browser process). |
| int process_id; |
| // The following are the parameters to CreateLoaderAndStart. |
| mojo::PendingReceiver<network::mojom::URLLoader> receiver; |
| int32_t request_id; |
| uint32_t options; |
| network::ResourceRequest url_request; |
| mojo::Remote<network::mojom::URLLoaderClient> client; |
| net::MutableNetworkTrafficAnnotationTag traffic_annotation; |
| }; |
| // Function signature for intercept method. |
| // Return true if the request was intercepted. Otherwise this class will |
| // forward the request to the original URLLoaderFactory. |
| using InterceptCallback = |
| base::RepeatingCallback<bool(RequestParams* params)>; |
| |
| // Function signature for a loading completion method. |
| // This class will listen on loading completion responses from the network, |
| // invoke this callback, and delegate the response to the original client. |
| using URLLoaderCompletionStatusCallback = base::RepeatingCallback<void( |
| const GURL& request_url, |
| const network::URLLoaderCompletionStatus& status)>; |
| |
| // Create an interceptor which calls |callback|. If |ready_callback| is not |
| // provided, a nested RunLoop is used to ensure the interceptor is ready |
| // before returning. If |ready_callback| is provided, no RunLoop is called, |
| // and instead |ready_callback| is called after the interceptor is installed. |
| // If provided, |completion_status_callback| is called when the load |
| // completes. |
| explicit URLLoaderInterceptor(InterceptCallback callback); |
| URLLoaderInterceptor( |
| InterceptCallback callback, |
| const URLLoaderCompletionStatusCallback& completion_status_callback, |
| base::OnceClosure ready_callback); |
| |
| ~URLLoaderInterceptor(); |
| |
| // Serves static data, similar to net::test::EmbeddedTestServer, for |
| // cases where you need a static origin, such as tests with origin trials. |
| // Optional callback will notify callers for any accessed urls. |
| static std::unique_ptr<URLLoaderInterceptor> ServeFilesFromDirectoryAtOrigin( |
| const std::string& relative_base_path, |
| const GURL& origin, |
| base::RepeatingCallback<void(const GURL&)> callback = base::DoNothing()); |
| |
| // Helper methods for use when intercepting. |
| // Writes the given response body, header, and SSL Info to |client|. |
| static void WriteResponse( |
| base::StringPiece headers, |
| base::StringPiece body, |
| network::mojom::URLLoaderClient* client, |
| base::Optional<net::SSLInfo> ssl_info = base::nullopt); |
| |
| // Reads the given path, relative to the root source directory, and writes it |
| // to |client|. For headers: |
| // 1) if |headers| is specified, it's used |
| // 2) otherwise if an adjoining file that ends in .mock-http-headers is |
| // found, its contents will be used |
| // 3) otherwise a simple 200 response will be used, with a Content-Type |
| // guessed from the file extension |
| // For SSL info, if |ssl_info| is specified, then it is added to the response. |
| static void WriteResponse( |
| const std::string& relative_path, |
| network::mojom::URLLoaderClient* client, |
| const std::string* headers = nullptr, |
| base::Optional<net::SSLInfo> ssl_info = base::nullopt); |
| |
| // Like above, but uses an absolute file path. |
| static void WriteResponse( |
| const base::FilePath& file_path, |
| network::mojom::URLLoaderClient* client, |
| const std::string* headers = nullptr, |
| base::Optional<net::SSLInfo> ssl_info = base::nullopt); |
| |
| // Attempts to write |body| to |client| and complete the load with status OK. |
| // client->OnReceiveResponse() must have been called prior to this. |
| static MojoResult WriteResponseBody(base::StringPiece body, |
| network::mojom::URLLoaderClient* client); |
| |
| // Returns an interceptor that (as long as it says alive) will intercept |
| // requests to |url| and fail them using the provided |error|. |
| // |ready_callback| is optional and avoids the use of RunLoop, see |
| // the constructor for more detail. |
| static std::unique_ptr<URLLoaderInterceptor> SetupRequestFailForURL( |
| const GURL& url, |
| net::Error error, |
| base::OnceClosure ready_callback = {}); |
| |
| private: |
| class BrowserProcessWrapper; |
| class Interceptor; |
| class IOState; |
| class RenderProcessHostWrapper; |
| class URLLoaderFactoryGetterWrapper; |
| class URLLoaderFactoryNavigationWrapper; |
| |
| // Used to create a factory associated with a specific RenderProcessHost. |
| void CreateURLLoaderFactoryForRenderProcessHost( |
| mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver, |
| int process_id, |
| mojo::PendingRemote<network::mojom::URLLoaderFactory> original_factory); |
| |
| // Callback on UI thread whenever a |
| // StoragePartition::GetURLLoaderFactoryForBrowserProcess is called on an |
| // object that doesn't have a test factory set up. |
| mojo::PendingRemote<network::mojom::URLLoaderFactory> |
| GetURLLoaderFactoryForBrowserProcess( |
| mojo::PendingRemote<network::mojom::URLLoaderFactory> original_factory); |
| |
| // Callback on UI thread whenever NavigationURLLoaderImpl needs a |
| // URLLoaderFactory with a network::mojom::TrustedURLLoaderHeaderClient. |
| void InterceptNavigationRequestCallback( |
| mojo::PendingReceiver<network::mojom::URLLoaderFactory>* receiver); |
| |
| // Attempts to intercept the given request, returning true if it was |
| // intercepted. |
| bool Intercept(RequestParams* params); |
| |
| // Called on IO thread at initialization and shutdown. |
| void InitializeOnIOThread(base::OnceClosure closure); |
| |
| bool use_runloop_; |
| base::OnceClosure ready_callback_; |
| InterceptCallback callback_; |
| scoped_refptr<IOState> io_thread_; |
| // For intecepting non-frame requests from the browser process. There is one |
| // per StoragePartition. Only accessed on UI thread. |
| std::set<std::unique_ptr<BrowserProcessWrapper>> |
| browser_process_interceptors_; |
| |
| std::set<std::unique_ptr<URLLoaderFactoryNavigationWrapper>> |
| navigation_wrappers_; |
| |
| DISALLOW_COPY_AND_ASSIGN(URLLoaderInterceptor); |
| }; |
| |
| } // namespace content |
| |
| #endif // CONTENT_PUBLIC_TEST_URL_LOADER_INTERCEPTOR_H_ |