diff --git a/atom/browser/api/atom_api_url_request.cc b/atom/browser/api/atom_api_url_request.cc index 5e71f243c493..0b4939f2b978 100644 --- a/atom/browser/api/atom_api_url_request.cc +++ b/atom/browser/api/atom_api_url_request.cc @@ -8,6 +8,9 @@ #include "native_mate/dictionary.h" #include "atom/browser/net/atom_url_request.h" #include "atom/common/node_includes.h" +#include "atom/common/native_mate_converters/net_converter.h" +#include "atom/common/native_mate_converters/string16_converter.h" +#include "atom/common/native_mate_converters/callback.h" namespace { @@ -19,9 +22,9 @@ const char* const kEnd = "end"; namespace mate { template<> -struct Converter> { +struct Converter> { static v8::Local ToV8(v8::Isolate* isolate, - scoped_refptr val) { + scoped_refptr val) { mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate); if (val) { @@ -37,10 +40,10 @@ struct Converter> { }; template<> -struct Converter> { +struct Converter> { static v8::Local ToV8( v8::Isolate* isolate, - scoped_refptr buffer) { + scoped_refptr buffer) { return node::Buffer::Copy(isolate, buffer->data(), buffer->size()).ToLocalChecked(); } }; @@ -79,11 +82,13 @@ mate::WrappableBase* URLRequest::New(mate::Arguments* args) { auto api_url_request = new URLRequest(args->isolate(), args->GetThis()); auto weak_ptr = api_url_request->weak_ptr_factory_.GetWeakPtr(); - auto atom_url_request = AtomURLRequest::create(browser_context, url, weak_ptr); - - atom_url_request->set_method(method); + auto atom_url_request = AtomURLRequest::Create( + browser_context, + method, + url, + weak_ptr); - api_url_request->atom_url_request_ = atom_url_request; + api_url_request->atom_request_ = atom_url_request; return api_url_request; @@ -97,12 +102,12 @@ void URLRequest::BuildPrototype(v8::Isolate* isolate, mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate()) // Request API .MakeDestroyable() - .SetMethod("write", &URLRequest::Write) - .SetMethod("end", &URLRequest::End) + .SetMethod("_write", &URLRequest::Write) + .SetMethod("_end", &URLRequest::End) .SetMethod("abort", &URLRequest::Abort) - .SetMethod("setHeader", &URLRequest::SetHeader) - .SetMethod("getHeader", &URLRequest::GetHeader) - .SetMethod("removaHeader", &URLRequest::RemoveHeader) + .SetMethod("_setHeader", &URLRequest::SetHeader) + .SetMethod("_getHeader", &URLRequest::GetHeader) + .SetMethod("_removaHeader", &URLRequest::RemoveHeader) // Response APi .SetProperty("statusCode", &URLRequest::StatusCode) .SetProperty("statusMessage", &URLRequest::StatusMessage) @@ -114,92 +119,83 @@ void URLRequest::BuildPrototype(v8::Isolate* isolate, } void URLRequest::Write() { - atom_url_request_->Write(); + atom_request_->Write(); } void URLRequest::End() { pin(); - atom_url_request_->End(); + atom_request_->End(); } void URLRequest::Abort() { - atom_url_request_->Abort(); + atom_request_->Abort(); } -void URLRequest::SetHeader() { - atom_url_request_->SetHeader(); +void URLRequest::SetHeader(const std::string& name, const std::string& value) { + atom_request_->SetHeader(name, value); } -void URLRequest::GetHeader() { - atom_url_request_->GetHeader(); +std::string URLRequest::GetHeader(const std::string& name) { + return atom_request_->GetHeader(name); } -void URLRequest::RemoveHeader() { - atom_url_request_->RemoveHeader(); +void URLRequest::RemoveHeader(const std::string& name) { + atom_request_->RemoveHeader(name); } +void URLRequest::OnAuthenticationRequired( + scoped_refptr auth_info) { + EmitRequestEvent( + "login", + auth_info.get(), + base::Bind(&AtomURLRequest::PassLoginInformation, atom_request_)); +} + void URLRequest::OnResponseStarted() { - //v8::Local _emitResponse; - - //auto wrapper = GetWrapper(); - //if (mate::Dictionary(isolate(), wrapper).Get("_emitResponse", &_emitResponse)) - // _emitResponse->Call(wrapper, 0, nullptr); EmitRequestEvent("response"); } -void URLRequest::OnResponseData(scoped_refptr buffer) { +void URLRequest::OnResponseData( + scoped_refptr buffer) { if (!buffer || !buffer->data() || !buffer->size()) { return; } EmitResponseEvent("data", buffer); - //v8::Local _emitData; - //auto data = mate::ConvertToV8(isolate(), buffer); - - //auto wrapper = GetWrapper(); - //if (mate::Dictionary(isolate(), wrapper).Get("_emitData", &_emitData)) - // _emitData->Call(wrapper, 1, &data); } void URLRequest::OnResponseCompleted() { - - //v8::Local _emitEnd; - - //auto wrapper = GetWrapper(); - //if (mate::Dictionary(isolate(), wrapper).Get("_emitEnd", &_emitEnd)) - // _emitEnd->Call(wrapper, 0, nullptr); - EmitResponseEvent("end"); } -int URLRequest::StatusCode() { - if (auto response_headers = atom_url_request_->GetResponseHeaders()) { +int URLRequest::StatusCode() const { + if (auto response_headers = atom_request_->GetResponseHeaders()) { return response_headers->response_code(); } return -1; } -std::string URLRequest::StatusMessage() { +std::string URLRequest::StatusMessage() const { std::string result; - if (auto response_headers = atom_url_request_->GetResponseHeaders()) { + if (auto response_headers = atom_request_->GetResponseHeaders()) { result = response_headers->GetStatusText(); } return result; } -scoped_refptr URLRequest::RawResponseHeaders() { - return atom_url_request_->GetResponseHeaders(); +scoped_refptr URLRequest::RawResponseHeaders() const { + return atom_request_->GetResponseHeaders(); } -uint32_t URLRequest::ResponseHttpVersionMajor() { - if (auto response_headers = atom_url_request_->GetResponseHeaders()) { +uint32_t URLRequest::ResponseHttpVersionMajor() const { + if (auto response_headers = atom_request_->GetResponseHeaders()) { return response_headers->GetHttpVersion().major_value(); } return 0; } -uint32_t URLRequest::ResponseHttpVersionMinor() { - if (auto response_headers = atom_url_request_->GetResponseHeaders()) { +uint32_t URLRequest::ResponseHttpVersionMinor() const { + if (auto response_headers = atom_request_->GetResponseHeaders()) { return response_headers->GetHttpVersion().minor_value(); } return 0; diff --git a/atom/browser/api/atom_api_url_request.h b/atom/browser/api/atom_api_url_request.h index 1ab4f1cc9171..7fd5c6ee924e 100644 --- a/atom/browser/api/atom_api_url_request.h +++ b/atom/browser/api/atom_api_url_request.h @@ -35,25 +35,27 @@ private: void Write(); void End(); void Abort(); - void SetHeader(); - void GetHeader(); - void RemoveHeader(); + void SetHeader(const std::string& name, const std::string& value); + std::string GetHeader(const std::string& name); + void RemoveHeader(const std::string& name); friend class AtomURLRequest; + void OnAuthenticationRequired( + scoped_refptr auth_info); void OnResponseStarted(); - void OnResponseData(scoped_refptr data); + void OnResponseData(scoped_refptr data); void OnResponseCompleted(); - int StatusCode(); - std::string StatusMessage(); - scoped_refptr RawResponseHeaders(); - uint32_t ResponseHttpVersionMajor(); - uint32_t ResponseHttpVersionMinor(); + int StatusCode() const; + std::string StatusMessage() const; + scoped_refptr RawResponseHeaders() const; + uint32_t ResponseHttpVersionMajor() const; + uint32_t ResponseHttpVersionMinor() const; template std::array, sizeof...(ArgTypes)> - BuildArgsArray(ArgTypes... args); + BuildArgsArray(ArgTypes... args) const; template void EmitRequestEvent(ArgTypes... args); @@ -61,12 +63,10 @@ private: template void EmitResponseEvent(ArgTypes... args); - - void pin(); void unpin(); - scoped_refptr atom_url_request_; + scoped_refptr atom_request_; v8::Global wrapper_; base::WeakPtrFactory weak_ptr_factory_; @@ -76,7 +76,7 @@ private: template std::array, sizeof...(ArgTypes)> -URLRequest::BuildArgsArray(ArgTypes... args) { +URLRequest::BuildArgsArray(ArgTypes... args) const { std::array, sizeof...(ArgTypes)> result = { mate::ConvertToV8(isolate(), args)... }; return result; diff --git a/atom/browser/net/atom_url_request.cc b/atom/browser/net/atom_url_request.cc index bdabbfc9fa61..9c825888aaed 100644 --- a/atom/browser/net/atom_url_request.cc +++ b/atom/browser/net/atom_url_request.cc @@ -10,6 +10,7 @@ #include "content/public/browser/browser_thread.h" #include "net/base/io_buffer.h" + namespace { const int kBufferSize = 4096; @@ -18,19 +19,20 @@ const int kBufferSize = 4096; namespace atom { -AtomURLRequest::AtomURLRequest(base::WeakPtr delegate) +AtomURLRequest::AtomURLRequest(base::WeakPtr delegate) : delegate_(delegate) - , buffer_( new net::IOBuffer(kBufferSize)) { + , buffer_(new net::IOBuffer(kBufferSize)) { } AtomURLRequest::~AtomURLRequest() { } -scoped_refptr AtomURLRequest::create( +scoped_refptr AtomURLRequest::Create( AtomBrowserContext* browser_context, + const std::string& method, const std::string& url, base::WeakPtr delegate) { - + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK(browser_context); DCHECK(!url.empty()); @@ -44,69 +46,117 @@ scoped_refptr AtomURLRequest::create( scoped_refptr atom_url_request = new AtomURLRequest(delegate); - atom_url_request->url_request_ = context->CreateRequest(GURL(url), + atom_url_request->request_ = context->CreateRequest(GURL(url), net::RequestPriority::DEFAULT_PRIORITY, atom_url_request.get()); + atom_url_request->request_->set_method(method); return atom_url_request; } -void AtomURLRequest::Write() { +void AtomURLRequest::Write() const { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); } -void AtomURLRequest::End() { - // Called on content::BrowserThread::UI +void AtomURLRequest::End() const { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); content::BrowserThread::PostTask( content::BrowserThread::IO, FROM_HERE, - base::Bind(&AtomURLRequest::StartOnIOThread, this)); + base::Bind(&AtomURLRequest::DoStart, this)); } -void AtomURLRequest::Abort() { +void AtomURLRequest::Abort() const { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); } -void AtomURLRequest::SetHeader() { - +void AtomURLRequest::SetHeader(const std::string& name, + const std::string& value) const { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + request_->SetExtraRequestHeaderByName(name, value, true); } -void AtomURLRequest::GetHeader() { - +std::string AtomURLRequest::GetHeader(const std::string& name) const { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + std::string result; + const auto& extra_headers = request_->extra_request_headers(); + if (!extra_headers.GetHeader(name, &result)) { + net::HttpRequestHeaders* request_headers = nullptr; + if (request_->GetFullRequestHeaders(request_headers) && request_headers) { + request_headers->GetHeader(name, &result); + } + } + return result; } -void AtomURLRequest::RemoveHeader() { - +void AtomURLRequest::RemoveHeader(const std::string& name) const { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + request_->RemoveRequestHeaderByName(name); } -scoped_refptr AtomURLRequest::GetResponseHeaders() { - return url_request_->response_headers(); +scoped_refptr +AtomURLRequest::GetResponseHeaders() const { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + return request_->response_headers(); } - -void AtomURLRequest::StartOnIOThread() { - // Called on content::BrowserThread::IO - - url_request_->Start(); +void AtomURLRequest::PassLoginInformation(const base::string16& username, + const base::string16& password) const { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (username.empty() || password.empty()) { + content::BrowserThread::PostTask( + content::BrowserThread::IO, FROM_HERE, + base::Bind(&AtomURLRequest::DoCancelAuth, this)); + } + else { + content::BrowserThread::PostTask( + content::BrowserThread::IO, FROM_HERE, + base::Bind(&AtomURLRequest::DoSetAuth, this, username, password)); + } } -void AtomURLRequest::set_method(const std::string& method) { - url_request_->set_method(method); +void AtomURLRequest::DoStart() const { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + request_->Start(); +} + +void AtomURLRequest::DoSetAuth(const base::string16& username, + const base::string16& password) const { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + request_->SetAuth(net::AuthCredentials(username, password)); +} + +void AtomURLRequest::DoCancelAuth() const { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + request_->CancelAuth(); +} + +void AtomURLRequest::OnAuthRequired(net::URLRequest* request, + net::AuthChallengeInfo* auth_info) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::Bind(&AtomURLRequest::InformDelegateAuthenticationRequired, + this, + scoped_refptr(auth_info))); } void AtomURLRequest::OnResponseStarted(net::URLRequest* request) { - // Called on content::BrowserThread::IO + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + DCHECK_EQ(request, request_.get()); - DCHECK_EQ(request, url_request_.get()); - - if (url_request_->status().is_success()) { + if (request_->status().is_success()) { // Cache net::HttpResponseHeaders instance, a read-only objects // so that headers and other http metainformation can be simultaneously // read from UI thread while request data is simulataneously streaming // on IO thread. - response_headers_ = url_request_->response_headers(); + response_headers_ = request_->response_headers(); } content::BrowserThread::PostTask( @@ -117,29 +167,28 @@ void AtomURLRequest::OnResponseStarted(net::URLRequest* request) { } void AtomURLRequest::ReadResponse() { - - // Called on content::BrowserThread::IO + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); // Some servers may treat HEAD requests as GET requests. To free up the // network connection as soon as possible, signal that the request has // completed immediately, without trying to read any data back (all we care // about is the response code and headers, which we already have). int bytes_read = 0; - if (url_request_->status().is_success() /* TODO && (request_type_ != URLFetcher::HEAD)*/) { - if (!url_request_->Read(buffer_.get(), kBufferSize, &bytes_read)) + if (request_->status().is_success() /* TODO && (request_type_ != URLFetcher::HEAD)*/) { + if (!request_->Read(buffer_.get(), kBufferSize, &bytes_read)) bytes_read = -1; } - OnReadCompleted(url_request_.get(), bytes_read); + OnReadCompleted(request_.get(), bytes_read); } void AtomURLRequest::OnReadCompleted(net::URLRequest* request, int bytes_read) { - // Called on content::BrowserThread::IO + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - DCHECK_EQ(request, url_request_.get()); + DCHECK_EQ(request, request_.get()); do { - if (!url_request_->status().is_success() || bytes_read <= 0) + if (!request_->status().is_success() || bytes_read <= 0) break; @@ -148,9 +197,9 @@ void AtomURLRequest::OnReadCompleted(net::URLRequest* request, int bytes_read) { // Failed to transfer data to UI thread. return; } - } while (url_request_->Read(buffer_.get(), kBufferSize, &bytes_read)); + } while (request_->Read(buffer_.get(), kBufferSize, &bytes_read)); - const auto status = url_request_->status(); + const auto status = request_->status(); if (!status.is_io_pending() /* TODO || request_type_ == URLFetcher::HEAD*/ ) { @@ -161,9 +210,8 @@ void AtomURLRequest::OnReadCompleted(net::URLRequest* request, int bytes_read) { } - bool AtomURLRequest::CopyAndPostBuffer(int bytes_read) { - // Called on content::BrowserThread::IO. + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); // data is only a wrapper for the async buffer_. // Make a deep copy of payload and transfer ownership to the UI thread. @@ -176,16 +224,25 @@ bool AtomURLRequest::CopyAndPostBuffer(int bytes_read) { } -void AtomURLRequest::InformDelegateResponseStarted() { - // Called on content::BrowserThread::UI. +void AtomURLRequest::InformDelegateAuthenticationRequired( + scoped_refptr auth_info) const { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (delegate_) { + delegate_->OnAuthenticationRequired(auth_info); + } +} + +void AtomURLRequest::InformDelegateResponseStarted() const { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (delegate_) { delegate_->OnResponseStarted(); } } -void AtomURLRequest::InformDelegateResponseData(scoped_refptr data) { - // Called on content::BrowserThread::IO. +void AtomURLRequest::InformDelegateResponseData( + scoped_refptr data) const { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); // Transfer ownership of the data buffer, data will be released // by the delegate's OnResponseData. @@ -194,7 +251,9 @@ void AtomURLRequest::InformDelegateResponseData(scoped_refptrOnResponseCompleted(); } diff --git a/atom/browser/net/atom_url_request.h b/atom/browser/net/atom_url_request.h index 1aeffd5560c9..be3c270ff9f9 100644 --- a/atom/browser/net/atom_url_request.h +++ b/atom/browser/net/atom_url_request.h @@ -27,43 +27,52 @@ namespace api { class AtomURLRequest : public base::RefCountedThreadSafe, public net::URLRequest::Delegate { public: - static scoped_refptr create( + static scoped_refptr Create( AtomBrowserContext* browser_context, + const std::string& method, const std::string& url, base::WeakPtr delegate); - void set_method(const std::string& method); - - void Write(); - void End(); - void Abort(); - void SetHeader(); - void GetHeader(); - void RemoveHeader(); - - scoped_refptr GetResponseHeaders(); + void Write() const; + void End() const; + void Abort() const; + void SetHeader(const std::string& name, const std::string& value) const; + std::string GetHeader(const std::string& name) const; + void RemoveHeader(const std::string& name) const; + void PassLoginInformation(const base::string16& username, + const base::string16& password) const; + scoped_refptr GetResponseHeaders() const; protected: // Overrides of net::URLRequest::Delegate + virtual void OnAuthRequired(net::URLRequest* request, + net::AuthChallengeInfo* auth_info) override; virtual void OnResponseStarted(net::URLRequest* request) override; - virtual void OnReadCompleted(net::URLRequest* request, int bytes_read) override; + virtual void OnReadCompleted(net::URLRequest* request, + int bytes_read) override; private: friend class base::RefCountedThreadSafe; - void StartOnIOThread(); + void DoStart() const; + void DoSetAuth(const base::string16& username, + const base::string16& password) const; + void DoCancelAuth() const; void ReadResponse(); bool CopyAndPostBuffer(int bytes_read); - void InformDelegateResponseStarted(); - void InformDelegateResponseData(scoped_refptr data); - void InformDelegateResponseCompleted(); + void InformDelegateAuthenticationRequired( + scoped_refptr auth_info) const; + void InformDelegateResponseStarted() const; + void InformDelegateResponseData( + scoped_refptr data) const; + void InformDelegateResponseCompleted() const; AtomURLRequest(base::WeakPtr delegate); virtual ~AtomURLRequest(); base::WeakPtr delegate_; - std::unique_ptr url_request_; + std::unique_ptr request_; scoped_refptr buffer_; scoped_refptr response_headers_; diff --git a/lib/browser/api/net.js b/lib/browser/api/net.js index 96c448020b88..a476cdf87ae7 100644 --- a/lib/browser/api/net.js +++ b/lib/browser/api/net.js @@ -55,6 +55,48 @@ Net.prototype.request = function(options, callback) { return request } +URLRequest.prototype._init = function() { + this._finished = false; + this._hasBody = true; + this._chunkedEncoding = false; + this._headersSent = false; +} + + +URLRequest.prototype.setHeader = function(name, value) { + if (typeof name !== 'string') + throw new TypeError('`name` should be a string in setHeader(name, value).'); + if (value === undefined) + throw new Error('`value` required in setHeader("' + name + '", value).'); + if (this._headersSent) + throw new Error('Can\'t set headers after they are sent.'); + + this._setHeader(name, value) +}; + + +URLRequest.prototype.getHeader = function(name) { + if (arguments.length < 1) { + throw new Error('`name` is required for getHeader(name).'); + } + + return this._getHeader(name); +}; + + +URLRequest.prototype.removeHeader = function(name) { + if (arguments.length < 1) { + throw new Error('`name` is required for removeHeader(name).'); + } + + if (this._headersSent) { + throw new Error('Can\'t remove headers after they are sent.'); + } + + this._removeHeader(name); +}; + + URLRequest.prototype._emitRequestEvent = function(name) { if (name === 'response') { this.response = new URLResponse(this); @@ -69,5 +111,82 @@ URLRequest.prototype._emitResponseEvent = function() { } +URLRequest.prototype.write = function(chunk, encoding, callback) { + + if (this.finished) { + var err = new Error('write after end'); + process.nextTick(writeAfterEndNT, this, err, callback); + + return true; + } + + /* TODO + if (!this._header) { + this._implicitHeader(); + } + */ + + if (!this._hasBody) { + //debug('This type of response MUST NOT have a body. ' + + // 'Ignoring write() calls.'); + return true; + } + + + if (typeof chunk !== 'string' && !(chunk instanceof Buffer)) { + throw new TypeError('First argument must be a string or Buffer'); + } + + + // If we get an empty string or buffer, then just do nothing, and + // signal the user to keep writing. + if (chunk.length === 0) return true; + + var len, ret; + if (this.chunkedEncoding) { + if (typeof chunk === 'string' && + encoding !== 'hex' && + encoding !== 'base64' && + encoding !== 'binary') { + len = Buffer.byteLength(chunk, encoding); + chunk = len.toString(16) + CRLF + chunk + CRLF; + ret = this._send(chunk, encoding, callback); + } else { + // buffer, or a non-toString-friendly encoding + if (typeof chunk === 'string') + len = Buffer.byteLength(chunk, encoding); + else + len = chunk.length; + + if (this.connection && !this.connection.corked) { + this.connection.cork(); + process.nextTick(connectionCorkNT, this.connection); + } + this._send(len.toString(16), 'binary', null); + this._send(crlf_buf, null, null); + this._send(chunk, encoding, null); + ret = this._send(crlf_buf, null, callback); + } + } else { + ret = this._send(chunk, encoding, callback); + } + + //debug('write ret = ' + ret); + return ret; +}; + + + +URLRequest.prototype.end = function() { + this._end(); +}; + + +function writeAfterEndNT(self, err, callback) { + self.emit('error', err); + if (callback) callback(err); +} + + module.exports = net \ No newline at end of file