atom_url_request.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. // Copyright (c) 2016 GitHub, Inc.
  2. // Copyright (c) 2011 The Chromium Authors. All rights reserved.
  3. // Use of this source code is governed by the MIT license that can be
  4. // found in the LICENSE file.
  5. #include "atom/browser/net/atom_url_request.h"
  6. #include <string>
  7. #include "atom/browser/api/atom_api_url_request.h"
  8. #include "atom/browser/atom_browser_context.h"
  9. #include "atom/browser/net/atom_url_request_job_factory.h"
  10. #include "base/callback.h"
  11. #include "content/public/browser/browser_thread.h"
  12. #include "net/base/elements_upload_data_stream.h"
  13. #include "net/base/io_buffer.h"
  14. #include "net/base/load_flags.h"
  15. #include "net/base/upload_bytes_element_reader.h"
  16. #include "net/url_request/redirect_info.h"
  17. namespace {
  18. const int kBufferSize = 4096;
  19. } // namespace
  20. namespace atom {
  21. namespace internal {
  22. class UploadOwnedIOBufferElementReader : public net::UploadBytesElementReader {
  23. public:
  24. explicit UploadOwnedIOBufferElementReader(
  25. scoped_refptr<const net::IOBufferWithSize> buffer)
  26. : net::UploadBytesElementReader(buffer->data(), buffer->size()),
  27. buffer_(buffer) {}
  28. ~UploadOwnedIOBufferElementReader() override {}
  29. static UploadOwnedIOBufferElementReader* CreateWithBuffer(
  30. scoped_refptr<const net::IOBufferWithSize> buffer) {
  31. return new UploadOwnedIOBufferElementReader(std::move(buffer));
  32. }
  33. private:
  34. scoped_refptr<const net::IOBuffer> buffer_;
  35. DISALLOW_COPY_AND_ASSIGN(UploadOwnedIOBufferElementReader);
  36. };
  37. } // namespace internal
  38. AtomURLRequest::AtomURLRequest(api::URLRequest* delegate)
  39. : delegate_(delegate),
  40. response_read_buffer_(new net::IOBuffer(kBufferSize)) {}
  41. AtomURLRequest::~AtomURLRequest() {
  42. DCHECK(!request_context_getter_);
  43. DCHECK(!request_);
  44. }
  45. scoped_refptr<AtomURLRequest> AtomURLRequest::Create(
  46. AtomBrowserContext* browser_context,
  47. const std::string& method,
  48. const std::string& url,
  49. const std::string& redirect_policy,
  50. api::URLRequest* delegate) {
  51. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  52. DCHECK(browser_context);
  53. DCHECK(!url.empty());
  54. DCHECK(delegate);
  55. if (!browser_context || url.empty() || !delegate) {
  56. return nullptr;
  57. }
  58. scoped_refptr<brightray::URLRequestContextGetter> request_context_getter(
  59. browser_context->GetRequestContext());
  60. DCHECK(request_context_getter);
  61. scoped_refptr<AtomURLRequest> atom_url_request(new AtomURLRequest(delegate));
  62. if (content::BrowserThread::PostTask(
  63. content::BrowserThread::IO, FROM_HERE,
  64. base::BindOnce(&AtomURLRequest::DoInitialize, atom_url_request,
  65. request_context_getter, method, url,
  66. redirect_policy))) {
  67. return atom_url_request;
  68. }
  69. return nullptr;
  70. }
  71. void AtomURLRequest::Terminate() {
  72. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  73. delegate_ = nullptr;
  74. content::BrowserThread::PostTask(
  75. content::BrowserThread::IO, FROM_HERE,
  76. base::BindOnce(&AtomURLRequest::DoTerminate, this));
  77. }
  78. void AtomURLRequest::DoInitialize(
  79. scoped_refptr<net::URLRequestContextGetter> request_context_getter,
  80. const std::string& method,
  81. const std::string& url,
  82. const std::string& redirect_policy) {
  83. DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  84. DCHECK(request_context_getter);
  85. redirect_policy_ = redirect_policy;
  86. request_context_getter_ = request_context_getter;
  87. request_context_getter_->AddObserver(this);
  88. auto* context = request_context_getter_->GetURLRequestContext();
  89. if (!context) {
  90. // Called after shutdown.
  91. DoCancelWithError("Cannot start a request after shutdown.", true);
  92. return;
  93. }
  94. DCHECK(context);
  95. request_ = context->CreateRequest(
  96. GURL(url), net::RequestPriority::DEFAULT_PRIORITY, this);
  97. if (!request_) {
  98. DoCancelWithError("Failed to create a net::URLRequest.", true);
  99. return;
  100. }
  101. request_->set_method(method);
  102. // Do not send cookies from the cookie store.
  103. DoSetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES);
  104. // Set a flag to stop custom protocol from intercepting this request.
  105. request_->SetUserData(DisableProtocolInterceptFlagKey(),
  106. base::WrapUnique(new base::SupportsUserData::Data()));
  107. }
  108. void AtomURLRequest::DoTerminate() {
  109. DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  110. request_.reset();
  111. if (request_context_getter_) {
  112. request_context_getter_->RemoveObserver(this);
  113. request_context_getter_ = nullptr;
  114. }
  115. }
  116. bool AtomURLRequest::Write(scoped_refptr<const net::IOBufferWithSize> buffer,
  117. bool is_last) {
  118. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  119. return content::BrowserThread::PostTask(
  120. content::BrowserThread::IO, FROM_HERE,
  121. base::BindOnce(&AtomURLRequest::DoWriteBuffer, this, buffer, is_last));
  122. }
  123. void AtomURLRequest::SetChunkedUpload(bool is_chunked_upload) {
  124. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  125. // The method can be called only before switching to multi-threaded mode,
  126. // i.e. before the first call to write.
  127. // So it is safe to change the object in the UI thread.
  128. is_chunked_upload_ = is_chunked_upload;
  129. }
  130. void AtomURLRequest::Cancel() {
  131. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  132. content::BrowserThread::PostTask(
  133. content::BrowserThread::IO, FROM_HERE,
  134. base::BindOnce(&AtomURLRequest::DoCancel, this));
  135. }
  136. void AtomURLRequest::FollowRedirect() {
  137. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  138. content::BrowserThread::PostTask(
  139. content::BrowserThread::IO, FROM_HERE,
  140. base::BindOnce(&AtomURLRequest::DoFollowRedirect, this));
  141. }
  142. void AtomURLRequest::SetExtraHeader(const std::string& name,
  143. const std::string& value) const {
  144. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  145. content::BrowserThread::PostTask(
  146. content::BrowserThread::IO, FROM_HERE,
  147. base::BindOnce(&AtomURLRequest::DoSetExtraHeader, this, name, value));
  148. }
  149. void AtomURLRequest::RemoveExtraHeader(const std::string& name) const {
  150. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  151. content::BrowserThread::PostTask(
  152. content::BrowserThread::IO, FROM_HERE,
  153. base::BindOnce(&AtomURLRequest::DoRemoveExtraHeader, this, name));
  154. }
  155. void AtomURLRequest::PassLoginInformation(
  156. const base::string16& username,
  157. const base::string16& password) const {
  158. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  159. if (username.empty() || password.empty()) {
  160. content::BrowserThread::PostTask(
  161. content::BrowserThread::IO, FROM_HERE,
  162. base::BindOnce(&AtomURLRequest::DoCancelAuth, this));
  163. } else {
  164. content::BrowserThread::PostTask(
  165. content::BrowserThread::IO, FROM_HERE,
  166. base::BindOnce(&AtomURLRequest::DoSetAuth, this, username, password));
  167. }
  168. }
  169. void AtomURLRequest::SetLoadFlags(int flags) const {
  170. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  171. content::BrowserThread::PostTask(
  172. content::BrowserThread::IO, FROM_HERE,
  173. base::BindOnce(&AtomURLRequest::DoSetLoadFlags, this, flags));
  174. }
  175. void AtomURLRequest::DoWriteBuffer(
  176. scoped_refptr<const net::IOBufferWithSize> buffer,
  177. bool is_last) {
  178. DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  179. if (!request_) {
  180. return;
  181. }
  182. if (is_chunked_upload_) {
  183. // Chunked encoding case.
  184. bool first_call = false;
  185. if (!chunked_stream_writer_) {
  186. std::unique_ptr<net::ChunkedUploadDataStream> chunked_stream(
  187. new net::ChunkedUploadDataStream(0));
  188. chunked_stream_writer_ = chunked_stream->CreateWriter();
  189. request_->set_upload(std::move(chunked_stream));
  190. first_call = true;
  191. }
  192. if (buffer)
  193. // Non-empty buffer.
  194. chunked_stream_writer_->AppendData(buffer->data(), buffer->size(),
  195. is_last);
  196. else if (is_last)
  197. // Empty buffer and last chunk, i.e. request.end().
  198. chunked_stream_writer_->AppendData(nullptr, 0, true);
  199. if (first_call) {
  200. request_->Start();
  201. }
  202. } else {
  203. if (buffer) {
  204. // Handling potential empty buffers.
  205. using internal::UploadOwnedIOBufferElementReader;
  206. auto* element_reader =
  207. UploadOwnedIOBufferElementReader::CreateWithBuffer(std::move(buffer));
  208. upload_element_readers_.push_back(
  209. std::unique_ptr<net::UploadElementReader>(element_reader));
  210. }
  211. if (is_last) {
  212. auto* elements_upload_data_stream = new net::ElementsUploadDataStream(
  213. std::move(upload_element_readers_), 0);
  214. request_->set_upload(
  215. std::unique_ptr<net::UploadDataStream>(elements_upload_data_stream));
  216. request_->Start();
  217. }
  218. }
  219. }
  220. void AtomURLRequest::DoCancel() {
  221. DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  222. if (request_) {
  223. request_->Cancel();
  224. }
  225. DoTerminate();
  226. }
  227. void AtomURLRequest::DoFollowRedirect() {
  228. DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  229. if (request_ && request_->is_redirecting() && redirect_policy_ == "manual") {
  230. request_->FollowDeferredRedirect();
  231. }
  232. }
  233. void AtomURLRequest::DoSetExtraHeader(const std::string& name,
  234. const std::string& value) const {
  235. DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  236. if (!request_) {
  237. return;
  238. }
  239. request_->SetExtraRequestHeaderByName(name, value, true);
  240. }
  241. void AtomURLRequest::DoRemoveExtraHeader(const std::string& name) const {
  242. DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  243. if (!request_) {
  244. return;
  245. }
  246. request_->RemoveRequestHeaderByName(name);
  247. }
  248. void AtomURLRequest::DoSetAuth(const base::string16& username,
  249. const base::string16& password) const {
  250. DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  251. if (!request_) {
  252. return;
  253. }
  254. request_->SetAuth(net::AuthCredentials(username, password));
  255. }
  256. void AtomURLRequest::DoCancelAuth() const {
  257. DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  258. if (!request_) {
  259. return;
  260. }
  261. request_->CancelAuth();
  262. }
  263. void AtomURLRequest::DoCancelWithError(const std::string& error,
  264. bool isRequestError) {
  265. DoCancel();
  266. content::BrowserThread::PostTask(
  267. content::BrowserThread::UI, FROM_HERE,
  268. base::BindOnce(&AtomURLRequest::InformDelegateErrorOccured, this, error,
  269. isRequestError));
  270. }
  271. void AtomURLRequest::DoSetLoadFlags(int flags) const {
  272. DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  273. if (!request_) {
  274. return;
  275. }
  276. request_->SetLoadFlags(request_->load_flags() | flags);
  277. }
  278. void AtomURLRequest::OnReceivedRedirect(net::URLRequest* request,
  279. const net::RedirectInfo& info,
  280. bool* defer_redirect) {
  281. DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  282. if (!request_ || redirect_policy_ == "follow")
  283. return;
  284. if (redirect_policy_ == "error") {
  285. request->Cancel();
  286. DoCancelWithError(
  287. "Request cannot follow redirect with the current redirect mode", true);
  288. } else if (redirect_policy_ == "manual") {
  289. *defer_redirect = true;
  290. scoped_refptr<net::HttpResponseHeaders> response_headers =
  291. request->response_headers();
  292. content::BrowserThread::PostTask(
  293. content::BrowserThread::UI, FROM_HERE,
  294. base::BindOnce(&AtomURLRequest::InformDelegateReceivedRedirect, this,
  295. info.status_code, info.new_method, info.new_url,
  296. response_headers));
  297. }
  298. }
  299. void AtomURLRequest::OnAuthRequired(net::URLRequest* request,
  300. net::AuthChallengeInfo* auth_info) {
  301. DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  302. content::BrowserThread::PostTask(
  303. content::BrowserThread::UI, FROM_HERE,
  304. base::BindOnce(&AtomURLRequest::InformDelegateAuthenticationRequired,
  305. this, scoped_refptr<net::AuthChallengeInfo>(auth_info)));
  306. }
  307. void AtomURLRequest::OnResponseStarted(net::URLRequest* request,
  308. int net_error) {
  309. DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  310. if (!request_) {
  311. return;
  312. }
  313. DCHECK_EQ(request, request_.get());
  314. scoped_refptr<net::HttpResponseHeaders> response_headers =
  315. request->response_headers();
  316. const auto& status = request_->status();
  317. if (status.is_success()) {
  318. // Success or pending trigger a Read.
  319. content::BrowserThread::PostTask(
  320. content::BrowserThread::UI, FROM_HERE,
  321. base::BindOnce(&AtomURLRequest::InformDelegateResponseStarted, this,
  322. response_headers));
  323. ReadResponse();
  324. } else if (status.status() == net::URLRequestStatus::Status::FAILED) {
  325. // Report error on Start.
  326. DoCancelWithError(net::ErrorToString(net_error), true);
  327. }
  328. // We don't report an error is the request is canceled.
  329. }
  330. void AtomURLRequest::ReadResponse() {
  331. DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  332. int bytes_read = -1;
  333. if (request_->Read(response_read_buffer_.get(), kBufferSize, &bytes_read)) {
  334. OnReadCompleted(request_.get(), bytes_read);
  335. }
  336. }
  337. void AtomURLRequest::OnReadCompleted(net::URLRequest* request, int bytes_read) {
  338. DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  339. if (!request_) {
  340. return;
  341. }
  342. DCHECK_EQ(request, request_.get());
  343. const auto status = request_->status();
  344. if (status.error() == bytes_read &&
  345. bytes_read == net::ERR_CONTENT_DECODING_INIT_FAILED) {
  346. // When the request job is unable to create a source stream for the
  347. // content encoding, we fail the request.
  348. DoCancelWithError(net::ErrorToString(net::ERR_CONTENT_DECODING_INIT_FAILED),
  349. true);
  350. return;
  351. }
  352. bool response_error = false;
  353. bool data_ended = false;
  354. bool data_transfer_error = false;
  355. do {
  356. if (!status.is_success()) {
  357. response_error = true;
  358. break;
  359. }
  360. if (bytes_read == 0) {
  361. data_ended = true;
  362. break;
  363. }
  364. if (bytes_read < 0 || !CopyAndPostBuffer(bytes_read)) {
  365. data_transfer_error = true;
  366. break;
  367. }
  368. } while (
  369. request_->Read(response_read_buffer_.get(), kBufferSize, &bytes_read));
  370. if (response_error) {
  371. DoCancelWithError(net::ErrorToString(status.ToNetError()), false);
  372. } else if (data_ended) {
  373. content::BrowserThread::PostTask(
  374. content::BrowserThread::UI, FROM_HERE,
  375. base::BindOnce(&AtomURLRequest::InformDelegateResponseCompleted, this));
  376. DoTerminate();
  377. } else if (data_transfer_error) {
  378. // We abort the request on corrupted data transfer.
  379. DoCancelWithError("Failed to transfer data from IO to UI thread.", false);
  380. }
  381. }
  382. void AtomURLRequest::OnContextShuttingDown() {
  383. DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  384. DoCancel();
  385. }
  386. bool AtomURLRequest::CopyAndPostBuffer(int bytes_read) {
  387. DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  388. // data is only a wrapper for the asynchronous response_read_buffer_.
  389. // Make a deep copy of payload and transfer ownership to the UI thread.
  390. auto buffer_copy = WrapRefCounted(new net::IOBufferWithSize(bytes_read));
  391. memcpy(buffer_copy->data(), response_read_buffer_->data(), bytes_read);
  392. return content::BrowserThread::PostTask(
  393. content::BrowserThread::UI, FROM_HERE,
  394. base::BindOnce(&AtomURLRequest::InformDelegateResponseData, this,
  395. buffer_copy));
  396. }
  397. void AtomURLRequest::InformDelegateReceivedRedirect(
  398. int status_code,
  399. const std::string& method,
  400. const GURL& url,
  401. scoped_refptr<net::HttpResponseHeaders> response_headers) const {
  402. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  403. if (delegate_)
  404. delegate_->OnReceivedRedirect(status_code, method, url, response_headers);
  405. }
  406. void AtomURLRequest::InformDelegateAuthenticationRequired(
  407. scoped_refptr<net::AuthChallengeInfo> auth_info) const {
  408. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  409. if (delegate_)
  410. delegate_->OnAuthenticationRequired(auth_info);
  411. }
  412. void AtomURLRequest::InformDelegateResponseStarted(
  413. scoped_refptr<net::HttpResponseHeaders> response_headers) const {
  414. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  415. if (delegate_)
  416. delegate_->OnResponseStarted(response_headers);
  417. }
  418. void AtomURLRequest::InformDelegateResponseData(
  419. scoped_refptr<net::IOBufferWithSize> data) const {
  420. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  421. // Transfer ownership of the data buffer, data will be released
  422. // by the delegate's OnResponseData.
  423. if (delegate_)
  424. delegate_->OnResponseData(data);
  425. }
  426. void AtomURLRequest::InformDelegateResponseCompleted() const {
  427. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  428. if (delegate_)
  429. delegate_->OnResponseCompleted();
  430. }
  431. void AtomURLRequest::InformDelegateErrorOccured(const std::string& error,
  432. bool isRequestError) const {
  433. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  434. if (delegate_)
  435. delegate_->OnError(error, isRequestError);
  436. }
  437. void AtomURLRequest::GetUploadProgress(mate::Dictionary* progress) const {
  438. net::UploadProgress upload_progress;
  439. if (request_) {
  440. progress->Set("started", true);
  441. upload_progress = request_->GetUploadProgress();
  442. } else {
  443. progress->Set("started", false);
  444. }
  445. progress->Set("current", upload_progress.position());
  446. progress->Set("total", upload_progress.size());
  447. }
  448. } // namespace atom