123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- // Copyright (c) 2016 GitHub, Inc.
- // Use of this source code is governed by the MIT license that can be
- // found in the LICENSE file.
- #ifndef ATOM_BROWSER_API_ATOM_API_URL_REQUEST_H_
- #define ATOM_BROWSER_API_ATOM_API_URL_REQUEST_H_
- #include <array>
- #include <string>
- #include "atom/browser/api/event_emitter.h"
- #include "atom/browser/api/trackable_object.h"
- #include "base/memory/weak_ptr.h"
- #include "native_mate/dictionary.h"
- #include "native_mate/handle.h"
- #include "native_mate/wrappable_base.h"
- #include "net/base/auth.h"
- #include "net/base/io_buffer.h"
- #include "net/http/http_response_headers.h"
- #include "net/url_request/url_request_context.h"
- namespace atom {
- class AtomURLRequest;
- namespace api {
- //
- // The URLRequest class implements the V8 binding between the JavaScript API
- // and Chromium native net library. It is responsible for handling HTTP/HTTPS
- // requests.
- //
- // The current class provides only the binding layer. Two other JavaScript
- // classes (ClientRequest and IncomingMessage) in the net module provide the
- // final API, including some state management and arguments validation.
- //
- // URLRequest's methods fall into two main categories: command and event
- // methods. They are always executed on the Browser's UI thread.
- // Command methods are called directly from JavaScript code via the API defined
- // in BuildPrototype. A command method is generally implemented by forwarding
- // the call to a corresponding method on AtomURLRequest which does the
- // synchronization on the Browser IO thread. The latter then calls into Chromium
- // net library. On the other hand, net library events originate on the IO
- // thread in AtomURLRequest and are synchronized back on the UI thread, then
- // forwarded to a corresponding event method in URLRequest and then to
- // JavaScript via the EmitRequestEvent/EmitResponseEvent helpers.
- //
- // URLRequest lifetime management: we followed the Wrapper/Wrappable pattern
- // defined in native_mate. However, we augment that pattern with a pin/unpin
- // mechanism. The main reason is that we want the JS API to provide a similar
- // lifetime guarantees as the XMLHttpRequest.
- // https://xhr.spec.whatwg.org/#garbage-collection
- //
- // The primary motivation is to not garbage collect a URLInstance as long as the
- // object is emitting network events. For instance, in the following JS code
- //
- // (function() {
- // let request = new URLRequest(...);
- // request.on('response', (response)=>{
- // response.on('data', (data) = > {
- // console.log(data.toString());
- // });
- // });
- // })();
- //
- // we still want data to be logged even if the response/request objects are n
- // more referenced in JavaScript.
- //
- // Binding by simply following the native_mate Wrapper/Wrappable pattern will
- // delete the URLRequest object when the corresponding JS object is collected.
- // The v8 handle is a private member in WrappableBase and it is always weak,
- // there is no way to make it strong without changing native_mate.
- // The solution we implement consists of maintaining some kind of state that
- // prevents collection of JS wrappers as long as the request is emitting network
- // events. At initialization, the object is unpinned. When the request starts,
- // it is pinned. When no more events would be emitted, the object is unpinned
- // and lifetime is again managed by the standard native mate Wrapper/Wrappable
- // pattern.
- //
- // pin/unpin: are implemented by constructing/reseting a V8 strong persistent
- // handle.
- //
- // The URLRequest/AtmURLRequest interaction could have been implemented in a
- // single class. However, it implies that the resulting class lifetime will be
- // managed by two conflicting mechanisms: JavaScript garbage collection and
- // Chromium reference counting. Reasoning about lifetime issues become much
- // more complex.
- //
- // We chose to split the implementation into two classes linked via a
- // reference counted/raw pointers. A URLRequest instance is deleted if it is
- // unpinned and the corresponding JS wrapper object is garbage collected. On the
- // other hand, an AtmURLRequest instance lifetime is totally governed by
- // reference counting.
- //
- class URLRequest : public mate::EventEmitter<URLRequest> {
- public:
- static mate::WrappableBase* New(mate::Arguments* args);
- static void BuildPrototype(v8::Isolate* isolate,
- v8::Local<v8::FunctionTemplate> prototype);
- // Methods for reporting events into JavaScript.
- void OnReceivedRedirect(
- int status_code,
- const std::string& method,
- const GURL& url,
- scoped_refptr<net::HttpResponseHeaders> response_headers);
- void OnAuthenticationRequired(
- scoped_refptr<const net::AuthChallengeInfo> auth_info);
- void OnResponseStarted(
- scoped_refptr<net::HttpResponseHeaders> response_headers);
- void OnResponseData(scoped_refptr<const net::IOBufferWithSize> data);
- void OnResponseCompleted();
- void OnError(const std::string& error, bool isRequestError);
- mate::Dictionary GetUploadProgress(v8::Isolate* isolate);
- protected:
- explicit URLRequest(v8::Isolate* isolate, v8::Local<v8::Object> wrapper);
- ~URLRequest() override;
- private:
- template <typename Flags>
- class StateBase {
- public:
- void SetFlag(Flags flag);
- protected:
- explicit StateBase(Flags initialState);
- bool operator==(Flags flag) const;
- bool IsFlagSet(Flags flag) const;
- private:
- Flags state_;
- };
- enum class RequestStateFlags {
- kNotStarted = 0x0,
- kStarted = 0x1,
- kFinished = 0x2,
- kCanceled = 0x4,
- kFailed = 0x8,
- kClosed = 0x10
- };
- class RequestState : public StateBase<RequestStateFlags> {
- public:
- RequestState();
- bool NotStarted() const;
- bool Started() const;
- bool Finished() const;
- bool Canceled() const;
- bool Failed() const;
- bool Closed() const;
- };
- enum class ResponseStateFlags {
- kNotStarted = 0x0,
- kStarted = 0x1,
- kEnded = 0x2,
- kFailed = 0x4
- };
- class ResponseState : public StateBase<ResponseStateFlags> {
- public:
- ResponseState();
- bool NotStarted() const;
- bool Started() const;
- bool Ended() const;
- bool Canceled() const;
- bool Failed() const;
- bool Closed() const;
- };
- bool NotStarted() const;
- bool Finished() const;
- bool Canceled() const;
- bool Failed() const;
- bool Write(scoped_refptr<const net::IOBufferWithSize> buffer, bool is_last);
- void Cancel();
- void FollowRedirect();
- bool SetExtraHeader(const std::string& name, const std::string& value);
- void RemoveExtraHeader(const std::string& name);
- void SetChunkedUpload(bool is_chunked_upload);
- void SetLoadFlags(int flags);
- int StatusCode() const;
- std::string StatusMessage() const;
- net::HttpResponseHeaders* RawResponseHeaders() const;
- uint32_t ResponseHttpVersionMajor() const;
- uint32_t ResponseHttpVersionMinor() const;
- void Close();
- void Pin();
- void Unpin();
- template <typename... Args>
- void EmitRequestEvent(Args... args);
- template <typename... Args>
- void EmitResponseEvent(Args... args);
- scoped_refptr<AtomURLRequest> atom_request_;
- RequestState request_state_;
- ResponseState response_state_;
- // Used to implement pin/unpin.
- v8::Global<v8::Object> wrapper_;
- scoped_refptr<net::HttpResponseHeaders> response_headers_;
- DISALLOW_COPY_AND_ASSIGN(URLRequest);
- };
- } // namespace api
- } // namespace atom
- #endif // ATOM_BROWSER_API_ATOM_API_URL_REQUEST_H_
|