cef/libcef/browser/net_service/stream_reader_url_loader.h
Marshall Greenblatt 8f240861e3 Implement NetworkService request interception/handling (see issue #2622).
Implementation notes:
- Chromium change: CookieMonster::SetCookieableSchemes needs to be called
  immediately after the CookieMonster is created in NetworkContext::
  ApplyContextParamsToBuilder. Add a Profile::GetCookieableSchemes method and
  NetworkContextParams.cookieable_schemes member (set from
  ProfileNetworkContextService::CreateNetworkContextParams) to support that.
- Chromium change: Add a ContentBrowserClient::HandleExternalProtocol variant
  that exposes additional NetworkService request information.
- GetResourceResponseFilter is not yet implemented.

API changes:
- Resource-related callbacks have been moved from CefRequestHandler to a new
  CefResourceRequestHandler interface which is returned via the
  GetResourceRequestHandler method. If the CefRequestHandler declines to handle
  a resource it can optionally be handled by the CefRequestContextHandler, if
  any, associated with the loading context.
- The OnProtocolExecution callback has been moved from CefRequestHandler to
  CefResourceRequestHandler and will be called if a custom scheme request is
  unhandled.
- Cookie send/save permission callbacks have been moved from CefRequestHandler
  and CefResourceHandler to CefResourceRequestHandler.
- New methods added to CefResourceHandler that better match NetworkService
  execution sequence expectations. The old methods are now deprecated.
- New methods added to CefRequest and CefResponse.

Known behavior changes with the NetworkService implementation:
- Modifying the |new_url| parameter in OnResourceRedirect will no longer result
  in the method being called an additional time (likely a bug in the old
  implementation).
- Modifying the request URL in OnResourceResponse would previously cause a
  redirect. This behavior is now deprecated because the NetworkService does not
  support this functionality when using default network loaders. Temporary
  support has been added in combination with CefResourceHandler usage only.
- Other changes to the request object in OnResourceResponse will now cause the
  request to be restarted. This means that OnBeforeResourceLoad, etc, will be
  called an additional time with the new request information.
- CefResponse::GetMimeType will now be empty for non-200 responses.
- Requests using custom schemes can now be handled via CefResourceRequestHandler
  with the same callback behavior as builtin schemes.
- Redirects of custom scheme requests will now be followed as expected.
- Default handling of builtin schemes can now be disabled by setting
  |disable_default_handling| to true in GetResourceRequestHandler.
- Unhandled requests (custom scheme or builtin scheme with default handling
  disabled) will fail with an CefResponse::GetError value of
  ERR_UNKNOWN_URL_SCHEME.
- The CefSchemeHandlerFactory::Create callback will now include cookie headers.

To test:
- Run `cefclient --enable-network-service`. All resources should load
  successfully (this tests the transparent proxy capability).
- All tests pass with NetworkService disabled.
- The following tests pass with NetworkService enabled:
  - CookieTest.*
  - FrameTest.* (excluding .*Nav)
  - NavigationTest.* (excluding .Redirect*)
  - RequestHandlerTest.*
  - RequestContextTest.Basic*
  - RequestContextTest.Popup*
  - RequestTest.*
  - ResourceManagerTest.*
  - ResourceRequestHandlerTest.* (excluding .Filter*)
  - SchemeHandlerTest.*
  - StreamResourceHandlerTest.*
2019-04-23 22:53:28 -04:00

236 lines
9.2 KiB
C++

// 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 <map>
#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<void(int64_t /* bytes_skipped */)>;
// 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<void(int /* bytes_read */)>;
// 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<void(std::unique_ptr<InputStream>)>;
// 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<std::string, std::string>;
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<Delegate> response_delegate);
~StreamReaderURLLoader() override;
void Start();
void ContinueResponse(bool was_redirected);
// network::mojom::URLLoader methods:
void FollowRedirect(const std::vector<std::string>& removed_headers,
const net::HttpRequestHeaders& modified_headers,
const base::Optional<GURL>& 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<net::HttpRequestHeaders>& headers);
void OnInputStreamOpened(std::unique_ptr<Delegate> returned_delegate,
std::unique_ptr<InputStream> 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<std::string>& 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<Delegate> response_delegate_;
scoped_refptr<InputStreamReader> input_stream_reader_;
mojo::ScopedDataPipeProducerHandle producer_handle_;
scoped_refptr<network::NetToMojoPendingBuffer> pending_buffer_;
mojo::SimpleWatcher writable_handle_watcher_;
base::ThreadChecker thread_checker_;
scoped_refptr<base::SequencedTaskRunner> stream_work_task_runner_;
base::WeakPtrFactory<StreamReaderURLLoader> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(StreamReaderURLLoader);
};
} // namespace net_service
#endif // CEF_LIBCEF_BROWSER_NET_SERVICE_STREAM_READER_URL_LOADER_H_