2016-09-19 09:21:09 +00:00
|
|
|
// Copyright (c) 2013 GitHub, Inc.
|
|
|
|
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by the MIT license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
|
|
|
#include "atom/browser/net/atom_url_request.h"
|
|
|
|
#include "atom/browser/api/atom_api_url_request.h"
|
|
|
|
#include "atom/browser/atom_browser_context.h"
|
|
|
|
#include "base/callback.h"
|
|
|
|
#include "content/public/browser/browser_thread.h"
|
2016-09-21 07:23:00 +00:00
|
|
|
#include "net/base/io_buffer.h"
|
2016-09-26 12:03:49 +00:00
|
|
|
#include "net/base/elements_upload_data_stream.h"
|
|
|
|
#include "net/base/upload_bytes_element_reader.h"
|
2016-09-21 07:23:00 +00:00
|
|
|
|
2016-09-21 15:35:03 +00:00
|
|
|
|
2016-09-21 07:23:00 +00:00
|
|
|
namespace {
|
|
|
|
const int kBufferSize = 4096;
|
|
|
|
} // namespace
|
2016-09-19 09:21:09 +00:00
|
|
|
|
|
|
|
namespace atom {
|
|
|
|
|
2016-09-26 12:03:49 +00:00
|
|
|
namespace internal {
|
|
|
|
|
|
|
|
|
|
|
|
class UploadOwnedIOBufferElementReader : public net::UploadBytesElementReader {
|
|
|
|
public:
|
|
|
|
explicit UploadOwnedIOBufferElementReader(
|
|
|
|
scoped_refptr<const net::IOBufferWithSize> buffer)
|
|
|
|
: net::UploadBytesElementReader(buffer->data(), buffer->size())
|
|
|
|
, buffer_(buffer) {
|
|
|
|
}
|
2016-09-26 12:59:53 +00:00
|
|
|
|
2016-09-26 12:03:49 +00:00
|
|
|
~UploadOwnedIOBufferElementReader() override {
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static UploadOwnedIOBufferElementReader* CreateWithBuffer(
|
|
|
|
scoped_refptr<const net::IOBufferWithSize> buffer) {
|
|
|
|
return new UploadOwnedIOBufferElementReader(std::move(buffer));
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
scoped_refptr<const net::IOBuffer> buffer_;
|
|
|
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(UploadOwnedIOBufferElementReader);
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-09-21 15:35:03 +00:00
|
|
|
AtomURLRequest::AtomURLRequest(base::WeakPtr<api::URLRequest> delegate)
|
2016-09-21 07:23:00 +00:00
|
|
|
: delegate_(delegate)
|
2016-09-26 12:03:49 +00:00
|
|
|
, response_read_buffer_(new net::IOBuffer(kBufferSize))
|
|
|
|
, is_chunked_upload_(is_chunked_upload_) {
|
2016-09-19 09:21:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
AtomURLRequest::~AtomURLRequest() {
|
|
|
|
}
|
|
|
|
|
2016-09-21 15:35:03 +00:00
|
|
|
scoped_refptr<AtomURLRequest> AtomURLRequest::Create(
|
2016-09-26 12:59:53 +00:00
|
|
|
AtomBrowserContext* browser_context,
|
|
|
|
const std::string& method,
|
|
|
|
const std::string& url,
|
|
|
|
base::WeakPtr<api::URLRequest> delegate) {
|
2016-09-21 15:35:03 +00:00
|
|
|
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
2016-09-19 13:06:13 +00:00
|
|
|
DCHECK(browser_context);
|
|
|
|
DCHECK(!url.empty());
|
2016-09-19 09:21:09 +00:00
|
|
|
|
2016-09-19 13:06:13 +00:00
|
|
|
auto request_context_getter = browser_context->url_request_context_getter();
|
2016-09-19 09:21:09 +00:00
|
|
|
|
2016-09-19 13:06:13 +00:00
|
|
|
DCHECK(request_context_getter);
|
2016-09-19 09:21:09 +00:00
|
|
|
|
2016-09-19 13:06:13 +00:00
|
|
|
auto context = request_context_getter->GetURLRequestContext();
|
2016-09-19 09:21:09 +00:00
|
|
|
|
2016-09-19 13:06:13 +00:00
|
|
|
DCHECK(context);
|
|
|
|
|
2016-09-26 12:59:53 +00:00
|
|
|
scoped_refptr<AtomURLRequest> atom_url_request =
|
|
|
|
new AtomURLRequest(delegate);
|
2016-09-19 09:21:09 +00:00
|
|
|
|
2016-09-21 15:35:03 +00:00
|
|
|
atom_url_request->request_ = context->CreateRequest(GURL(url),
|
2016-09-19 13:06:13 +00:00
|
|
|
net::RequestPriority::DEFAULT_PRIORITY,
|
|
|
|
atom_url_request.get());
|
2016-09-19 09:21:09 +00:00
|
|
|
|
2016-09-21 15:35:03 +00:00
|
|
|
atom_url_request->request_->set_method(method);
|
2016-09-19 09:21:09 +00:00
|
|
|
return atom_url_request;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-09-19 13:06:13 +00:00
|
|
|
|
2016-09-26 12:59:53 +00:00
|
|
|
bool AtomURLRequest::WriteBuffer(
|
|
|
|
scoped_refptr<const net::IOBufferWithSize> buffer,
|
|
|
|
bool is_last) {
|
2016-09-26 12:03:49 +00:00
|
|
|
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
2016-09-26 12:59:53 +00:00
|
|
|
return content::BrowserThread::PostTask(
|
|
|
|
content::BrowserThread::IO, FROM_HERE,
|
|
|
|
base::Bind(&AtomURLRequest::DoWriteBuffer, this, buffer, is_last));
|
2016-09-19 09:21:09 +00:00
|
|
|
}
|
|
|
|
|
2016-09-26 12:03:49 +00:00
|
|
|
void AtomURLRequest::SetChunkedUpload() {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-09-21 15:35:03 +00:00
|
|
|
void AtomURLRequest::Abort() const {
|
|
|
|
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
2016-09-19 13:06:13 +00:00
|
|
|
}
|
|
|
|
|
2016-09-21 15:35:03 +00:00
|
|
|
void AtomURLRequest::SetHeader(const std::string& name,
|
|
|
|
const std::string& value) const {
|
|
|
|
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
|
|
|
request_->SetExtraRequestHeaderByName(name, value, true);
|
2016-09-19 13:06:13 +00:00
|
|
|
}
|
|
|
|
|
2016-09-21 15:35:03 +00:00
|
|
|
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;
|
|
|
|
}
|
2016-09-19 13:06:13 +00:00
|
|
|
|
2016-09-21 15:35:03 +00:00
|
|
|
void AtomURLRequest::RemoveHeader(const std::string& name) const {
|
|
|
|
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
|
|
|
request_->RemoveRequestHeaderByName(name);
|
2016-09-19 13:06:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-09-21 15:35:03 +00:00
|
|
|
scoped_refptr<const net::HttpResponseHeaders>
|
|
|
|
AtomURLRequest::GetResponseHeaders() const {
|
|
|
|
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
|
|
|
return request_->response_headers();
|
|
|
|
}
|
2016-09-19 13:06:13 +00:00
|
|
|
|
|
|
|
|
2016-09-21 15:35:03 +00:00
|
|
|
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));
|
|
|
|
}
|
2016-09-21 07:23:00 +00:00
|
|
|
}
|
2016-09-19 13:06:13 +00:00
|
|
|
|
|
|
|
|
2016-09-26 12:59:53 +00:00
|
|
|
void AtomURLRequest::DoWriteBuffer(
|
|
|
|
scoped_refptr<const net::IOBufferWithSize> buffer,
|
|
|
|
bool is_last) {
|
2016-09-21 15:35:03 +00:00
|
|
|
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
2016-09-19 13:06:13 +00:00
|
|
|
|
2016-09-26 12:03:49 +00:00
|
|
|
if (is_chunked_upload_) {
|
|
|
|
|
|
|
|
// Chunked encoding case.
|
|
|
|
|
|
|
|
bool first_call = false;
|
|
|
|
if (!chunked_stream_writer_) {
|
|
|
|
std::unique_ptr<net::ChunkedUploadDataStream> chunked_stream(
|
|
|
|
new net::ChunkedUploadDataStream(0));
|
|
|
|
chunked_stream_writer_ = chunked_stream->CreateWriter();
|
|
|
|
request_->set_upload(std::move(chunked_stream));
|
|
|
|
first_call = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buffer) {
|
|
|
|
// Non-empty buffer.
|
|
|
|
auto write_result = chunked_stream_writer_->AppendData(
|
|
|
|
buffer->data(),
|
|
|
|
buffer->size(),
|
|
|
|
is_last);
|
|
|
|
}
|
|
|
|
else if (is_last) {
|
|
|
|
// Empty buffer and last chunck, i.e. request.end().
|
|
|
|
auto write_result = chunked_stream_writer_->AppendData(
|
|
|
|
nullptr,
|
|
|
|
0,
|
|
|
|
true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (first_call) {
|
|
|
|
request_->Start();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
|
|
|
if (buffer) {
|
|
|
|
// Handling potential empty buffers.
|
2016-09-26 12:59:53 +00:00
|
|
|
using internal::UploadOwnedIOBufferElementReader;
|
|
|
|
auto element_reader = UploadOwnedIOBufferElementReader::CreateWithBuffer(
|
|
|
|
std::move(buffer));
|
|
|
|
upload_element_readers_.push_back(
|
|
|
|
std::unique_ptr<net::UploadElementReader>(element_reader));
|
2016-09-26 12:03:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (is_last) {
|
2016-09-26 12:59:53 +00:00
|
|
|
auto elements_upload_data_stream = new net::ElementsUploadDataStream(
|
|
|
|
std::move(upload_element_readers_),
|
|
|
|
0);
|
|
|
|
request_->set_upload(
|
|
|
|
std::unique_ptr<net::UploadDataStream>(elements_upload_data_stream));
|
2016-09-26 12:03:49 +00:00
|
|
|
request_->Start();
|
|
|
|
}
|
|
|
|
}
|
2016-09-21 15:35:03 +00:00
|
|
|
}
|
2016-09-19 13:06:13 +00:00
|
|
|
|
2016-09-21 15:35:03 +00:00
|
|
|
void AtomURLRequest::DoSetAuth(const base::string16& username,
|
|
|
|
const base::string16& password) const {
|
|
|
|
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
|
|
|
request_->SetAuth(net::AuthCredentials(username, password));
|
2016-09-19 13:06:13 +00:00
|
|
|
}
|
2016-09-21 07:23:00 +00:00
|
|
|
|
2016-09-21 15:35:03 +00:00
|
|
|
void AtomURLRequest::DoCancelAuth() const {
|
|
|
|
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
|
|
|
request_->CancelAuth();
|
|
|
|
}
|
2016-09-21 07:23:00 +00:00
|
|
|
|
2016-09-21 15:35:03 +00:00
|
|
|
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<net::AuthChallengeInfo>(auth_info)));
|
2016-09-19 13:06:13 +00:00
|
|
|
}
|
|
|
|
|
2016-09-21 07:23:00 +00:00
|
|
|
void AtomURLRequest::OnResponseStarted(net::URLRequest* request) {
|
2016-09-21 15:35:03 +00:00
|
|
|
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
|
|
|
DCHECK_EQ(request, request_.get());
|
2016-09-21 07:23:00 +00:00
|
|
|
|
2016-09-21 15:35:03 +00:00
|
|
|
if (request_->status().is_success()) {
|
2016-09-21 07:23:00 +00:00
|
|
|
// 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.
|
2016-09-21 15:35:03 +00:00
|
|
|
response_headers_ = request_->response_headers();
|
2016-09-21 07:23:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
content::BrowserThread::PostTask(
|
|
|
|
content::BrowserThread::UI, FROM_HERE,
|
|
|
|
base::Bind(&AtomURLRequest::InformDelegateResponseStarted, this));
|
|
|
|
|
|
|
|
ReadResponse();
|
2016-09-19 13:06:13 +00:00
|
|
|
}
|
|
|
|
|
2016-09-21 07:23:00 +00:00
|
|
|
void AtomURLRequest::ReadResponse() {
|
2016-09-21 15:35:03 +00:00
|
|
|
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
2016-09-19 13:06:13 +00:00
|
|
|
|
2016-09-21 07:23:00 +00:00
|
|
|
// 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;
|
2016-09-26 12:59:53 +00:00
|
|
|
if (request_->status().is_success()
|
|
|
|
/* TODO && (request_type_ != URLFetcher::HEAD)*/) {
|
2016-09-26 12:03:49 +00:00
|
|
|
if (!request_->Read(response_read_buffer_.get(), kBufferSize, &bytes_read))
|
2016-09-21 07:23:00 +00:00
|
|
|
bytes_read = -1;
|
|
|
|
}
|
2016-09-21 15:35:03 +00:00
|
|
|
OnReadCompleted(request_.get(), bytes_read);
|
2016-09-19 09:21:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-09-26 12:59:53 +00:00
|
|
|
void AtomURLRequest::OnReadCompleted(net::URLRequest* request,
|
|
|
|
int bytes_read) {
|
2016-09-21 15:35:03 +00:00
|
|
|
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
2016-09-21 07:23:00 +00:00
|
|
|
|
2016-09-21 15:35:03 +00:00
|
|
|
DCHECK_EQ(request, request_.get());
|
2016-09-21 07:23:00 +00:00
|
|
|
|
|
|
|
do {
|
2016-09-21 15:35:03 +00:00
|
|
|
if (!request_->status().is_success() || bytes_read <= 0)
|
2016-09-21 07:23:00 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
const auto result = CopyAndPostBuffer(bytes_read);
|
|
|
|
if (!result) {
|
|
|
|
// Failed to transfer data to UI thread.
|
|
|
|
return;
|
|
|
|
}
|
2016-09-26 12:59:53 +00:00
|
|
|
} while (request_->Read(response_read_buffer_.get(),
|
|
|
|
kBufferSize,
|
|
|
|
&bytes_read));
|
2016-09-21 07:23:00 +00:00
|
|
|
|
2016-09-21 15:35:03 +00:00
|
|
|
const auto status = request_->status();
|
2016-09-21 07:23:00 +00:00
|
|
|
|
2016-09-26 12:59:53 +00:00
|
|
|
if (!status.is_io_pending()
|
|
|
|
/* TODO || request_type_ == URLFetcher::HEAD*/ ) {
|
2016-09-21 07:23:00 +00:00
|
|
|
|
|
|
|
content::BrowserThread::PostTask(
|
|
|
|
content::BrowserThread::UI, FROM_HERE,
|
|
|
|
base::Bind(&AtomURLRequest::InformDelegateResponseCompleted, this));
|
|
|
|
}
|
|
|
|
|
2016-09-19 09:21:09 +00:00
|
|
|
}
|
|
|
|
|
2016-09-21 07:23:00 +00:00
|
|
|
bool AtomURLRequest::CopyAndPostBuffer(int bytes_read) {
|
2016-09-21 15:35:03 +00:00
|
|
|
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
2016-09-21 07:23:00 +00:00
|
|
|
|
2016-09-26 12:03:49 +00:00
|
|
|
// data is only a wrapper for the async response_read_buffer_.
|
2016-09-21 07:23:00 +00:00
|
|
|
// Make a deep copy of payload and transfer ownership to the UI thread.
|
2016-09-26 12:59:53 +00:00
|
|
|
auto buffer_copy = new net::IOBufferWithSize(bytes_read);
|
2016-09-26 12:03:49 +00:00
|
|
|
memcpy(buffer_copy->data(), response_read_buffer_->data(), bytes_read);
|
2016-09-21 07:23:00 +00:00
|
|
|
|
|
|
|
return content::BrowserThread::PostTask(
|
2016-09-19 09:21:09 +00:00
|
|
|
content::BrowserThread::UI, FROM_HERE,
|
2016-09-26 12:59:53 +00:00
|
|
|
base::Bind(&AtomURLRequest::InformDelegateResponseData,
|
|
|
|
this,
|
|
|
|
buffer_copy));
|
2016-09-19 09:21:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-09-21 15:35:03 +00:00
|
|
|
void AtomURLRequest::InformDelegateAuthenticationRequired(
|
|
|
|
scoped_refptr<net::AuthChallengeInfo> 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);
|
2016-09-21 07:23:00 +00:00
|
|
|
|
2016-09-19 09:21:09 +00:00
|
|
|
if (delegate_) {
|
|
|
|
delegate_->OnResponseStarted();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-21 15:35:03 +00:00
|
|
|
void AtomURLRequest::InformDelegateResponseData(
|
|
|
|
scoped_refptr<net::IOBufferWithSize> data) const {
|
|
|
|
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
2016-09-21 07:23:00 +00:00
|
|
|
|
|
|
|
// Transfer ownership of the data buffer, data will be released
|
|
|
|
// by the delegate's OnResponseData.
|
|
|
|
if (delegate_) {
|
|
|
|
delegate_->OnResponseData(data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-21 15:35:03 +00:00
|
|
|
void AtomURLRequest::InformDelegateResponseCompleted() const {
|
|
|
|
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
|
|
|
|
2016-09-21 07:23:00 +00:00
|
|
|
if (delegate_) {
|
|
|
|
delegate_->OnResponseCompleted();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-19 09:21:09 +00:00
|
|
|
|
|
|
|
} // namespace atom
|