url_loader.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "pdf/loader/url_loader.h"
  5. #include <stddef.h>
  6. #include <stdint.h>
  7. #include <algorithm>
  8. #include <string>
  9. #include <utility>
  10. #include "base/bind.h"
  11. #include "base/callback.h"
  12. #include "base/check_op.h"
  13. #include "base/memory/weak_ptr.h"
  14. #include "base/notreached.h"
  15. #include "net/base/net_errors.h"
  16. #include "net/cookies/site_for_cookies.h"
  17. #include "net/http/http_util.h"
  18. #include "pdf/loader/result_codes.h"
  19. #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
  20. #include "third_party/blink/public/platform/web_data.h"
  21. #include "third_party/blink/public/platform/web_http_body.h"
  22. #include "third_party/blink/public/platform/web_http_header_visitor.h"
  23. #include "third_party/blink/public/platform/web_string.h"
  24. #include "third_party/blink/public/platform/web_url.h"
  25. #include "third_party/blink/public/platform/web_url_error.h"
  26. #include "third_party/blink/public/platform/web_url_request.h"
  27. #include "third_party/blink/public/platform/web_url_response.h"
  28. #include "third_party/blink/public/web/web_associated_url_loader.h"
  29. #include "third_party/blink/public/web/web_associated_url_loader_options.h"
  30. #include "url/gurl.h"
  31. namespace chrome_pdf {
  32. namespace {
  33. // Taken from `content/renderer/pepper/url_response_info_util.cc`.
  34. class HeadersToString final : public blink::WebHTTPHeaderVisitor {
  35. public:
  36. explicit HeadersToString(std::string& buffer_ref) : buffer_ref_(buffer_ref) {}
  37. void VisitHeader(const blink::WebString& name,
  38. const blink::WebString& value) override {
  39. if (!buffer_ref_.empty())
  40. buffer_ref_.append("\n");
  41. buffer_ref_.append(name.Utf8());
  42. buffer_ref_.append(": ");
  43. buffer_ref_.append(value.Utf8());
  44. }
  45. private:
  46. // Reference allows writing directly into `UrlResponse::headers`.
  47. std::string& buffer_ref_;
  48. };
  49. } // namespace
  50. UrlRequest::UrlRequest() = default;
  51. UrlRequest::UrlRequest(const UrlRequest& other) = default;
  52. UrlRequest::UrlRequest(UrlRequest&& other) noexcept = default;
  53. UrlRequest& UrlRequest::operator=(const UrlRequest& other) = default;
  54. UrlRequest& UrlRequest::operator=(UrlRequest&& other) noexcept = default;
  55. UrlRequest::~UrlRequest() = default;
  56. UrlResponse::UrlResponse() = default;
  57. UrlResponse::UrlResponse(const UrlResponse& other) = default;
  58. UrlResponse::UrlResponse(UrlResponse&& other) noexcept = default;
  59. UrlResponse& UrlResponse::operator=(const UrlResponse& other) = default;
  60. UrlResponse& UrlResponse::operator=(UrlResponse&& other) noexcept = default;
  61. UrlResponse::~UrlResponse() = default;
  62. UrlLoader::UrlLoader(base::WeakPtr<Client> client)
  63. : client_(std::move(client)) {}
  64. UrlLoader::~UrlLoader() = default;
  65. // Modeled on `content::PepperURLLoaderHost::OnHostMsgOpen()`.
  66. void UrlLoader::Open(const UrlRequest& request,
  67. base::OnceCallback<void(int)> callback) {
  68. DCHECK_EQ(state_, LoadingState::kWaitingToOpen);
  69. DCHECK(callback);
  70. state_ = LoadingState::kOpening;
  71. open_callback_ = std::move(callback);
  72. if (!client_ || !client_->IsValid()) {
  73. AbortLoad(Result::kErrorFailed);
  74. return;
  75. }
  76. // Modeled on `content::CreateWebURLRequest()`.
  77. // TODO(crbug.com/1129291): The original code performs additional validations
  78. // that we probably don't need in the new process model.
  79. blink::WebURLRequest blink_request;
  80. blink_request.SetUrl(
  81. client_->CompleteURL(blink::WebString::FromUTF8(request.url)));
  82. blink_request.SetHttpMethod(blink::WebString::FromASCII(request.method));
  83. blink_request.SetSiteForCookies(client_->SiteForCookies());
  84. blink_request.SetSkipServiceWorker(true);
  85. // Note: The PDF plugin doesn't set the `X-Requested-With` header.
  86. if (!request.headers.empty()) {
  87. net::HttpUtil::HeadersIterator it(request.headers.begin(),
  88. request.headers.end(), "\n\r");
  89. while (it.GetNext()) {
  90. blink_request.AddHttpHeaderField(blink::WebString::FromUTF8(it.name()),
  91. blink::WebString::FromUTF8(it.values()));
  92. }
  93. }
  94. if (!request.body.empty()) {
  95. blink::WebHTTPBody body;
  96. body.Initialize();
  97. body.AppendData(request.body);
  98. blink_request.SetHttpBody(body);
  99. }
  100. if (!request.custom_referrer_url.empty()) {
  101. client_->SetReferrerForRequest(blink_request,
  102. GURL(request.custom_referrer_url));
  103. }
  104. buffer_lower_threshold_ = request.buffer_lower_threshold;
  105. buffer_upper_threshold_ = request.buffer_upper_threshold;
  106. DCHECK_GT(buffer_lower_threshold_, 0u);
  107. DCHECK_LE(buffer_lower_threshold_, buffer_upper_threshold_);
  108. blink_request.SetRequestContext(blink::mojom::RequestContextType::PLUGIN);
  109. blink_request.SetRequestDestination(
  110. network::mojom::RequestDestination::kEmbed);
  111. // TODO(crbug.com/822081): Revisit whether we need universal access.
  112. blink::WebAssociatedURLLoaderOptions options;
  113. options.grant_universal_access = true;
  114. ignore_redirects_ = request.ignore_redirects;
  115. blink_loader_ = client_->CreateAssociatedURLLoader(options);
  116. blink_loader_->LoadAsynchronously(blink_request, this);
  117. }
  118. // Modeled on `ppapi::proxy::URLLoaderResource::ReadResponseBody()`.
  119. void UrlLoader::ReadResponseBody(base::span<char> buffer,
  120. base::OnceCallback<void(int)> callback) {
  121. // Can be in `kLoadComplete` if still reading after loading finished.
  122. DCHECK(state_ == LoadingState::kStreamingData ||
  123. state_ == LoadingState::kLoadComplete)
  124. << static_cast<int>(state_);
  125. if (buffer.empty()) {
  126. std::move(callback).Run(Result::kErrorBadArgument);
  127. return;
  128. }
  129. DCHECK(!read_callback_);
  130. DCHECK(callback);
  131. read_callback_ = std::move(callback);
  132. client_buffer_ = buffer;
  133. if (!buffer_.empty() || state_ == LoadingState::kLoadComplete)
  134. RunReadCallback();
  135. }
  136. // Modeled on `ppapi::proxy::URLLoadResource::Close()`.
  137. void UrlLoader::Close() {
  138. if (state_ != LoadingState::kLoadComplete)
  139. AbortLoad(Result::kErrorAborted);
  140. }
  141. // Modeled on `content::PepperURLLoaderHost::WillFollowRedirect()`.
  142. bool UrlLoader::WillFollowRedirect(
  143. const blink::WebURL& new_url,
  144. const blink::WebURLResponse& redirect_response) {
  145. DCHECK_EQ(state_, LoadingState::kOpening);
  146. // TODO(crbug.com/1129291): The original code performs additional validations
  147. // that we probably don't need in the new process model.
  148. // Note that `pp::URLLoader::FollowRedirect()` is not supported, so the
  149. // redirect can be canceled immediately by returning `false` here.
  150. return !ignore_redirects_;
  151. }
  152. void UrlLoader::DidSendData(uint64_t bytes_sent,
  153. uint64_t total_bytes_to_be_sent) {
  154. // Doesn't apply to PDF viewer requests.
  155. NOTREACHED();
  156. }
  157. // Modeled on `content::PepperURLLoaderHost::DidReceiveResponse()`.
  158. void UrlLoader::DidReceiveResponse(const blink::WebURLResponse& response) {
  159. DCHECK_EQ(state_, LoadingState::kOpening);
  160. // Modeled on `content::DataFromWebURLResponse()`.
  161. response_.status_code = response.HttpStatusCode();
  162. HeadersToString headers_to_string(response_.headers);
  163. response.VisitHttpHeaderFields(&headers_to_string);
  164. state_ = LoadingState::kStreamingData;
  165. std::move(open_callback_).Run(Result::kSuccess);
  166. }
  167. void UrlLoader::DidDownloadData(uint64_t data_length) {
  168. // Doesn't apply to PDF viewer requests.
  169. NOTREACHED();
  170. }
  171. // Modeled on `content::PepperURLLoaderHost::DidReceiveData()`.
  172. void UrlLoader::DidReceiveData(const char* data, int data_length) {
  173. DCHECK_EQ(state_, LoadingState::kStreamingData);
  174. // It's surprisingly difficult to guarantee that this is always >0.
  175. if (data_length < 1)
  176. return;
  177. buffer_.insert(buffer_.end(), data, data + data_length);
  178. // Defer loading if the buffer is too full.
  179. if (!deferring_loading_ && buffer_.size() >= buffer_upper_threshold_) {
  180. deferring_loading_ = true;
  181. blink_loader_->SetDefersLoading(true);
  182. }
  183. RunReadCallback();
  184. }
  185. // Modeled on `content::PepperURLLoaderHost::DidFinishLoading()`.
  186. void UrlLoader::DidFinishLoading() {
  187. DCHECK_EQ(state_, LoadingState::kStreamingData);
  188. SetLoadComplete(Result::kSuccess);
  189. RunReadCallback();
  190. }
  191. // Modeled on `content::PepperURLLoaderHost::DidFail()`.
  192. void UrlLoader::DidFail(const blink::WebURLError& error) {
  193. DCHECK(state_ == LoadingState::kOpening ||
  194. state_ == LoadingState::kStreamingData)
  195. << static_cast<int>(state_);
  196. int32_t pp_error = Result::kErrorFailed;
  197. switch (error.reason()) {
  198. case net::ERR_ACCESS_DENIED:
  199. case net::ERR_NETWORK_ACCESS_DENIED:
  200. pp_error = Result::kErrorNoAccess;
  201. break;
  202. default:
  203. if (error.is_web_security_violation())
  204. pp_error = Result::kErrorNoAccess;
  205. break;
  206. }
  207. AbortLoad(pp_error);
  208. }
  209. void UrlLoader::AbortLoad(int32_t result) {
  210. DCHECK_LT(result, 0);
  211. SetLoadComplete(result);
  212. buffer_.clear();
  213. if (open_callback_) {
  214. DCHECK(!read_callback_);
  215. std::move(open_callback_).Run(complete_result_);
  216. } else if (read_callback_) {
  217. RunReadCallback();
  218. }
  219. }
  220. // Modeled on `ppapi::proxy::URLLoaderResource::FillUserBuffer()`.
  221. void UrlLoader::RunReadCallback() {
  222. if (!read_callback_)
  223. return;
  224. DCHECK(!client_buffer_.empty());
  225. int32_t num_bytes = std::min(
  226. {buffer_.size(), client_buffer_.size(), static_cast<size_t>(INT32_MAX)});
  227. if (num_bytes > 0) {
  228. auto read_begin = buffer_.begin();
  229. auto read_end = read_begin + num_bytes;
  230. std::copy(read_begin, read_end, client_buffer_.data());
  231. buffer_.erase(read_begin, read_end);
  232. // Resume loading if the buffer is too empty.
  233. if (deferring_loading_ && buffer_.size() <= buffer_lower_threshold_) {
  234. deferring_loading_ = false;
  235. blink_loader_->SetDefersLoading(false);
  236. }
  237. } else {
  238. DCHECK_EQ(state_, LoadingState::kLoadComplete);
  239. num_bytes = complete_result_;
  240. DCHECK_LE(num_bytes, 0);
  241. static_assert(Result::kSuccess == 0,
  242. "Result::kSuccess should be equivalent to 0 bytes");
  243. }
  244. client_buffer_ = {};
  245. std::move(read_callback_).Run(num_bytes);
  246. }
  247. void UrlLoader::SetLoadComplete(int32_t result) {
  248. DCHECK_NE(state_, LoadingState::kLoadComplete);
  249. DCHECK_LE(result, 0);
  250. state_ = LoadingState::kLoadComplete;
  251. complete_result_ = result;
  252. blink_loader_.reset();
  253. }
  254. } // namespace chrome_pdf