1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
|
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_EMBEDDER_SUPPORT_ANDROID_UTIL_ANDROID_STREAM_READER_URL_LOADER_H_
#define COMPONENTS_EMBEDDER_SUPPORT_ANDROID_UTIL_ANDROID_STREAM_READER_URL_LOADER_H_
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "mojo/public/cpp/bindings/pending_remote.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/url_loader.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
namespace embedder_support {
class InputStream;
class InputStreamReaderWrapper;
// Custom URLLoader implementation for loading responses from Android
// InputStreams. Although this works generally for implementers of the
// ResponseDelegate interface, this specifically aims to support:
//
// - shouldInterceptRequest callback
// - content:// URLs, which load content from Android ContentProviders (which
// could be in-app or come from other apps)
// - file:///android_asset/ & file:///android_res/ URLs, which load in-app
// content from the app's asset/ and res/ folders
class AndroidStreamReaderURLLoader : public network::mojom::URLLoader {
public:
// Delegate abstraction for obtaining input streams.
class ResponseDelegate {
public:
virtual ~ResponseDelegate() = default;
// This method is called from a worker thread, not from the IO thread.
virtual std::unique_ptr<embedder_support::InputStream> OpenInputStream(
JNIEnv* env) = 0;
// All the methods below are called on the URLLoader thread (IO thread).
// This method is called if the result of calling OpenInputStream was null.
// Returns true if the request was restarted with a new loader or
// was completed, false otherwise.
virtual bool OnInputStreamOpenFailed() = 0;
// Allows the delegate to update the mime type, by setting |mime_type| and
// returning true.
virtual bool GetMimeType(JNIEnv* env,
const GURL& url,
embedder_support::InputStream* stream,
std::string* mime_type) = 0;
// Allows the delegate to set the charset of the response by setting
// |charset|.
virtual void GetCharset(JNIEnv* env,
const GURL& url,
embedder_support::InputStream* stream,
std::string* charset) = 0;
// Allows the delegate to add extra response headers.
virtual void AppendResponseHeaders(JNIEnv* env,
net::HttpResponseHeaders* headers) = 0;
// Called right before URLLoaderClient::OnReceiveResponse is called. If this
// method returns true then a copy of the response is made and returned in
// OnResponseCache.
virtual bool ShouldCacheResponse(network::mojom::URLResponseHead* response);
// Called if ShouldCacheResponse returned true and the response was
// successful.
virtual void OnResponseCache(const std::string& data) {}
};
struct SecurityOptions {
bool disable_web_security = false;
bool allow_cors_to_same_scheme = false;
};
// Delegate that ensures that the provided `value` is set as a Set-Cookie
// response to the given `request`.
using SetCookieHeader = base::RepeatingCallback<void(
const network::ResourceRequest& request,
std::string_view value,
const std::optional<base::Time>& server_time)>;
AndroidStreamReaderURLLoader(
const network::ResourceRequest& resource_request,
mojo::PendingRemote<network::mojom::URLLoaderClient> client,
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
std::unique_ptr<ResponseDelegate> response_delegate,
std::optional<SecurityOptions> security_options,
std::optional<SetCookieHeader> set_cookie_header = std::nullopt);
AndroidStreamReaderURLLoader(const AndroidStreamReaderURLLoader&) = delete;
AndroidStreamReaderURLLoader& operator=(const AndroidStreamReaderURLLoader&) =
delete;
~AndroidStreamReaderURLLoader() override;
void Start(std::unique_ptr<InputStream> input_stream);
// network::mojom::URLLoader overrides:
void FollowRedirect(
const std::vector<std::string>& removed_headers,
const net::HttpRequestHeaders& modified_headers,
const net::HttpRequestHeaders& modified_cors_exempt_headers,
const std::optional<GURL>& new_url) override;
void SetPriority(net::RequestPriority priority,
int intra_priority_value) override;
private:
bool ParseRange(const net::HttpRequestHeaders& headers);
void OnInputStreamOpened(
std::unique_ptr<AndroidStreamReaderURLLoader::ResponseDelegate>
returned_delegate,
std::unique_ptr<embedder_support::InputStream> input_stream);
void OnReaderSeekCompleted(int result);
void HeadersComplete(int status_code, const std::string& status_text);
void RequestCompleteWithStatus(
const network::URLLoaderCompletionStatus& status);
void RequestComplete(int status_code);
void SendBody();
void OnDataPipeWritable(MojoResult result);
void CleanUp();
// Called after trying to read some bytes from the stream. |result| can be a
// positive number (the number of bytes read), zero (no bytes were read
// because the stream is finished), or negative (error condition).
void DidRead(int result);
// Reads some bytes from the stream. Calls |DidRead| after each read (also, in
// the case where it fails to read due to an error).
void ReadMore();
// The Set-Cookie header can't be sent over IPC. Because this stream reader
// bypasses the network stack, the Set-Cookie is ignored entirely.
// This is called before sending the response to give the embedder
// an opportunity to save headers.
void SetCookies();
// Send response headers and the data pipe consumer handle (for the body) to
// the URLLoaderClient. Requires |consumer_handle_| to be valid, and will make
// |consumer_handle_| invalid after running.
void SendResponseToClient();
// Expected content size
int64_t expected_content_size_ = -1;
mojo::ScopedDataPipeConsumerHandle consumer_handle_;
net::HttpByteRange byte_range_;
network::ResourceRequest resource_request_;
network::mojom::URLResponseHeadPtr response_head_;
bool reject_cors_request_;
mojo::Remote<network::mojom::URLLoaderClient> client_;
const net::MutableNetworkTrafficAnnotationTag traffic_annotation_;
std::unique_ptr<ResponseDelegate> response_delegate_;
scoped_refptr<InputStreamReaderWrapper> input_stream_reader_wrapper_;
// If true, a copy of the response is made.
bool cache_response_ = false;
std::string cached_response_;
mojo::ScopedDataPipeProducerHandle producer_handle_;
scoped_refptr<network::NetToMojoPendingBuffer> pending_buffer_;
mojo::SimpleWatcher writable_handle_watcher_;
base::Time start_time_;
std::optional<SetCookieHeader> set_cookie_header_;
base::ThreadChecker thread_checker_;
base::WeakPtrFactory<AndroidStreamReaderURLLoader> weak_factory_{this};
};
} // namespace embedder_support
#endif // COMPONENTS_EMBEDDER_SUPPORT_ANDROID_UTIL_ANDROID_STREAM_READER_URL_LOADER_H_
|