diff --git a/atom/browser/api/atom_api_url_request.cc b/atom/browser/api/atom_api_url_request.cc index 623487fa6782..8bd8074f966c 100644 --- a/atom/browser/api/atom_api_url_request.cc +++ b/atom/browser/api/atom_api_url_request.cc @@ -115,8 +115,6 @@ mate::WrappableBase* URLRequest::New(mate::Arguments* args) { auto session = Session::FromPartition(args->isolate(), session_name); auto browser_context = session->browser_context(); - - 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( @@ -126,12 +124,10 @@ mate::WrappableBase* URLRequest::New(mate::Arguments* args) { weak_ptr); api_url_request->atom_request_ = atom_url_request; - return api_url_request; } - // static void URLRequest::BuildPrototype(v8::Isolate* isolate, v8::Local prototype) { @@ -139,17 +135,17 @@ void URLRequest::BuildPrototype(v8::Isolate* isolate, mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate()) // Request API .MakeDestroyable() - .SetMethod("_writeBuffer", &URLRequest::WriteBuffer) + .SetMethod("writeBuffer", &URLRequest::WriteBuffer) .SetMethod("abort", &URLRequest::Abort) - .SetMethod("_setHeader", &URLRequest::SetHeader) - .SetMethod("_getHeader", &URLRequest::GetHeader) - .SetMethod("_removaHeader", &URLRequest::RemoveHeader) + .SetMethod("setExtraHeader", &URLRequest::SetExtraHeader) + .SetMethod("removeExtraHeader", &URLRequest::RemoveExtraHeader) + .SetMethod("setChunkedUpload", &URLRequest::SetChunkedUpload) // Response APi - .SetProperty("_statusCode", &URLRequest::StatusCode) - .SetProperty("_statusMessage", &URLRequest::StatusMessage) - .SetProperty("_rawResponseHeaders", &URLRequest::RawResponseHeaders) - .SetProperty("_httpVersionMajor", &URLRequest::ResponseHttpVersionMajor) - .SetProperty("_httpVersionMinor", &URLRequest::ResponseHttpVersionMinor); + .SetProperty("statusCode", &URLRequest::StatusCode) + .SetProperty("statusMessage", &URLRequest::StatusMessage) + .SetProperty("rawResponseHeaders", &URLRequest::RawResponseHeaders) + .SetProperty("httpVersionMajor", &URLRequest::ResponseHttpVersionMajor) + .SetProperty("httpVersionMinor", &URLRequest::ResponseHttpVersionMinor); } @@ -166,7 +162,7 @@ void URLRequest::Abort() { atom_request_->Abort(); } -bool URLRequest::SetHeader(const std::string& name, +bool URLRequest::SetExtraHeader(const std::string& name, const std::string& value) { if (!net::HttpUtil::IsValidHeaderName(name)) { return false; @@ -176,14 +172,16 @@ bool URLRequest::SetHeader(const std::string& name, return false; } - atom_request_->SetHeader(name, value); + atom_request_->SetExtraHeader(name, value); return true; } -std::string URLRequest::GetHeader(const std::string& name) { - return atom_request_->GetHeader(name); + +void URLRequest::RemoveExtraHeader(const std::string& name) { + atom_request_->RemoveExtraHeader(name); } -void URLRequest::RemoveHeader(const std::string& name) { - atom_request_->RemoveHeader(name); + +void URLRequest::SetChunkedUpload(bool is_chunked_upload) { + atom_request_->SetChunkedUpload(is_chunked_upload); } void URLRequest::OnAuthenticationRequired( @@ -196,7 +194,7 @@ void URLRequest::OnAuthenticationRequired( void URLRequest::OnResponseStarted() { - EmitRequestEvent("response"); + Emit("response"); } void URLRequest::OnResponseData( diff --git a/atom/browser/api/atom_api_url_request.h b/atom/browser/api/atom_api_url_request.h index 55bc516c4d13..34373978844b 100644 --- a/atom/browser/api/atom_api_url_request.h +++ b/atom/browser/api/atom_api_url_request.h @@ -35,9 +35,9 @@ private: bool WriteBuffer(scoped_refptr buffer, bool is_last); void Abort(); - bool SetHeader(const std::string& name, const std::string& value); - std::string GetHeader(const std::string& name); - void RemoveHeader(const std::string& name); + bool SetExtraHeader(const std::string& name, const std::string& value); + void RemoveExtraHeader(const std::string& name); + void SetChunkedUpload(bool is_chunked_upload); friend class AtomURLRequest; void OnAuthenticationRequired( diff --git a/atom/browser/net/atom_url_request.cc b/atom/browser/net/atom_url_request.cc index 6afc0cbb0483..f9707f3dae81 100644 --- a/atom/browser/net/atom_url_request.cc +++ b/atom/browser/net/atom_url_request.cc @@ -95,13 +95,13 @@ bool AtomURLRequest::WriteBuffer( base::Bind(&AtomURLRequest::DoWriteBuffer, this, buffer, is_last)); } -void AtomURLRequest::SetChunkedUpload() { +void AtomURLRequest::SetChunkedUpload(bool is_chunked_upload) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); // The method can be called only before switching to multi-threaded mode, // i.e. before the first call to write. // So it is safe to change the object in the UI thread. - is_chunked_upload_ = true; + is_chunked_upload_ = is_chunked_upload; } @@ -109,26 +109,14 @@ void AtomURLRequest::Abort() const { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); } -void AtomURLRequest::SetHeader(const std::string& name, +void AtomURLRequest::SetExtraHeader(const std::string& name, const std::string& value) const { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); request_->SetExtraRequestHeaderByName(name, value, true); } -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(const std::string& name) const { +void AtomURLRequest::RemoveExtraHeader(const std::string& name) const { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); request_->RemoveRequestHeaderByName(name); } diff --git a/atom/browser/net/atom_url_request.h b/atom/browser/net/atom_url_request.h index 38f60c8d3b5a..a3288bfab6ff 100644 --- a/atom/browser/net/atom_url_request.h +++ b/atom/browser/net/atom_url_request.h @@ -36,11 +36,10 @@ public: bool WriteBuffer(scoped_refptr buffer, bool is_last); - void SetChunkedUpload(); + void SetChunkedUpload(bool is_chunked_upload); 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 SetExtraHeader(const std::string& name, const std::string& value) const; + void RemoveExtraHeader(const std::string& name) const; void PassLoginInformation(const base::string16& username, const base::string16& password) const; scoped_refptr GetResponseHeaders() const; diff --git a/lib/browser/api/net.js b/lib/browser/api/net.js index 7392c7687ce1..3594d0b2a373 100644 --- a/lib/browser/api/net.js +++ b/lib/browser/api/net.js @@ -8,22 +8,22 @@ const {URLRequest} = net Object.setPrototypeOf(Net.prototype, EventEmitter.prototype) Object.setPrototypeOf(URLRequest.prototype, EventEmitter.prototype) -class URLResponse extends EventEmitter { - constructor(request) { +class IncomingMessage extends EventEmitter { + constructor(url_request) { super(); - this.request = request; + this._url_request = url_request; } get statusCode() { - return this.request._statusCode; + return this._url_request.statusCode; } get statusMessage() { - return this.request._statusMessage; + return this._url_request.statusMessage; } get headers() { - return this.request._rawResponseHeaders; + return this._url_request.rawResponseHeaders; } get httpVersion() { @@ -31,161 +31,195 @@ class URLResponse extends EventEmitter { } get httpVersionMajor() { - return this.request._httpVersionMajor; + return this._url_request.httpVersionMajor; } get httpVersionMinor() { - return this.request._httpVersionMinor; + return this._url_request.httpVersionMinor; } get rawHeaders() { - return this.request._rawResponseHeaders; + return this._url_request.rawResponseHeaders; } } -Net.prototype.request = function(options, callback) { - let request = new URLRequest(options) - - if (callback) { - request.once('response', callback) - } - - - return request -} - -URLRequest.prototype._init = function() { - // Flag to prevent writings after end. - this._finished = false; - - // Set when the request uses chuned encoding. Can be switched - // to true only once and never set back to false. - this._chunkedEncoding = false; - - // Flag to prevent request's headers modifications after - // headers flush. - 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); - this.emit(name, this.response); - } else { - this.emit.apply(this, arguments); - } +URLRequest.prototype._emitRequestEvent = function() { + this._request.emit.apply(this._request, arguments); } URLRequest.prototype._emitResponseEvent = function() { - this.response.emit.apply(this.response, arguments); + this._response.emit.apply(this._response, arguments); } +class ClientRequest extends EventEmitter { -URLRequest.prototype._write = function(chunk, encoding, callback, is_last) { + constructor(options, callback) { + super(); + + // Flag to prevent writings after end. + this._finished = false; + + // Set when the request uses chuned encoding. Can be switched + // to true only once and never set back to false. + this._chunkedEncoding = false; + + // Flag to prevent request's headers modifications after + // headers flush. + this._headersSent = false; + + // This is a copy of the extra headers structure held by the native + // net::URLRequest. The main reason is to keep the getHeader API synchronous + // after the request starts. + this._extra_headers = {}; + + let url = options.url; + let method = options.method; + let session = options.session; + + let url_request = new URLRequest({ + url: url, + method: method, + session: session + }); + + this._url_request = url_request; + url_request._request = this; + + url_request.on('response', ()=> { + let response = new IncomingMessage(url_request); + url_request._response = response; + this.emit('response', response); + }); + + if (callback) { + this.once('response', callback) + } + } + + + setHeader(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.'); + + let key = name.toLowerCase(); + this._extra_headers[key] = value; + this._url_request.setExtraHeader(name, value) + } + + + getHeader(name) { + if (arguments.length < 1) { + throw new Error('`name` is required for getHeader(name).'); + } + + if (!this._extra_headers) { + return; + } + + var key = name.toLowerCase(); + return this._extra_headers[key]; + } + + + removeHeader(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.'); + } + + var key = name.toLowerCase(); + delete this._extra_headers[key]; + this._url_request.removeExtraHeader(name); + } + + + _write(chunk, encoding, callback, is_last) { - let chunk_is_string = typeof chunk === 'string'; - let chunk_is_buffer = chunk instanceof Buffer; - if (!chunk_is_string && !chunk_is_buffer) { - throw new TypeError('First argument must be a string or Buffer.'); + let chunk_is_string = typeof chunk === 'string'; + let chunk_is_buffer = chunk instanceof Buffer; + if (!chunk_is_string && !chunk_is_buffer) { + throw new TypeError('First argument must be a string or Buffer.'); + } + + if (chunk_is_string) { + // We convert all strings into binary buffers. + chunk = Buffer.from(chunk, encoding); + } + + // Headers are assumed to be sent on first call to _writeBuffer, + // i.e. after the first call to write or end. + let result = this._url_request.writeBuffer(chunk, is_last); + + // Since writing to the network is asynchronous, we conservatively + // assume that request headers are written after delivering the first + // buffer to the network IO thread. + if (!this._headersSent) { + this._url_request.setChunkedUpload(this._chunkedEncoding); + this._headersSent = true; + } + + // The write callback is fired asynchronously to mimic Node.js. + if (callback) { + process.nextTick(callback); + } + + return result; } - if (chunk_is_string) { - // We convert all strings into binary buffers. - chunk = Buffer.from(chunk, encoding); + write(data, encoding, callback) { + + if (this._finished) { + let error = new Error('Write after end.'); + process.nextTick(writeAfterEndNT, this, error, callback); + return true; + } + + return this._write(data, encoding, callback, false); } - // Headers are assumed to be sent on first call to _writeBuffer, - // i.e. after the first call to write or end. - let result = this._writeBuffer(chunk, is_last); - // Since writing to the network is asynchronous, we conservatively - // assume that request headers are written after delivering the first - // buffer to the network IO thread. - this._headersSent = true; + end(data, encoding, callback) { + if (this._finished) { + return false; + } + + this._finished = true; - // The write callback is fired asynchronously to mimic Node.js. - if (callback) { - process.nextTick(callback); + if (typeof data === 'function') { + callback = data; + encoding = null; + data = null; + } else if (typeof encoding === 'function') { + callback = encoding; + encoding = null; + } + + data = data || ''; + + return this._write(data, encoding, callback, true); } - return result; } -URLRequest.prototype.write = function(data, encoding, callback) { - - if (this._finished) { - let error = new Error('Write after end.'); - process.nextTick(writeAfterEndNT, this, error, callback); - return true; - } - - return this._write(data, encoding, callback, false); -}; - - -URLRequest.prototype.end = function(data, encoding, callback) { - if (this._finished) { - return false; - } - - this._finished = true; - - if (typeof data === 'function') { - callback = data; - encoding = null; - data = null; - } else if (typeof encoding === 'function') { - callback = encoding; - encoding = null; - } - - data = data || ''; - - return this._write(data, encoding, callback, true); -}; - - function writeAfterEndNT(self, error, callback) { self.emit('error', error); if (callback) callback(error); } +Net.prototype.request = function(options, callback) { + return new ClientRequest(options, callback); +} + +net.ClientRequest = ClientRequest; + module.exports = net \ No newline at end of file