// Copyright (c) 2019 The Chromium Embedded Framework Authors. Portions // Copyright (c) 2018 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 CEF_LIBCEF_BROWSER_NET_SERVICE_STREAM_READER_URL_LOADER_H_ #define CEF_LIBCEF_BROWSER_NET_SERVICE_STREAM_READER_URL_LOADER_H_ #include #include "base/functional/callback.h" #include "base/threading/thread_checker.h" #include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/system/simple_watcher.h" #include "net/http/http_byte_range.h" #include "services/network/public/cpp/net_adapters.h" #include "services/network/public/cpp/resource_request.h" #include "services/network/public/mojom/network_context.mojom.h" #include "services/network/public/mojom/url_loader.mojom.h" #include "services/network/public/mojom/url_response_head.mojom.h" namespace net_service { class InputStreamReader; // Abstract class representing an input stream. All methods are called in // sequence on a worker thread, but not necessarily on the same thread. class InputStream { public: virtual ~InputStream() = default; // Callback for asynchronous continuation of Skip(). If |bytes_skipped| > 0 // then either Skip() will be called again until the requested number of // bytes have been skipped or the request will proceed. If |bytes_skipped| // <= 0 the request will fail with net::ERR_REQUEST_RANGE_NOT_SATISFIABLE. using SkipCallback = base::OnceCallback; // Skip over and discard |n| bytes of data from this input stream. If data // is available immediately set |bytes_skipped| to the number of of bytes // skipped and return true. To read the data at a later time set // |bytes_skipped| to 0, return true and execute |callback| when the data is // available. To indicate failure set |bytes_skipped| to < 0 (e.g. // net::ERR_FAILED) and return false. virtual bool Skip(int64_t n, int64_t* bytes_skipped, SkipCallback callback) = 0; // Callback for asynchronous continuation of Read(). If |bytes_read| == 0 // the response will be considered complete. If |bytes_read| > 0 then Read() // will be called again until the request is complete (based on either the // result or the expected content length). If |bytes_read| < 0 then the // request will fail and the |bytes_read| value will be treated as the error // code. using ReadCallback = base::OnceCallback; // Read response data. If data is available immediately copy up to |length| // bytes into |dest|, set |bytes_read| to the number of bytes copied, and // return true. To read the data at a later time set |bytes_read| to 0, return // true and execute |callback| when the data is available. To indicate // response completion set |bytes_read| to 0 and return false. To indicate // failure set |bytes_read| to < 0 (e.g. net::ERR_FAILED) and return false. virtual bool Read(net::IOBuffer* dest, int length, int* bytes_read, ReadCallback callback) = 0; }; // Abstract class for handling intercepted resource responses. All methods are // called on the IO thread unless otherwise indicated. class ResourceResponse { public: virtual ~ResourceResponse() = default; // Callback for asynchronous continuation of Open(). If the InputStream is // null the request will be canceled. using OpenCallback = base::OnceCallback)>; // This method is called on a worker thread. Return true and execute // |callback| to continue the request. Return false to cancel the request. // |request| may be different from the request used to create the // StreamReaderURLLoader if a redirect was followed. virtual bool OpenInputStream(int32_t request_id, const network::ResourceRequest& request, OpenCallback callback) = 0; // This method is called to populate the response headers. using HeaderMap = std::multimap; virtual void GetResponseHeaders(int32_t request_id, int* status_code, std::string* reason_phrase, std::string* mime_type, std::string* charset, int64_t* content_length, HeaderMap* extra_headers) = 0; }; // Custom URLLoader implementation for loading network responses from stream. // Methods are called on the IO thread unless otherwise indicated. // Based on android_webview/browser/network_service/ // android_stream_reader_url_loader.h class StreamReaderURLLoader : public network::mojom::URLLoader { public: // Delegate abstraction for obtaining input streams. All methods are called // on the IO thread unless otherwise indicated. class Delegate : public ResourceResponse { public: // Called if the result of calling OpenInputStream was nullptr. Returns // true if the failure was handled. virtual bool OnInputStreamOpenFailed(int32_t request_id) = 0; }; StreamReaderURLLoader( int32_t request_id, const network::ResourceRequest& request, mojo::PendingRemote client, mojo::PendingRemote header_client, const net::MutableNetworkTrafficAnnotationTag& traffic_annotation, std::optional cached_metadata, std::unique_ptr response_delegate); StreamReaderURLLoader(const StreamReaderURLLoader&) = delete; StreamReaderURLLoader& operator=(const StreamReaderURLLoader&) = delete; ~StreamReaderURLLoader() override; void Start(); // Called by the client in response to OnReceiveResponse. void Continue(); void Cancel(); // network::mojom::URLLoader methods: void FollowRedirect( const std::vector& removed_headers, const net::HttpRequestHeaders& modified_headers, const net::HttpRequestHeaders& modified_cors_exempt_headers, const std::optional& new_url) override; void SetPriority(net::RequestPriority priority, int intra_priority_value) override; void PauseReadingBodyFromNet() override; void ResumeReadingBodyFromNet() override; private: void ContinueWithRequestHeaders( int32_t result, const std::optional& headers); void OnInputStreamOpened(std::unique_ptr returned_delegate, std::unique_ptr input_stream); void OnReaderSkipCompleted(int64_t bytes_skipped); void HeadersComplete(int status_code, int64_t expected_content_length); void ContinueWithResponseHeaders( network::mojom::URLResponseHeadPtr pending_response, int32_t result, const std::optional& headers, const std::optional& redirect_url); void ReadMore(); void OnDataPipeWritable(MojoResult result); void OnReaderReadCompleted(int bytes_read); void RequestComplete(int status_code); void CleanUp(); bool ParseRange(const net::HttpRequestHeaders& headers); bool byte_range_valid() const; const int32_t request_id_; size_t header_length_ = 0; int64_t total_bytes_read_ = 0; net::HttpByteRange byte_range_; network::ResourceRequest request_; mojo::Remote client_; mojo::Remote header_client_; const net::MutableNetworkTrafficAnnotationTag traffic_annotation_; std::optional cached_metadata_; std::unique_ptr response_delegate_; scoped_refptr input_stream_reader_; mojo::ScopedDataPipeProducerHandle producer_handle_; scoped_refptr pending_buffer_; mojo::SimpleWatcher writable_handle_watcher_; base::ThreadChecker thread_checker_; scoped_refptr stream_work_task_runner_; base::OnceClosure open_cancel_callback_; bool need_client_callback_ = false; bool got_client_callback_ = false; base::WeakPtrFactory weak_factory_; }; } // namespace net_service #endif // CEF_LIBCEF_BROWSER_NET_SERVICE_STREAM_READER_URL_LOADER_H_