atom_api_url_request.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. // Copyright (c) 2016 GitHub, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include "atom/browser/api/atom_api_url_request.h"
  5. #include <string>
  6. #include "atom/browser/api/atom_api_session.h"
  7. #include "atom/browser/net/atom_url_request.h"
  8. #include "atom/common/api/event_emitter_caller.h"
  9. #include "atom/common/native_mate_converters/callback.h"
  10. #include "atom/common/native_mate_converters/gurl_converter.h"
  11. #include "atom/common/native_mate_converters/net_converter.h"
  12. #include "atom/common/native_mate_converters/string16_converter.h"
  13. #include "atom/common/node_includes.h"
  14. #include "native_mate/dictionary.h"
  15. namespace mate {
  16. template <>
  17. struct Converter<scoped_refptr<const net::IOBufferWithSize>> {
  18. static v8::Local<v8::Value> ToV8(
  19. v8::Isolate* isolate,
  20. scoped_refptr<const net::IOBufferWithSize> buffer) {
  21. return node::Buffer::Copy(isolate, buffer->data(), buffer->size())
  22. .ToLocalChecked();
  23. }
  24. static bool FromV8(v8::Isolate* isolate,
  25. v8::Local<v8::Value> val,
  26. scoped_refptr<const net::IOBufferWithSize>* out) {
  27. auto size = node::Buffer::Length(val);
  28. if (size == 0) {
  29. // Support conversion from empty buffer. A use case is
  30. // a GET request without body.
  31. // Since zero-sized IOBuffer(s) are not supported, we set the
  32. // out pointer to null.
  33. *out = nullptr;
  34. return true;
  35. }
  36. auto* data = node::Buffer::Data(val);
  37. if (!data) {
  38. // This is an error as size is positive but data is null.
  39. return false;
  40. }
  41. *out = new net::IOBufferWithSize(size);
  42. // We do a deep copy. We could have used Buffer's internal memory
  43. // but that is much more complicated to be properly handled.
  44. memcpy((*out)->data(), data, size);
  45. return true;
  46. }
  47. };
  48. } // namespace mate
  49. namespace atom {
  50. namespace api {
  51. template <typename Flags>
  52. URLRequest::StateBase<Flags>::StateBase(Flags initialState)
  53. : state_(initialState) {}
  54. template <typename Flags>
  55. void URLRequest::StateBase<Flags>::SetFlag(Flags flag) {
  56. state_ =
  57. static_cast<Flags>(static_cast<int>(state_) | static_cast<int>(flag));
  58. }
  59. template <typename Flags>
  60. bool URLRequest::StateBase<Flags>::operator==(Flags flag) const {
  61. return state_ == flag;
  62. }
  63. template <typename Flags>
  64. bool URLRequest::StateBase<Flags>::IsFlagSet(Flags flag) const {
  65. return static_cast<int>(state_) & static_cast<int>(flag);
  66. }
  67. URLRequest::RequestState::RequestState()
  68. : StateBase(RequestStateFlags::kNotStarted) {}
  69. bool URLRequest::RequestState::NotStarted() const {
  70. return *this == RequestStateFlags::kNotStarted;
  71. }
  72. bool URLRequest::RequestState::Started() const {
  73. return IsFlagSet(RequestStateFlags::kStarted);
  74. }
  75. bool URLRequest::RequestState::Finished() const {
  76. return IsFlagSet(RequestStateFlags::kFinished);
  77. }
  78. bool URLRequest::RequestState::Canceled() const {
  79. return IsFlagSet(RequestStateFlags::kCanceled);
  80. }
  81. bool URLRequest::RequestState::Failed() const {
  82. return IsFlagSet(RequestStateFlags::kFailed);
  83. }
  84. bool URLRequest::RequestState::Closed() const {
  85. return IsFlagSet(RequestStateFlags::kClosed);
  86. }
  87. URLRequest::ResponseState::ResponseState()
  88. : StateBase(ResponseStateFlags::kNotStarted) {}
  89. bool URLRequest::ResponseState::NotStarted() const {
  90. return *this == ResponseStateFlags::kNotStarted;
  91. }
  92. bool URLRequest::ResponseState::Started() const {
  93. return IsFlagSet(ResponseStateFlags::kStarted);
  94. }
  95. bool URLRequest::ResponseState::Ended() const {
  96. return IsFlagSet(ResponseStateFlags::kEnded);
  97. }
  98. bool URLRequest::ResponseState::Failed() const {
  99. return IsFlagSet(ResponseStateFlags::kFailed);
  100. }
  101. URLRequest::URLRequest(v8::Isolate* isolate, v8::Local<v8::Object> wrapper) {
  102. InitWith(isolate, wrapper);
  103. }
  104. URLRequest::~URLRequest() {
  105. // A request has been created in JS, it was not used and then
  106. // it got collected, no close event to cleanup, only destructor
  107. // is called.
  108. if (atom_request_) {
  109. atom_request_->Terminate();
  110. }
  111. }
  112. // static
  113. mate::WrappableBase* URLRequest::New(mate::Arguments* args) {
  114. auto* isolate = args->isolate();
  115. v8::Local<v8::Object> options;
  116. args->GetNext(&options);
  117. mate::Dictionary dict(isolate, options);
  118. std::string method;
  119. dict.Get("method", &method);
  120. std::string url;
  121. dict.Get("url", &url);
  122. std::string redirect_policy;
  123. dict.Get("redirect", &redirect_policy);
  124. std::string partition;
  125. mate::Handle<api::Session> session;
  126. if (dict.Get("session", &session)) {
  127. } else if (dict.Get("partition", &partition)) {
  128. session = Session::FromPartition(isolate, partition);
  129. } else {
  130. // Use the default session if not specified.
  131. session = Session::FromPartition(isolate, "");
  132. }
  133. auto* browser_context = session->browser_context();
  134. auto* api_url_request = new URLRequest(args->isolate(), args->GetThis());
  135. auto atom_url_request = AtomURLRequest::Create(
  136. browser_context, method, url, redirect_policy, api_url_request);
  137. api_url_request->atom_request_ = atom_url_request;
  138. return api_url_request;
  139. }
  140. // static
  141. void URLRequest::BuildPrototype(v8::Isolate* isolate,
  142. v8::Local<v8::FunctionTemplate> prototype) {
  143. prototype->SetClassName(mate::StringToV8(isolate, "URLRequest"));
  144. mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
  145. // Request API
  146. .MakeDestroyable()
  147. .SetMethod("write", &URLRequest::Write)
  148. .SetMethod("cancel", &URLRequest::Cancel)
  149. .SetMethod("setExtraHeader", &URLRequest::SetExtraHeader)
  150. .SetMethod("removeExtraHeader", &URLRequest::RemoveExtraHeader)
  151. .SetMethod("setChunkedUpload", &URLRequest::SetChunkedUpload)
  152. .SetMethod("followRedirect", &URLRequest::FollowRedirect)
  153. .SetMethod("_setLoadFlags", &URLRequest::SetLoadFlags)
  154. .SetProperty("notStarted", &URLRequest::NotStarted)
  155. .SetProperty("finished", &URLRequest::Finished)
  156. // Response APi
  157. .SetProperty("statusCode", &URLRequest::StatusCode)
  158. .SetProperty("statusMessage", &URLRequest::StatusMessage)
  159. .SetProperty("rawResponseHeaders", &URLRequest::RawResponseHeaders)
  160. .SetProperty("httpVersionMajor", &URLRequest::ResponseHttpVersionMajor)
  161. .SetProperty("httpVersionMinor", &URLRequest::ResponseHttpVersionMinor);
  162. }
  163. bool URLRequest::NotStarted() const {
  164. return request_state_.NotStarted();
  165. }
  166. bool URLRequest::Finished() const {
  167. return request_state_.Finished();
  168. }
  169. bool URLRequest::Canceled() const {
  170. return request_state_.Canceled();
  171. }
  172. bool URLRequest::Write(scoped_refptr<const net::IOBufferWithSize> buffer,
  173. bool is_last) {
  174. if (request_state_.Canceled() || request_state_.Failed() ||
  175. request_state_.Finished() || request_state_.Closed()) {
  176. return false;
  177. }
  178. if (request_state_.NotStarted()) {
  179. request_state_.SetFlag(RequestStateFlags::kStarted);
  180. // Pin on first write.
  181. Pin();
  182. }
  183. if (is_last) {
  184. request_state_.SetFlag(RequestStateFlags::kFinished);
  185. EmitRequestEvent(true, "finish");
  186. }
  187. DCHECK(atom_request_);
  188. if (atom_request_) {
  189. return atom_request_->Write(buffer, is_last);
  190. }
  191. return false;
  192. }
  193. void URLRequest::Cancel() {
  194. if (request_state_.Canceled() || request_state_.Closed()) {
  195. // Cancel only once.
  196. return;
  197. }
  198. // Mark as canceled.
  199. request_state_.SetFlag(RequestStateFlags::kCanceled);
  200. DCHECK(atom_request_);
  201. if (atom_request_ && request_state_.Started()) {
  202. // Really cancel if it was started.
  203. atom_request_->Cancel();
  204. }
  205. EmitRequestEvent(true, "abort");
  206. if (response_state_.Started() && !response_state_.Ended()) {
  207. EmitResponseEvent(true, "aborted");
  208. }
  209. Close();
  210. }
  211. void URLRequest::FollowRedirect() {
  212. if (request_state_.Canceled() || request_state_.Closed()) {
  213. return;
  214. }
  215. DCHECK(atom_request_);
  216. if (atom_request_) {
  217. atom_request_->FollowRedirect();
  218. }
  219. }
  220. bool URLRequest::SetExtraHeader(const std::string& name,
  221. const std::string& value) {
  222. // Request state must be in the initial non started state.
  223. if (!request_state_.NotStarted()) {
  224. // Cannot change headers after send.
  225. return false;
  226. }
  227. if (!net::HttpUtil::IsValidHeaderName(name)) {
  228. return false;
  229. }
  230. if (!net::HttpUtil::IsValidHeaderValue(value)) {
  231. return false;
  232. }
  233. DCHECK(atom_request_);
  234. if (atom_request_) {
  235. atom_request_->SetExtraHeader(name, value);
  236. }
  237. return true;
  238. }
  239. void URLRequest::RemoveExtraHeader(const std::string& name) {
  240. // State must be equal to not started.
  241. if (!request_state_.NotStarted()) {
  242. // Cannot change headers after send.
  243. return;
  244. }
  245. DCHECK(atom_request_);
  246. if (atom_request_) {
  247. atom_request_->RemoveExtraHeader(name);
  248. }
  249. }
  250. void URLRequest::SetChunkedUpload(bool is_chunked_upload) {
  251. // State must be equal to not started.
  252. if (!request_state_.NotStarted()) {
  253. // Cannot change headers after send.
  254. return;
  255. }
  256. DCHECK(atom_request_);
  257. if (atom_request_) {
  258. atom_request_->SetChunkedUpload(is_chunked_upload);
  259. }
  260. }
  261. void URLRequest::SetLoadFlags(int flags) {
  262. // State must be equal to not started.
  263. if (!request_state_.NotStarted()) {
  264. // Cannot change load flags after start.
  265. return;
  266. }
  267. DCHECK(atom_request_);
  268. if (atom_request_) {
  269. atom_request_->SetLoadFlags(flags);
  270. }
  271. }
  272. void URLRequest::OnReceivedRedirect(
  273. int status_code,
  274. const std::string& method,
  275. const GURL& url,
  276. scoped_refptr<net::HttpResponseHeaders> response_headers) {
  277. if (request_state_.Canceled() || request_state_.Closed()) {
  278. return;
  279. }
  280. DCHECK(atom_request_);
  281. if (!atom_request_) {
  282. return;
  283. }
  284. EmitRequestEvent(false, "redirect", status_code, method, url,
  285. response_headers.get());
  286. }
  287. void URLRequest::OnAuthenticationRequired(
  288. scoped_refptr<const net::AuthChallengeInfo> auth_info) {
  289. if (request_state_.Canceled() || request_state_.Closed()) {
  290. return;
  291. }
  292. DCHECK(atom_request_);
  293. if (!atom_request_) {
  294. return;
  295. }
  296. Emit("login", auth_info.get(),
  297. base::Bind(&AtomURLRequest::PassLoginInformation, atom_request_));
  298. }
  299. void URLRequest::OnResponseStarted(
  300. scoped_refptr<net::HttpResponseHeaders> response_headers) {
  301. if (request_state_.Canceled() || request_state_.Failed() ||
  302. request_state_.Closed()) {
  303. // Don't emit any event after request cancel.
  304. return;
  305. }
  306. response_headers_ = response_headers;
  307. response_state_.SetFlag(ResponseStateFlags::kStarted);
  308. Emit("response");
  309. }
  310. void URLRequest::OnResponseData(
  311. scoped_refptr<const net::IOBufferWithSize> buffer) {
  312. if (request_state_.Canceled() || request_state_.Closed() ||
  313. request_state_.Failed() || response_state_.Failed()) {
  314. // In case we received an unexpected event from Chromium net,
  315. // don't emit any data event after request cancel/error/close.
  316. return;
  317. }
  318. if (!buffer || !buffer->data() || !buffer->size()) {
  319. return;
  320. }
  321. Emit("data", buffer);
  322. }
  323. void URLRequest::OnResponseCompleted() {
  324. if (request_state_.Canceled() || request_state_.Closed() ||
  325. request_state_.Failed() || response_state_.Failed()) {
  326. // In case we received an unexpected event from Chromium net,
  327. // don't emit any data event after request cancel/error/close.
  328. return;
  329. }
  330. response_state_.SetFlag(ResponseStateFlags::kEnded);
  331. Emit("end");
  332. Close();
  333. }
  334. void URLRequest::OnError(const std::string& error, bool isRequestError) {
  335. auto error_object = v8::Exception::Error(mate::StringToV8(isolate(), error));
  336. if (isRequestError) {
  337. request_state_.SetFlag(RequestStateFlags::kFailed);
  338. EmitRequestEvent(false, "error", error_object);
  339. } else {
  340. response_state_.SetFlag(ResponseStateFlags::kFailed);
  341. EmitResponseEvent(false, "error", error_object);
  342. }
  343. Close();
  344. }
  345. int URLRequest::StatusCode() const {
  346. if (response_headers_) {
  347. return response_headers_->response_code();
  348. }
  349. return -1;
  350. }
  351. std::string URLRequest::StatusMessage() const {
  352. std::string result;
  353. if (response_headers_) {
  354. result = response_headers_->GetStatusText();
  355. }
  356. return result;
  357. }
  358. net::HttpResponseHeaders* URLRequest::RawResponseHeaders() const {
  359. return response_headers_.get();
  360. }
  361. uint32_t URLRequest::ResponseHttpVersionMajor() const {
  362. if (response_headers_) {
  363. return response_headers_->GetHttpVersion().major_value();
  364. }
  365. return 0;
  366. }
  367. uint32_t URLRequest::ResponseHttpVersionMinor() const {
  368. if (response_headers_) {
  369. return response_headers_->GetHttpVersion().minor_value();
  370. }
  371. return 0;
  372. }
  373. void URLRequest::Close() {
  374. if (!request_state_.Closed()) {
  375. request_state_.SetFlag(RequestStateFlags::kClosed);
  376. if (response_state_.Started()) {
  377. // Emit a close event if we really have a response object.
  378. EmitResponseEvent(true, "close");
  379. }
  380. EmitRequestEvent(true, "close");
  381. }
  382. Unpin();
  383. if (atom_request_) {
  384. // A request has been created in JS, used and then it ended.
  385. // We release unneeded net resources.
  386. atom_request_->Terminate();
  387. }
  388. atom_request_ = nullptr;
  389. }
  390. void URLRequest::Pin() {
  391. if (wrapper_.IsEmpty()) {
  392. wrapper_.Reset(isolate(), GetWrapper());
  393. }
  394. }
  395. void URLRequest::Unpin() {
  396. wrapper_.Reset();
  397. }
  398. template <typename... Args>
  399. void URLRequest::EmitRequestEvent(Args... args) {
  400. v8::HandleScope handle_scope(isolate());
  401. mate::CustomEmit(isolate(), GetWrapper(), "_emitRequestEvent", args...);
  402. }
  403. template <typename... Args>
  404. void URLRequest::EmitResponseEvent(Args... args) {
  405. v8::HandleScope handle_scope(isolate());
  406. mate::CustomEmit(isolate(), GetWrapper(), "_emitResponseEvent", args...);
  407. }
  408. } // namespace api
  409. } // namespace atom