void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
v8::Local<v8::Context> context, void* priv) {
v8::Isolate* isolate = context->GetIsolate();
- Menu::thehub
- SetConstructor(isolate, base::Bind(&Menu::New));
+ Menu::SetConstructor(isolate, base::Bind(&Menu::New));
mate::Dictionary dict(isolate, exports);
dict.Set("Menu", Menu::GetConstructor(isolate)->GetFunction());
#include "native_mate/dictionary.h"
#include "atom/browser/net/atom_url_request.h"
+#include "atom/common/node_includes.h"
+namespace {
+const char* const kResponse = "response";
+const char* const kData = "data";
+const char* const kEnd = "end";
+
+}
+namespace mate {
+
+template<>
+struct Converter<scoped_refptr<net::HttpResponseHeaders>> {
+ static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
+ scoped_refptr<net::HttpResponseHeaders> val) {
+
+ mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
+ if (val) {
+ size_t iter = 0;
+ std::string name;
+ std::string value;
+ while (val->EnumerateHeaderLines(&iter, &name, &value)) {
+ dict.Set(name, value);
+ }
+ }
+ return dict.GetHandle();
+ }
+};
+
+template<>
+struct Converter<scoped_refptr<net::IOBufferWithSize>> {
+ static v8::Local<v8::Value> ToV8(
+ v8::Isolate* isolate,
+ scoped_refptr<net::IOBufferWithSize> buffer) {
+ return node::Buffer::Copy(isolate, buffer->data(), buffer->size()).ToLocalChecked();
+ }
+};
+
+}
namespace atom {
namespace api {
-
+
URLRequest::URLRequest(v8::Isolate* isolate,
v8::Local<v8::Object> wrapper)
: weak_ptr_factory_(this) {
// Response APi
.SetProperty("statusCode", &URLRequest::StatusCode)
.SetProperty("statusMessage", &URLRequest::StatusMessage)
- .SetProperty("responseHeaders", &URLRequest::ResponseHeaders)
- .SetProperty("responseHttpVersion", &URLRequest::ResponseHttpVersion);
+ .SetProperty("rawResponseHeaders", &URLRequest::RawResponseHeaders)
+ .SetProperty("httpVersionMajor", &URLRequest::ResponseHttpVersionMajor)
+ .SetProperty("httpVersionMinor", &URLRequest::ResponseHttpVersionMinor);
+
+
}
void URLRequest::Write() {
void URLRequest::OnResponseStarted() {
- v8::Local<v8::Function> _emitResponse;
+ //v8::Local<v8::Function> _emitResponse;
- auto wrapper = GetWrapper();
- if (mate::Dictionary(isolate(), wrapper).Get("_emitResponse", &_emitResponse))
- _emitResponse->Call(wrapper, 0, nullptr);
+ //auto wrapper = GetWrapper();
+ //if (mate::Dictionary(isolate(), wrapper).Get("_emitResponse", &_emitResponse))
+ // _emitResponse->Call(wrapper, 0, nullptr);
+ EmitRequestEvent("response");
}
-void URLRequest::OnResponseData() {
- Emit("data");
+void URLRequest::OnResponseData(scoped_refptr<net::IOBufferWithSize> buffer) {
+ if (!buffer || !buffer->data() || !buffer->size()) {
+ return;
+ }
+
+ EmitResponseEvent("data", buffer);
+ //v8::Local<v8::Function> _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::OnResponseEnd() {
- Emit("end");
+void URLRequest::OnResponseCompleted() {
+
+ //v8::Local<v8::Function> _emitEnd;
+
+ //auto wrapper = GetWrapper();
+ //if (mate::Dictionary(isolate(), wrapper).Get("_emitEnd", &_emitEnd))
+ // _emitEnd->Call(wrapper, 0, nullptr);
+
+ EmitResponseEvent("end");
}
+
int URLRequest::StatusCode() {
- return atom_url_request_->StatusCode();
+ if (auto response_headers = atom_url_request_->GetResponseHeaders()) {
+ return response_headers->response_code();
+ }
+ return -1;
+}
+
+std::string URLRequest::StatusMessage() {
+ std::string result;
+ if (auto response_headers = atom_url_request_->GetResponseHeaders()) {
+ result = response_headers->GetStatusText();
+ }
+ return result;
}
-void URLRequest::StatusMessage() {
- return atom_url_request_->StatusMessage();
+scoped_refptr<net::HttpResponseHeaders> URLRequest::RawResponseHeaders() {
+ return atom_url_request_->GetResponseHeaders();
}
-void URLRequest::ResponseHeaders() {
- return atom_url_request_->ResponseHeaders();
+uint32_t URLRequest::ResponseHttpVersionMajor() {
+ if (auto response_headers = atom_url_request_->GetResponseHeaders()) {
+ return response_headers->GetHttpVersion().major_value();
+ }
+ return 0;
}
-void URLRequest::ResponseHttpVersion() {
- return atom_url_request_->ResponseHttpVersion();
+uint32_t URLRequest::ResponseHttpVersionMinor() {
+ if (auto response_headers = atom_url_request_->GetResponseHeaders()) {
+ return response_headers->GetHttpVersion().minor_value();
+ }
+ return 0;
}
void URLRequest::pin() {
#ifndef ATOM_BROWSER_API_ATOM_API_URL_REQUEST_H_
#define ATOM_BROWSER_API_ATOM_API_URL_REQUEST_H_
+#include <array>
#include "atom/browser/api/trackable_object.h"
#include "native_mate/handle.h"
#include "net/url_request/url_request_context.h"
+#include "net/http/http_response_headers.h"
+
namespace atom {
friend class AtomURLRequest;
void OnResponseStarted();
- void OnResponseData();
- void OnResponseEnd();
+ void OnResponseData(scoped_refptr<net::IOBufferWithSize> data);
+ void OnResponseCompleted();
int StatusCode();
- void StatusMessage();
- void ResponseHeaders();
- void ResponseHttpVersion();
+ std::string StatusMessage();
+ scoped_refptr<net::HttpResponseHeaders> RawResponseHeaders();
+ uint32_t ResponseHttpVersionMajor();
+ uint32_t ResponseHttpVersionMinor();
+
+
+ template <typename ... ArgTypes>
+ std::array<v8::Local<v8::Value>, sizeof...(ArgTypes)>
+ BuildArgsArray(ArgTypes... args);
+
+ template <typename ... ArgTypes>
+ void EmitRequestEvent(ArgTypes... args);
+
+ template <typename ... ArgTypes>
+ void EmitResponseEvent(ArgTypes... args);
+
void pin();
scoped_refptr<AtomURLRequest> atom_url_request_;
v8::Global<v8::Object> wrapper_;
base::WeakPtrFactory<URLRequest> weak_ptr_factory_;
+
DISALLOW_COPY_AND_ASSIGN(URLRequest);
};
+template <typename ... ArgTypes>
+std::array<v8::Local<v8::Value>, sizeof...(ArgTypes)>
+URLRequest::BuildArgsArray(ArgTypes... args) {
+ std::array<v8::Local<v8::Value>, sizeof...(ArgTypes)> result
+ = { mate::ConvertToV8(isolate(), args)... };
+ return result;
+}
+
+template <typename ... ArgTypes>
+void URLRequest::EmitRequestEvent(ArgTypes... args) {
+ auto arguments = BuildArgsArray(args...);
+ v8::Local<v8::Function> _emitRequestEvent;
+ auto wrapper = GetWrapper();
+ if (mate::Dictionary(isolate(), wrapper).Get("_emitRequestEvent", &_emitRequestEvent))
+ _emitRequestEvent->Call(wrapper, arguments.size(), arguments.data());
+}
+
+
+template <typename ... ArgTypes>
+void URLRequest::EmitResponseEvent(ArgTypes... args) {
+ auto arguments = BuildArgsArray(args...);
+ v8::Local<v8::Function> _emitResponseEvent;
+ auto wrapper = GetWrapper();
+ if (mate::Dictionary(isolate(), wrapper).Get("_emitResponseEvent", &_emitResponseEvent))
+ _emitResponseEvent->Call(wrapper, arguments.size(), arguments.data());
+}
+
+
+
+
} // namepsace api
} // namepsace atom
#include "atom/browser/atom_browser_context.h"
#include "base/callback.h"
#include "content/public/browser/browser_thread.h"
+#include "net/base/io_buffer.h"
+
+namespace {
+
+const int kBufferSize = 4096;
+
+} // namespace
namespace atom {
AtomURLRequest::AtomURLRequest(base::WeakPtr<api::URLRequest> delegate)
- : delegate_(delegate) {
+ : delegate_(delegate)
+ , buffer_( new net::IOBuffer(kBufferSize)) {
}
AtomURLRequest::~AtomURLRequest() {
}
void AtomURLRequest::End() {
- // post to io thread
+ // Called on content::BrowserThread::UI
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::Bind(&AtomURLRequest::StartOnIOThread, this));
+scoped_refptr<net::HttpResponseHeaders> AtomURLRequest::GetResponseHeaders() {
+ return url_request_->response_headers();
+}
-int AtomURLRequest::StatusCode() {
- return url_request_->GetResponseCode();
-}
+void AtomURLRequest::StartOnIOThread() {
+ // Called on content::BrowserThread::IO
-void AtomURLRequest::StatusMessage() {
+ url_request_->Start();
}
-void AtomURLRequest::ResponseHeaders() {
+
+
+void AtomURLRequest::set_method(const std::string& method) {
+ url_request_->set_method(method);
}
-void AtomURLRequest::ResponseHttpVersion() {
+void AtomURLRequest::OnResponseStarted(net::URLRequest* request) {
+ // Called on content::BrowserThread::IO
+
+ DCHECK_EQ(request, url_request_.get());
+
+ if (url_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();
+ }
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI, FROM_HERE,
+ base::Bind(&AtomURLRequest::InformDelegateResponseStarted, this));
+
+ ReadResponse();
}
+void AtomURLRequest::ReadResponse() {
+ // Called on content::BrowserThread::IO
-void AtomURLRequest::StartOnIOThread() {
- url_request_->Start();
+ // 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))
+ bytes_read = -1;
+ }
+ OnReadCompleted(url_request_.get(), bytes_read);
}
-void AtomURLRequest::set_method(const std::string& method) {
- url_request_->set_method(method);
+void AtomURLRequest::OnReadCompleted(net::URLRequest* request, int bytes_read) {
+ // Called on content::BrowserThread::IO
+
+ DCHECK_EQ(request, url_request_.get());
+
+ do {
+ if (!url_request_->status().is_success() || bytes_read <= 0)
+ break;
+
+
+ const auto result = CopyAndPostBuffer(bytes_read);
+ if (!result) {
+ // Failed to transfer data to UI thread.
+ return;
+ }
+ } while (url_request_->Read(buffer_.get(), kBufferSize, &bytes_read));
+
+ const auto status = url_request_->status();
+
+ if (!status.is_io_pending() /* TODO || request_type_ == URLFetcher::HEAD*/ ) {
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI, FROM_HERE,
+ base::Bind(&AtomURLRequest::InformDelegateResponseCompleted, this));
+ }
+
}
-void AtomURLRequest::OnResponseStarted(net::URLRequest* request)
-{
- // post to main thread
- content::BrowserThread::PostTask(
+
+bool AtomURLRequest::CopyAndPostBuffer(int bytes_read) {
+ // Called 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.
+ scoped_refptr<net::IOBufferWithSize> buffer_copy(new net::IOBufferWithSize(bytes_read));
+ memcpy(buffer_copy->data(), buffer_->data(), bytes_read);
+
+ return content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE,
- base::Bind(&AtomURLRequest::InformDelegeteResponseStarted, this));
+ base::Bind(&AtomURLRequest::InformDelegateResponseData, this, buffer_copy));
}
-void AtomURLRequest::OnReadCompleted(net::URLRequest* request, int bytes_read)
-{
- // post to main thread
-}
-void AtomURLRequest::InformDelegeteResponseStarted() {
+void AtomURLRequest::InformDelegateResponseStarted() {
+ // Called on content::BrowserThread::UI.
+
if (delegate_) {
delegate_->OnResponseStarted();
}
}
+void AtomURLRequest::InformDelegateResponseData(scoped_refptr<net::IOBufferWithSize> data) {
+ // Called on content::BrowserThread::IO.
+
+ // Transfer ownership of the data buffer, data will be released
+ // by the delegate's OnResponseData.
+ if (delegate_) {
+ delegate_->OnResponseData(data);
+ }
+}
+
+void AtomURLRequest::InformDelegateResponseCompleted() {
+ if (delegate_) {
+ delegate_->OnResponseCompleted();
+ }
+}
+
} // namespace atom
\ No newline at end of file
#include "base/memory/ref_counted.h"
#include "net/url_request/url_request.h"
+
+namespace net {
+class IOBuffer;
+class IOBufferWithSize;
+class DrainableIOBuffer;
+}
+
namespace atom {
class AtomBrowserContext;
void GetHeader();
void RemoveHeader();
- int StatusCode();
- void StatusMessage();
- void ResponseHeaders();
- void ResponseHttpVersion();
+ scoped_refptr<net::HttpResponseHeaders> GetResponseHeaders();
protected:
// Overrides of net::URLRequest::Delegate
private:
friend class base::RefCountedThreadSafe<AtomURLRequest>;
void StartOnIOThread();
- void InformDelegeteResponseStarted();
+
+ void ReadResponse();
+ bool CopyAndPostBuffer(int bytes_read);
+
+ void InformDelegateResponseStarted();
+ void InformDelegateResponseData(scoped_refptr<net::IOBufferWithSize> data);
+ void InformDelegateResponseCompleted();
AtomURLRequest(base::WeakPtr<api::URLRequest> delegate);
virtual ~AtomURLRequest();
base::WeakPtr<api::URLRequest> delegate_;
std::unique_ptr<net::URLRequest> url_request_;
+ scoped_refptr<net::IOBuffer> buffer_;
+ scoped_refptr<net::HttpResponseHeaders> response_headers_;
DISALLOW_COPY_AND_ASSIGN(AtomURLRequest);
};
Object.setPrototypeOf(URLRequest.prototype, EventEmitter.prototype)
class URLResponse extends EventEmitter {
- constructor(request) {
- super();
- this.request = request;
- }
+ constructor(request) {
+ super();
+ this.request = request;
+ }
- get statusCode() {
- return this.request.statusCode;
- }
+ get statusCode() {
+ return this.request.statusCode;
+ }
- get statusMessage() {
- return this.request.statusMessage;
- }
+ get statusMessage() {
+ return this.request.statusMessage;
+ }
- get headers() {
- return this.request.responseHeaders;
- }
+ get headers() {
+ return this.request.rawResponseHeaders;
+ }
- get httpVersion() {
- return this.request.responseHttpVersion;
- }
+ get httpVersion() {
+ return `${this.httpVersionMajor}.${this.httpVersionMinor}`;
+ }
+ get httpVersionMajor() {
+ return this.request.httpVersionMajor;
+ }
+ get httpVersionMinor() {
+ return this.request.httpVersionMinor;
+ }
+
+ get rawHeaders() {
+ return this.request.rawResponseHeaders;
+ }
}
Net.prototype.request = function(options, callback) {
- let request = new URLRequest(options)
+ let request = new URLRequest(options)
- if (callback) {
- request.once('response', callback)
- }
+ if (callback) {
+ request.once('response', callback)
+ }
- return request
+ return request
}
-URLRequest.prototype._emitResponse = function() {
+URLRequest.prototype._emitRequestEvent = function(name) {
+ if (name === 'response') {
this.response = new URLResponse(this);
- this.emit('response', this.response);
+ this.emit(name, this.response);
+ } else {
+ this.emit.apply(this, arguments);
+ }
+}
+
+URLRequest.prototype._emitResponseEvent = function() {
+ this.response.emit.apply(this.response, arguments);
}
+
module.exports = net
\ No newline at end of file