Implementing abort workflow, emitting error events.
This commit is contained in:
parent
cbbc4376ca
commit
08947682b0
5 changed files with 84 additions and 46 deletions
|
@ -135,7 +135,7 @@ void URLRequest::BuildPrototype(v8::Isolate* isolate,
|
||||||
mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
|
mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
|
||||||
// Request API
|
// Request API
|
||||||
.MakeDestroyable()
|
.MakeDestroyable()
|
||||||
.SetMethod("writeBuffer", &URLRequest::Write)
|
.SetMethod("write", &URLRequest::Write)
|
||||||
.SetMethod("abort", &URLRequest::Abort)
|
.SetMethod("abort", &URLRequest::Abort)
|
||||||
.SetMethod("setExtraHeader", &URLRequest::SetExtraHeader)
|
.SetMethod("setExtraHeader", &URLRequest::SetExtraHeader)
|
||||||
.SetMethod("removeExtraHeader", &URLRequest::RemoveExtraHeader)
|
.SetMethod("removeExtraHeader", &URLRequest::RemoveExtraHeader)
|
||||||
|
@ -212,6 +212,10 @@ void URLRequest::OnResponseCompleted() {
|
||||||
atom_request_ = nullptr;
|
atom_request_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void URLRequest::OnError(const std::string& error) {
|
||||||
|
auto error_object = v8::Exception::Error(mate::StringToV8(isolate(), error));
|
||||||
|
EmitRequestEvent("error", error_object);
|
||||||
|
}
|
||||||
|
|
||||||
int URLRequest::StatusCode() const {
|
int URLRequest::StatusCode() const {
|
||||||
if (auto response_headers = atom_request_->GetResponseHeaders()) {
|
if (auto response_headers = atom_request_->GetResponseHeaders()) {
|
||||||
|
|
|
@ -45,6 +45,7 @@ private:
|
||||||
void OnResponseStarted();
|
void OnResponseStarted();
|
||||||
void OnResponseData(scoped_refptr<const net::IOBufferWithSize> data);
|
void OnResponseData(scoped_refptr<const net::IOBufferWithSize> data);
|
||||||
void OnResponseCompleted();
|
void OnResponseCompleted();
|
||||||
|
void OnError(const std::string& error);
|
||||||
|
|
||||||
int StatusCode() const;
|
int StatusCode() const;
|
||||||
std::string StatusMessage() const;
|
std::string StatusMessage() const;
|
||||||
|
|
|
@ -107,6 +107,10 @@ void AtomURLRequest::SetChunkedUpload(bool is_chunked_upload) {
|
||||||
|
|
||||||
void AtomURLRequest::Abort() const {
|
void AtomURLRequest::Abort() const {
|
||||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||||
|
content::BrowserThread::PostTask(
|
||||||
|
content::BrowserThread::IO,
|
||||||
|
FROM_HERE,
|
||||||
|
base::Bind(&AtomURLRequest::DoAbort, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AtomURLRequest::SetExtraHeader(const std::string& name,
|
void AtomURLRequest::SetExtraHeader(const std::string& name,
|
||||||
|
@ -133,17 +137,15 @@ AtomURLRequest::GetResponseHeaders() const {
|
||||||
void AtomURLRequest::PassLoginInformation(const base::string16& username,
|
void AtomURLRequest::PassLoginInformation(const base::string16& username,
|
||||||
const base::string16& password) const {
|
const base::string16& password) const {
|
||||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||||
if (username.empty() || password.empty()) {
|
if (username.empty() || password.empty())
|
||||||
content::BrowserThread::PostTask(
|
content::BrowserThread::PostTask(
|
||||||
content::BrowserThread::IO, FROM_HERE,
|
content::BrowserThread::IO, FROM_HERE,
|
||||||
base::Bind(&AtomURLRequest::DoCancelAuth, this));
|
base::Bind(&AtomURLRequest::DoCancelAuth, this));
|
||||||
}
|
else
|
||||||
else {
|
|
||||||
content::BrowserThread::PostTask(
|
content::BrowserThread::PostTask(
|
||||||
content::BrowserThread::IO, FROM_HERE,
|
content::BrowserThread::IO, FROM_HERE,
|
||||||
base::Bind(&AtomURLRequest::DoSetAuth, this, username, password));
|
base::Bind(&AtomURLRequest::DoSetAuth, this, username, password));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void AtomURLRequest::DoWriteBuffer(
|
void AtomURLRequest::DoWriteBuffer(
|
||||||
|
@ -164,25 +166,22 @@ void AtomURLRequest::DoWriteBuffer(
|
||||||
first_call = true;
|
first_call = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buffer) {
|
if (buffer)
|
||||||
// Non-empty buffer.
|
// Non-empty buffer.
|
||||||
auto write_result = chunked_stream_writer_->AppendData(
|
auto write_result = chunked_stream_writer_->AppendData(
|
||||||
buffer->data(),
|
buffer->data(),
|
||||||
buffer->size(),
|
buffer->size(),
|
||||||
is_last);
|
is_last);
|
||||||
}
|
else if (is_last)
|
||||||
else if (is_last) {
|
|
||||||
// Empty buffer and last chunck, i.e. request.end().
|
// Empty buffer and last chunck, i.e. request.end().
|
||||||
auto write_result = chunked_stream_writer_->AppendData(
|
auto write_result = chunked_stream_writer_->AppendData(
|
||||||
nullptr,
|
nullptr,
|
||||||
0,
|
0,
|
||||||
true);
|
true);
|
||||||
}
|
|
||||||
|
|
||||||
if (first_call) {
|
if (first_call)
|
||||||
request_->Start();
|
request_->Start();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
|
|
||||||
if (buffer) {
|
if (buffer) {
|
||||||
|
@ -205,6 +204,11 @@ void AtomURLRequest::DoWriteBuffer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AtomURLRequest::DoAbort() const {
|
||||||
|
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||||
|
request_->Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
void AtomURLRequest::DoSetAuth(const base::string16& username,
|
void AtomURLRequest::DoSetAuth(const base::string16& username,
|
||||||
const base::string16& password) const {
|
const base::string16& password) const {
|
||||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||||
|
@ -231,20 +235,26 @@ void AtomURLRequest::OnResponseStarted(net::URLRequest* request) {
|
||||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||||
DCHECK_EQ(request, request_.get());
|
DCHECK_EQ(request, request_.get());
|
||||||
|
|
||||||
if (request_->status().is_success()) {
|
const auto& status = request_->status();
|
||||||
|
if (status.is_success()) {
|
||||||
// Cache net::HttpResponseHeaders instance, a read-only objects
|
// Cache net::HttpResponseHeaders instance, a read-only objects
|
||||||
// so that headers and other http metainformation can be simultaneously
|
// so that headers and other http metainformation can be simultaneously
|
||||||
// read from UI thread while request data is simulataneously streaming
|
// read from UI thread while request data is simulataneously streaming
|
||||||
// on IO thread.
|
// on IO thread.
|
||||||
response_headers_ = request_->response_headers();
|
response_headers_ = request_->response_headers();
|
||||||
}
|
|
||||||
|
|
||||||
content::BrowserThread::PostTask(
|
content::BrowserThread::PostTask(
|
||||||
content::BrowserThread::UI, FROM_HERE,
|
content::BrowserThread::UI, FROM_HERE,
|
||||||
base::Bind(&AtomURLRequest::InformDelegateResponseStarted, this));
|
base::Bind(&AtomURLRequest::InformDelegateResponseStarted, this));
|
||||||
|
|
||||||
ReadResponse();
|
ReadResponse();
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
auto error = net::ErrorToString(status.ToNetError());
|
||||||
|
content::BrowserThread::PostTask(
|
||||||
|
content::BrowserThread::UI, FROM_HERE,
|
||||||
|
base::Bind(&AtomURLRequest::InformDelegateErrorOccured, this, std::move(error)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AtomURLRequest::ReadResponse() {
|
void AtomURLRequest::ReadResponse() {
|
||||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||||
|
@ -255,10 +265,9 @@ void AtomURLRequest::ReadResponse() {
|
||||||
// about is the response code and headers, which we already have).
|
// about is the response code and headers, which we already have).
|
||||||
int bytes_read = 0;
|
int bytes_read = 0;
|
||||||
if (request_->status().is_success()
|
if (request_->status().is_success()
|
||||||
/* TODO && (request_type_ != URLFetcher::HEAD)*/) {
|
/* TODO && (request_type_ != URLFetcher::HEAD)*/)
|
||||||
if (!request_->Read(response_read_buffer_.get(), kBufferSize, &bytes_read))
|
if (!request_->Read(response_read_buffer_.get(), kBufferSize, &bytes_read))
|
||||||
bytes_read = -1;
|
bytes_read = -1;
|
||||||
}
|
|
||||||
OnReadCompleted(request_.get(), bytes_read);
|
OnReadCompleted(request_.get(), bytes_read);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,29 +278,31 @@ void AtomURLRequest::OnReadCompleted(net::URLRequest* request,
|
||||||
|
|
||||||
DCHECK_EQ(request, request_.get());
|
DCHECK_EQ(request, request_.get());
|
||||||
|
|
||||||
|
const auto status = request_->status();
|
||||||
do {
|
do {
|
||||||
if (!request_->status().is_success() || bytes_read <= 0)
|
if (!status.is_success() || bytes_read <= 0) {
|
||||||
|
auto error = net::ErrorToString(status.ToNetError());
|
||||||
|
content::BrowserThread::PostTask(
|
||||||
|
content::BrowserThread::UI, FROM_HERE,
|
||||||
|
base::Bind(&AtomURLRequest::InformDelegateErrorOccured, this, std::move(error)));
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
const auto result = CopyAndPostBuffer(bytes_read);
|
const auto result = CopyAndPostBuffer(bytes_read);
|
||||||
if (!result) {
|
if (!result)
|
||||||
// Failed to transfer data to UI thread.
|
// Failed to transfer data to UI thread.
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
} while (request_->Read(response_read_buffer_.get(),
|
} while (request_->Read(response_read_buffer_.get(),
|
||||||
kBufferSize,
|
kBufferSize,
|
||||||
&bytes_read));
|
&bytes_read));
|
||||||
|
|
||||||
const auto status = request_->status();
|
|
||||||
|
|
||||||
if (!status.is_io_pending()
|
if (!status.is_io_pending()
|
||||||
/* TODO || request_type_ == URLFetcher::HEAD*/ ) {
|
/* TODO || request_type_ == URLFetcher::HEAD*/ )
|
||||||
|
|
||||||
content::BrowserThread::PostTask(
|
content::BrowserThread::PostTask(
|
||||||
content::BrowserThread::UI, FROM_HERE,
|
content::BrowserThread::UI, FROM_HERE,
|
||||||
base::Bind(&AtomURLRequest::InformDelegateResponseCompleted, this));
|
base::Bind(&AtomURLRequest::InformDelegateResponseCompleted, this));
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,18 +325,15 @@ bool AtomURLRequest::CopyAndPostBuffer(int bytes_read) {
|
||||||
void AtomURLRequest::InformDelegateAuthenticationRequired(
|
void AtomURLRequest::InformDelegateAuthenticationRequired(
|
||||||
scoped_refptr<net::AuthChallengeInfo> auth_info) const {
|
scoped_refptr<net::AuthChallengeInfo> auth_info) const {
|
||||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||||
if (delegate_) {
|
if (delegate_)
|
||||||
delegate_->OnAuthenticationRequired(auth_info);
|
delegate_->OnAuthenticationRequired(auth_info);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void AtomURLRequest::InformDelegateResponseStarted() const {
|
void AtomURLRequest::InformDelegateResponseStarted() const {
|
||||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||||
|
if (delegate_)
|
||||||
if (delegate_) {
|
|
||||||
delegate_->OnResponseStarted();
|
delegate_->OnResponseStarted();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void AtomURLRequest::InformDelegateResponseData(
|
void AtomURLRequest::InformDelegateResponseData(
|
||||||
scoped_refptr<net::IOBufferWithSize> data) const {
|
scoped_refptr<net::IOBufferWithSize> data) const {
|
||||||
|
@ -333,17 +341,23 @@ void AtomURLRequest::InformDelegateResponseData(
|
||||||
|
|
||||||
// Transfer ownership of the data buffer, data will be released
|
// Transfer ownership of the data buffer, data will be released
|
||||||
// by the delegate's OnResponseData.
|
// by the delegate's OnResponseData.
|
||||||
if (delegate_) {
|
if (delegate_)
|
||||||
delegate_->OnResponseData(data);
|
delegate_->OnResponseData(data);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void AtomURLRequest::InformDelegateResponseCompleted() const {
|
void AtomURLRequest::InformDelegateResponseCompleted() const {
|
||||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||||
|
|
||||||
if (delegate_) {
|
if (delegate_)
|
||||||
delegate_->OnResponseCompleted();
|
delegate_->OnResponseCompleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AtomURLRequest::InformDelegateErrorOccured(
|
||||||
|
const std::string& error) const {
|
||||||
|
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||||
|
|
||||||
|
if (delegate_)
|
||||||
|
delegate_->OnError(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,7 @@ private:
|
||||||
friend class base::RefCountedThreadSafe<AtomURLRequest>;
|
friend class base::RefCountedThreadSafe<AtomURLRequest>;
|
||||||
void DoWriteBuffer(scoped_refptr<const net::IOBufferWithSize> buffer,
|
void DoWriteBuffer(scoped_refptr<const net::IOBufferWithSize> buffer,
|
||||||
bool is_last);
|
bool is_last);
|
||||||
|
void DoAbort() const;
|
||||||
void DoSetAuth(const base::string16& username,
|
void DoSetAuth(const base::string16& username,
|
||||||
const base::string16& password) const;
|
const base::string16& password) const;
|
||||||
void DoCancelAuth() const;
|
void DoCancelAuth() const;
|
||||||
|
@ -69,6 +70,7 @@ private:
|
||||||
void InformDelegateResponseData(
|
void InformDelegateResponseData(
|
||||||
scoped_refptr<net::IOBufferWithSize> data) const;
|
scoped_refptr<net::IOBufferWithSize> data) const;
|
||||||
void InformDelegateResponseCompleted() const;
|
void InformDelegateResponseCompleted() const;
|
||||||
|
void InformDelegateErrorOccured(const std::string& error) const;
|
||||||
|
|
||||||
AtomURLRequest(base::WeakPtr<api::URLRequest> delegate);
|
AtomURLRequest(base::WeakPtr<api::URLRequest> delegate);
|
||||||
virtual ~AtomURLRequest();
|
virtual ~AtomURLRequest();
|
||||||
|
|
|
@ -130,6 +130,12 @@ class ClientRequest extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flag to prevent request's headers modifications after
|
||||||
|
// headers flush.
|
||||||
|
this._started = false;
|
||||||
|
|
||||||
|
this._aborted = false;
|
||||||
|
|
||||||
// Flag to prevent writings after end.
|
// Flag to prevent writings after end.
|
||||||
this._finished = false;
|
this._finished = false;
|
||||||
|
|
||||||
|
@ -137,10 +143,6 @@ class ClientRequest extends EventEmitter {
|
||||||
// to true only once and never set back to false.
|
// to true only once and never set back to false.
|
||||||
this._chunkedEncoding = 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
|
// 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
|
// net::URLRequest. The main reason is to keep the getHeader API synchronous
|
||||||
// after the request starts.
|
// after the request starts.
|
||||||
|
@ -162,7 +164,7 @@ class ClientRequest extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
set chunkedEncoding(value) {
|
set chunkedEncoding(value) {
|
||||||
if (this._headersSent) {
|
if (this._started) {
|
||||||
throw new Error('Can\'t set the transfer encoding, headers have been sent.');
|
throw new Error('Can\'t set the transfer encoding, headers have been sent.');
|
||||||
}
|
}
|
||||||
this._chunkedEncoding = value;
|
this._chunkedEncoding = value;
|
||||||
|
@ -174,7 +176,7 @@ class ClientRequest extends EventEmitter {
|
||||||
throw new TypeError('`name` should be a string in setHeader(name, value).');
|
throw new TypeError('`name` should be a string in setHeader(name, value).');
|
||||||
if (value === undefined)
|
if (value === undefined)
|
||||||
throw new Error('`value` required in setHeader("' + name + '", value).');
|
throw new Error('`value` required in setHeader("' + name + '", value).');
|
||||||
if (this._headersSent)
|
if (this._started)
|
||||||
throw new Error('Can\'t set headers after they are sent.');
|
throw new Error('Can\'t set headers after they are sent.');
|
||||||
|
|
||||||
let key = name.toLowerCase();
|
let key = name.toLowerCase();
|
||||||
|
@ -202,7 +204,7 @@ class ClientRequest extends EventEmitter {
|
||||||
throw new Error('`name` is required for removeHeader(name).');
|
throw new Error('`name` is required for removeHeader(name).');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._headersSent) {
|
if (this._started) {
|
||||||
throw new Error('Can\'t remove headers after they are sent.');
|
throw new Error('Can\'t remove headers after they are sent.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,14 +229,14 @@ class ClientRequest extends EventEmitter {
|
||||||
|
|
||||||
// Headers are assumed to be sent on first call to _writeBuffer,
|
// Headers are assumed to be sent on first call to _writeBuffer,
|
||||||
// i.e. after the first call to write or end.
|
// i.e. after the first call to write or end.
|
||||||
let result = this._url_request.writeBuffer(chunk, is_last);
|
let result = this._url_request.write(chunk, is_last);
|
||||||
|
|
||||||
// Since writing to the network is asynchronous, we conservatively
|
// Since writing to the network is asynchronous, we conservatively
|
||||||
// assume that request headers are written after delivering the first
|
// assume that request headers are written after delivering the first
|
||||||
// buffer to the network IO thread.
|
// buffer to the network IO thread.
|
||||||
if (!this._headersSent) {
|
if (!this._started) {
|
||||||
this._url_request.setChunkedUpload(this.chunkedEncoding);
|
this._url_request.setChunkedUpload(this.chunkedEncoding);
|
||||||
this._headersSent = true;
|
this._started = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The write callback is fired asynchronously to mimic Node.js.
|
// The write callback is fired asynchronously to mimic Node.js.
|
||||||
|
@ -278,6 +280,21 @@ class ClientRequest extends EventEmitter {
|
||||||
return this._write(data, encoding, callback, true);
|
return this._write(data, encoding, callback, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abort() {
|
||||||
|
if (!this._started) {
|
||||||
|
// Does nothing if stream
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._aborted) {
|
||||||
|
this._url_request.abort();
|
||||||
|
this._aborted = true;
|
||||||
|
process.nextTick( ()=>{
|
||||||
|
this.emit('abort');
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeAfterEndNT(self, error, callback) {
|
function writeAfterEndNT(self, error, callback) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue