atom_api_url_request.h 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  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. #ifndef ATOM_BROWSER_API_ATOM_API_URL_REQUEST_H_
  5. #define ATOM_BROWSER_API_ATOM_API_URL_REQUEST_H_
  6. #include <array>
  7. #include <string>
  8. #include "atom/browser/api/event_emitter.h"
  9. #include "atom/browser/api/trackable_object.h"
  10. #include "base/memory/weak_ptr.h"
  11. #include "native_mate/dictionary.h"
  12. #include "native_mate/handle.h"
  13. #include "native_mate/wrappable_base.h"
  14. #include "net/base/auth.h"
  15. #include "net/base/io_buffer.h"
  16. #include "net/http/http_response_headers.h"
  17. #include "net/url_request/url_request_context.h"
  18. namespace atom {
  19. class AtomURLRequest;
  20. namespace api {
  21. //
  22. // The URLRequest class implements the V8 binding between the JavaScript API
  23. // and Chromium native net library. It is responsible for handling HTTP/HTTPS
  24. // requests.
  25. //
  26. // The current class provides only the binding layer. Two other JavaScript
  27. // classes (ClientRequest and IncomingMessage) in the net module provide the
  28. // final API, including some state management and arguments validation.
  29. //
  30. // URLRequest's methods fall into two main categories: command and event
  31. // methods. They are always executed on the Browser's UI thread.
  32. // Command methods are called directly from JavaScript code via the API defined
  33. // in BuildPrototype. A command method is generally implemented by forwarding
  34. // the call to a corresponding method on AtomURLRequest which does the
  35. // synchronization on the Browser IO thread. The latter then calls into Chromium
  36. // net library. On the other hand, net library events originate on the IO
  37. // thread in AtomURLRequest and are synchronized back on the UI thread, then
  38. // forwarded to a corresponding event method in URLRequest and then to
  39. // JavaScript via the EmitRequestEvent/EmitResponseEvent helpers.
  40. //
  41. // URLRequest lifetime management: we followed the Wrapper/Wrappable pattern
  42. // defined in native_mate. However, we augment that pattern with a pin/unpin
  43. // mechanism. The main reason is that we want the JS API to provide a similar
  44. // lifetime guarantees as the XMLHttpRequest.
  45. // https://xhr.spec.whatwg.org/#garbage-collection
  46. //
  47. // The primary motivation is to not garbage collect a URLInstance as long as the
  48. // object is emitting network events. For instance, in the following JS code
  49. //
  50. // (function() {
  51. // let request = new URLRequest(...);
  52. // request.on('response', (response)=>{
  53. // response.on('data', (data) = > {
  54. // console.log(data.toString());
  55. // });
  56. // });
  57. // })();
  58. //
  59. // we still want data to be logged even if the response/request objects are n
  60. // more referenced in JavaScript.
  61. //
  62. // Binding by simply following the native_mate Wrapper/Wrappable pattern will
  63. // delete the URLRequest object when the corresponding JS object is collected.
  64. // The v8 handle is a private member in WrappableBase and it is always weak,
  65. // there is no way to make it strong without changing native_mate.
  66. // The solution we implement consists of maintaining some kind of state that
  67. // prevents collection of JS wrappers as long as the request is emitting network
  68. // events. At initialization, the object is unpinned. When the request starts,
  69. // it is pinned. When no more events would be emitted, the object is unpinned
  70. // and lifetime is again managed by the standard native mate Wrapper/Wrappable
  71. // pattern.
  72. //
  73. // pin/unpin: are implemented by constructing/reseting a V8 strong persistent
  74. // handle.
  75. //
  76. // The URLRequest/AtmURLRequest interaction could have been implemented in a
  77. // single class. However, it implies that the resulting class lifetime will be
  78. // managed by two conflicting mechanisms: JavaScript garbage collection and
  79. // Chromium reference counting. Reasoning about lifetime issues become much
  80. // more complex.
  81. //
  82. // We chose to split the implementation into two classes linked via a
  83. // reference counted/raw pointers. A URLRequest instance is deleted if it is
  84. // unpinned and the corresponding JS wrapper object is garbage collected. On the
  85. // other hand, an AtmURLRequest instance lifetime is totally governed by
  86. // reference counting.
  87. //
  88. class URLRequest : public mate::EventEmitter<URLRequest> {
  89. public:
  90. static mate::WrappableBase* New(mate::Arguments* args);
  91. static void BuildPrototype(v8::Isolate* isolate,
  92. v8::Local<v8::FunctionTemplate> prototype);
  93. // Methods for reporting events into JavaScript.
  94. void OnReceivedRedirect(
  95. int status_code,
  96. const std::string& method,
  97. const GURL& url,
  98. scoped_refptr<net::HttpResponseHeaders> response_headers);
  99. void OnAuthenticationRequired(
  100. scoped_refptr<const net::AuthChallengeInfo> auth_info);
  101. void OnResponseStarted(
  102. scoped_refptr<net::HttpResponseHeaders> response_headers);
  103. void OnResponseData(scoped_refptr<const net::IOBufferWithSize> data);
  104. void OnResponseCompleted();
  105. void OnError(const std::string& error, bool isRequestError);
  106. mate::Dictionary GetUploadProgress(v8::Isolate* isolate);
  107. protected:
  108. explicit URLRequest(v8::Isolate* isolate, v8::Local<v8::Object> wrapper);
  109. ~URLRequest() override;
  110. private:
  111. template <typename Flags>
  112. class StateBase {
  113. public:
  114. void SetFlag(Flags flag);
  115. protected:
  116. explicit StateBase(Flags initialState);
  117. bool operator==(Flags flag) const;
  118. bool IsFlagSet(Flags flag) const;
  119. private:
  120. Flags state_;
  121. };
  122. enum class RequestStateFlags {
  123. kNotStarted = 0x0,
  124. kStarted = 0x1,
  125. kFinished = 0x2,
  126. kCanceled = 0x4,
  127. kFailed = 0x8,
  128. kClosed = 0x10
  129. };
  130. class RequestState : public StateBase<RequestStateFlags> {
  131. public:
  132. RequestState();
  133. bool NotStarted() const;
  134. bool Started() const;
  135. bool Finished() const;
  136. bool Canceled() const;
  137. bool Failed() const;
  138. bool Closed() const;
  139. };
  140. enum class ResponseStateFlags {
  141. kNotStarted = 0x0,
  142. kStarted = 0x1,
  143. kEnded = 0x2,
  144. kFailed = 0x4
  145. };
  146. class ResponseState : public StateBase<ResponseStateFlags> {
  147. public:
  148. ResponseState();
  149. bool NotStarted() const;
  150. bool Started() const;
  151. bool Ended() const;
  152. bool Canceled() const;
  153. bool Failed() const;
  154. bool Closed() const;
  155. };
  156. bool NotStarted() const;
  157. bool Finished() const;
  158. bool Canceled() const;
  159. bool Failed() const;
  160. bool Write(scoped_refptr<const net::IOBufferWithSize> buffer, bool is_last);
  161. void Cancel();
  162. void FollowRedirect();
  163. bool SetExtraHeader(const std::string& name, const std::string& value);
  164. void RemoveExtraHeader(const std::string& name);
  165. void SetChunkedUpload(bool is_chunked_upload);
  166. void SetLoadFlags(int flags);
  167. int StatusCode() const;
  168. std::string StatusMessage() const;
  169. net::HttpResponseHeaders* RawResponseHeaders() const;
  170. uint32_t ResponseHttpVersionMajor() const;
  171. uint32_t ResponseHttpVersionMinor() const;
  172. void Close();
  173. void Pin();
  174. void Unpin();
  175. template <typename... Args>
  176. void EmitRequestEvent(Args... args);
  177. template <typename... Args>
  178. void EmitResponseEvent(Args... args);
  179. scoped_refptr<AtomURLRequest> atom_request_;
  180. RequestState request_state_;
  181. ResponseState response_state_;
  182. // Used to implement pin/unpin.
  183. v8::Global<v8::Object> wrapper_;
  184. scoped_refptr<net::HttpResponseHeaders> response_headers_;
  185. DISALLOW_COPY_AND_ASSIGN(URLRequest);
  186. };
  187. } // namespace api
  188. } // namespace atom
  189. #endif // ATOM_BROWSER_API_ATOM_API_URL_REQUEST_H_