// 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/callback.h" #include "base/threading/thread_checker.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/mojom/network_context.mojom.h" #include "services/network/public/mojom/url_loader.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() {} // 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; }; // Unique identifier for RequestHandler callbacks. // Based on components/viz/common/surfaces/frame_sink_id.h class RequestId { public: constexpr RequestId() : request_id_(0), routing_id_(0) {} constexpr RequestId(const RequestId& other) : request_id_(other.request_id_), routing_id_(other.routing_id_) {} constexpr RequestId(uint32_t request_id, uint32_t routing_id) : request_id_(request_id), routing_id_(routing_id) {} constexpr bool is_valid() const { return request_id_ != 0 || routing_id_ != 0; } constexpr uint32_t request_id() const { return request_id_; } constexpr uint32_t routing_id() const { return routing_id_; } bool operator==(const RequestId& other) const { return request_id_ == other.request_id_ && routing_id_ == other.routing_id_; } bool operator!=(const RequestId& other) const { return !(*this == other); } bool operator<(const RequestId& other) const { return std::tie(request_id_, routing_id_) < std::tie(other.request_id_, other.routing_id_); } size_t hash() const { return base::HashInts(request_id_, routing_id_); } std::string ToString() const; std::string ToString(base::StringPiece debug_label) const; private: uint32_t request_id_; uint32_t routing_id_; }; std::ostream& operator<<(std::ostream& out, const RequestId& request_id); struct RequestIdHash { size_t operator()(const RequestId& key) const { return key.hash(); } }; // Abstract class for handling intercepted resource responses. All methods are // called on the IO thread unless otherwise indicated. class ResourceResponse { public: virtual ~ResourceResponse() {} // 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(const RequestId& id, const network::ResourceRequest& request, OpenCallback callback) = 0; // This method is called to populate the response headers. using HeaderMap = std::multimap; virtual void GetResponseHeaders(const RequestId& 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: // This method is called if the result of calling OpenInputStream was null. // The |restarted| parameter is set to true if the request was restarted // with a new loader. virtual void OnInputStreamOpenFailed(const RequestId& id, bool* restarted) = 0; }; StreamReaderURLLoader( const RequestId& request_id, const network::ResourceRequest& request, network::mojom::URLLoaderClientPtr client, network::mojom::TrustedHeaderClientPtr header_client, const net::MutableNetworkTrafficAnnotationTag& traffic_annotation, std::unique_ptr response_delegate); ~StreamReaderURLLoader() override; void Start(); void ContinueResponse(bool was_redirected); // network::mojom::URLLoader methods: void FollowRedirect(const std::vector& removed_headers, const net::HttpRequestHeaders& modified_headers, const base::Optional& new_url) override; void ProceedWithResponse() override; void SetPriority(net::RequestPriority priority, int intra_priority_value) override; void PauseReadingBodyFromNet() override; void ResumeReadingBodyFromNet() override; private: void ContinueWithRequestHeaders( int32_t result, const base::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(int32_t result, const base::Optional& headers, const GURL& redirect_url); void SendBody(); 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 RequestId request_id_; network::ResourceResponseHead pending_response_; size_t header_length_ = 0; int64_t total_bytes_read_ = 0; net::HttpByteRange byte_range_; network::ResourceRequest request_; network::mojom::URLLoaderClientPtr client_; network::mojom::TrustedHeaderClientPtr header_client_; const net::MutableNetworkTrafficAnnotationTag traffic_annotation_; 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_; base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(StreamReaderURLLoader); }; } // namespace net_service #endif // CEF_LIBCEF_BROWSER_NET_SERVICE_STREAM_READER_URL_LOADER_H_