refactor: rename the atom directory to shell
This commit is contained in:
parent
4575a4aae3
commit
d7f07e8a80
631 changed files with 0 additions and 0 deletions
25
shell/browser/net/about_protocol_handler.cc
Normal file
25
shell/browser/net/about_protocol_handler.cc
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) 2016 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/about_protocol_handler.h"
|
||||
|
||||
#include "atom/browser/net/url_request_about_job.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
AboutProtocolHandler::AboutProtocolHandler() {}
|
||||
|
||||
AboutProtocolHandler::~AboutProtocolHandler() {}
|
||||
|
||||
net::URLRequestJob* AboutProtocolHandler::MaybeCreateJob(
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate) const {
|
||||
return new URLRequestAboutJob(request, network_delegate);
|
||||
}
|
||||
|
||||
bool AboutProtocolHandler::IsSafeRedirectTarget(const GURL& location) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace atom
|
29
shell/browser/net/about_protocol_handler.h
Normal file
29
shell/browser/net/about_protocol_handler.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) 2016 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_ABOUT_PROTOCOL_HANDLER_H_
|
||||
#define ATOM_BROWSER_NET_ABOUT_PROTOCOL_HANDLER_H_
|
||||
|
||||
#include "net/url_request/url_request_job_factory.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class AboutProtocolHandler : public net::URLRequestJobFactory::ProtocolHandler {
|
||||
public:
|
||||
AboutProtocolHandler();
|
||||
~AboutProtocolHandler() override;
|
||||
|
||||
// net::URLRequestJobFactory::ProtocolHandler:
|
||||
net::URLRequestJob* MaybeCreateJob(
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate) const override;
|
||||
bool IsSafeRedirectTarget(const GURL& location) const override;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(AboutProtocolHandler);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_ABOUT_PROTOCOL_HANDLER_H_
|
34
shell/browser/net/asar/asar_protocol_handler.cc
Normal file
34
shell/browser/net/asar/asar_protocol_handler.cc
Normal file
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) 2014 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/asar/asar_protocol_handler.h"
|
||||
|
||||
#include "atom/browser/net/asar/url_request_asar_job.h"
|
||||
#include "base/task_runner.h"
|
||||
#include "net/base/filename_util.h"
|
||||
#include "net/base/net_errors.h"
|
||||
|
||||
namespace asar {
|
||||
|
||||
AsarProtocolHandler::AsarProtocolHandler(
|
||||
const scoped_refptr<base::TaskRunner>& file_task_runner)
|
||||
: file_task_runner_(file_task_runner) {}
|
||||
|
||||
AsarProtocolHandler::~AsarProtocolHandler() {}
|
||||
|
||||
net::URLRequestJob* AsarProtocolHandler::MaybeCreateJob(
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate) const {
|
||||
base::FilePath full_path;
|
||||
net::FileURLToFilePath(request->url(), &full_path);
|
||||
auto* job = new URLRequestAsarJob(request, network_delegate);
|
||||
job->Initialize(file_task_runner_, full_path);
|
||||
return job;
|
||||
}
|
||||
|
||||
bool AsarProtocolHandler::IsSafeRedirectTarget(const GURL& location) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace asar
|
37
shell/browser/net/asar/asar_protocol_handler.h
Normal file
37
shell/browser/net/asar/asar_protocol_handler.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) 2014 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_ASAR_ASAR_PROTOCOL_HANDLER_H_
|
||||
#define ATOM_BROWSER_NET_ASAR_ASAR_PROTOCOL_HANDLER_H_
|
||||
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "net/url_request/url_request_job_factory.h"
|
||||
|
||||
namespace base {
|
||||
class TaskRunner;
|
||||
}
|
||||
|
||||
namespace asar {
|
||||
|
||||
class AsarProtocolHandler : public net::URLRequestJobFactory::ProtocolHandler {
|
||||
public:
|
||||
explicit AsarProtocolHandler(
|
||||
const scoped_refptr<base::TaskRunner>& file_task_runner);
|
||||
~AsarProtocolHandler() override;
|
||||
|
||||
// net::URLRequestJobFactory::ProtocolHandler:
|
||||
net::URLRequestJob* MaybeCreateJob(
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate) const override;
|
||||
bool IsSafeRedirectTarget(const GURL& location) const override;
|
||||
|
||||
private:
|
||||
const scoped_refptr<base::TaskRunner> file_task_runner_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AsarProtocolHandler);
|
||||
};
|
||||
|
||||
} // namespace asar
|
||||
|
||||
#endif // ATOM_BROWSER_NET_ASAR_ASAR_PROTOCOL_HANDLER_H_
|
300
shell/browser/net/asar/asar_url_loader.cc
Normal file
300
shell/browser/net/asar/asar_url_loader.cc
Normal file
|
@ -0,0 +1,300 @@
|
|||
// Copyright (c) 2019 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/asar/asar_url_loader.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "atom/common/asar/archive.h"
|
||||
#include "atom/common/asar/asar_util.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/task/post_task.h"
|
||||
#include "content/public/browser/file_url_loader.h"
|
||||
#include "mojo/public/cpp/bindings/strong_binding.h"
|
||||
#include "net/base/filename_util.h"
|
||||
#include "net/base/mime_sniffer.h"
|
||||
#include "net/base/mime_util.h"
|
||||
#include "net/http/http_byte_range.h"
|
||||
#include "net/http/http_util.h"
|
||||
|
||||
namespace asar {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr size_t kDefaultFileUrlPipeSize = 65536;
|
||||
|
||||
// Because this makes things simpler.
|
||||
static_assert(kDefaultFileUrlPipeSize >= net::kMaxBytesToSniff,
|
||||
"Default file data pipe size must be at least as large as a MIME-"
|
||||
"type sniffing buffer.");
|
||||
|
||||
// Modified from the |FileURLLoader| in |file_url_loader_factory.cc|, to serve
|
||||
// asar files instead of normal files.
|
||||
class AsarURLLoader : public network::mojom::URLLoader {
|
||||
public:
|
||||
static void CreateAndStart(
|
||||
const network::ResourceRequest& request,
|
||||
network::mojom::URLLoaderRequest loader,
|
||||
network::mojom::URLLoaderClientPtrInfo client_info,
|
||||
scoped_refptr<net::HttpResponseHeaders> extra_response_headers) {
|
||||
// Owns itself. Will live as long as its URLLoader and URLLoaderClientPtr
|
||||
// bindings are alive - essentially until either the client gives up or all
|
||||
// file data has been sent to it.
|
||||
auto* asar_url_loader = new AsarURLLoader;
|
||||
asar_url_loader->Start(request, std::move(loader), std::move(client_info),
|
||||
std::move(extra_response_headers));
|
||||
}
|
||||
|
||||
// network::mojom::URLLoader:
|
||||
void FollowRedirect(const std::vector<std::string>& removed_headers,
|
||||
const net::HttpRequestHeaders& modified_headers,
|
||||
const base::Optional<GURL>& new_url) override {}
|
||||
void ProceedWithResponse() override {}
|
||||
void SetPriority(net::RequestPriority priority,
|
||||
int32_t intra_priority_value) override {}
|
||||
void PauseReadingBodyFromNet() override {}
|
||||
void ResumeReadingBodyFromNet() override {}
|
||||
|
||||
private:
|
||||
AsarURLLoader() : binding_(this) {}
|
||||
~AsarURLLoader() override = default;
|
||||
|
||||
void Start(const network::ResourceRequest& request,
|
||||
network::mojom::URLLoaderRequest loader,
|
||||
network::mojom::URLLoaderClientPtrInfo client_info,
|
||||
scoped_refptr<net::HttpResponseHeaders> extra_response_headers) {
|
||||
network::ResourceResponseHead head;
|
||||
head.request_start = base::TimeTicks::Now();
|
||||
head.response_start = base::TimeTicks::Now();
|
||||
head.headers = extra_response_headers;
|
||||
binding_.Bind(std::move(loader));
|
||||
binding_.set_connection_error_handler(base::BindOnce(
|
||||
&AsarURLLoader::OnConnectionError, base::Unretained(this)));
|
||||
|
||||
client_.Bind(std::move(client_info));
|
||||
|
||||
base::FilePath path;
|
||||
if (!net::FileURLToFilePath(request.url, &path)) {
|
||||
OnClientComplete(net::ERR_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine whether it is an asar file.
|
||||
base::FilePath asar_path, relative_path;
|
||||
if (!GetAsarArchivePath(path, &asar_path, &relative_path)) {
|
||||
content::CreateFileURLLoader(request, std::move(loader),
|
||||
std::move(client_), nullptr, false,
|
||||
extra_response_headers);
|
||||
MaybeDeleteSelf();
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse asar archive.
|
||||
std::shared_ptr<Archive> archive = GetOrCreateAsarArchive(asar_path);
|
||||
Archive::FileInfo info;
|
||||
if (!archive || !archive->GetFileInfo(relative_path, &info)) {
|
||||
OnClientComplete(net::ERR_FILE_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
// For unpacked path, read like normal file.
|
||||
base::FilePath real_path;
|
||||
if (info.unpacked) {
|
||||
archive->CopyFileOut(relative_path, &real_path);
|
||||
info.offset = 0;
|
||||
}
|
||||
|
||||
mojo::DataPipe pipe(kDefaultFileUrlPipeSize);
|
||||
if (!pipe.consumer_handle.is_valid()) {
|
||||
OnClientComplete(net::ERR_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
// Note that while the |Archive| already opens a |base::File|, we still need
|
||||
// to create a new |base::File| here, as it might be accessed by multiple
|
||||
// requests at the same time.
|
||||
base::File file(info.unpacked ? real_path : archive->path(),
|
||||
base::File::FLAG_OPEN | base::File::FLAG_READ);
|
||||
// Move cursor to sub-file.
|
||||
file.Seek(base::File::FROM_BEGIN, info.offset);
|
||||
|
||||
// File reading logics are copied from FileURLLoader.
|
||||
if (!file.IsValid()) {
|
||||
OnClientComplete(net::FileErrorToNetError(file.error_details()));
|
||||
return;
|
||||
}
|
||||
char initial_read_buffer[net::kMaxBytesToSniff];
|
||||
int initial_read_result =
|
||||
file.ReadAtCurrentPos(initial_read_buffer, net::kMaxBytesToSniff);
|
||||
if (initial_read_result < 0) {
|
||||
base::File::Error read_error = base::File::GetLastFileError();
|
||||
DCHECK_NE(base::File::FILE_OK, read_error);
|
||||
OnClientComplete(net::FileErrorToNetError(read_error));
|
||||
return;
|
||||
}
|
||||
size_t initial_read_size = static_cast<size_t>(initial_read_result);
|
||||
|
||||
std::string range_header;
|
||||
net::HttpByteRange byte_range;
|
||||
if (request.headers.GetHeader(net::HttpRequestHeaders::kRange,
|
||||
&range_header)) {
|
||||
// Handle a simple Range header for a single range.
|
||||
std::vector<net::HttpByteRange> ranges;
|
||||
bool fail = false;
|
||||
if (net::HttpUtil::ParseRangeHeader(range_header, &ranges) &&
|
||||
ranges.size() == 1) {
|
||||
byte_range = ranges[0];
|
||||
if (!byte_range.ComputeBounds(info.size))
|
||||
fail = true;
|
||||
} else {
|
||||
fail = true;
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
OnClientComplete(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
size_t first_byte_to_send = 0;
|
||||
size_t total_bytes_to_send = static_cast<size_t>(info.size);
|
||||
|
||||
if (byte_range.IsValid()) {
|
||||
first_byte_to_send =
|
||||
static_cast<size_t>(byte_range.first_byte_position());
|
||||
total_bytes_to_send =
|
||||
static_cast<size_t>(byte_range.last_byte_position()) -
|
||||
first_byte_to_send + 1;
|
||||
}
|
||||
|
||||
total_bytes_written_ = static_cast<size_t>(total_bytes_to_send);
|
||||
|
||||
head.content_length = base::saturated_cast<int64_t>(total_bytes_to_send);
|
||||
|
||||
if (first_byte_to_send < initial_read_size) {
|
||||
// Write any data we read for MIME sniffing, constraining by range where
|
||||
// applicable. This will always fit in the pipe (see assertion near
|
||||
// |kDefaultFileUrlPipeSize| definition).
|
||||
uint32_t write_size = std::min(
|
||||
static_cast<uint32_t>(initial_read_size - first_byte_to_send),
|
||||
static_cast<uint32_t>(total_bytes_to_send));
|
||||
const uint32_t expected_write_size = write_size;
|
||||
MojoResult result = pipe.producer_handle->WriteData(
|
||||
&initial_read_buffer[first_byte_to_send], &write_size,
|
||||
MOJO_WRITE_DATA_FLAG_NONE);
|
||||
if (result != MOJO_RESULT_OK || write_size != expected_write_size) {
|
||||
OnFileWritten(result);
|
||||
return;
|
||||
}
|
||||
|
||||
// Discount the bytes we just sent from the total range.
|
||||
first_byte_to_send = initial_read_size;
|
||||
total_bytes_to_send -= write_size;
|
||||
}
|
||||
|
||||
if (!net::GetMimeTypeFromFile(path, &head.mime_type)) {
|
||||
std::string new_type;
|
||||
net::SniffMimeType(initial_read_buffer, initial_read_result, request.url,
|
||||
head.mime_type,
|
||||
net::ForceSniffFileUrlsForHtml::kDisabled, &new_type);
|
||||
head.mime_type.assign(new_type);
|
||||
head.did_mime_sniff = true;
|
||||
}
|
||||
if (head.headers) {
|
||||
head.headers->AddHeader(
|
||||
base::StringPrintf("%s: %s", net::HttpRequestHeaders::kContentType,
|
||||
head.mime_type.c_str()));
|
||||
}
|
||||
client_->OnReceiveResponse(head);
|
||||
client_->OnStartLoadingResponseBody(std::move(pipe.consumer_handle));
|
||||
|
||||
if (total_bytes_to_send == 0) {
|
||||
// There's definitely no more data, so we're already done.
|
||||
OnFileWritten(MOJO_RESULT_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
// In case of a range request, seek to the appropriate position before
|
||||
// sending the remaining bytes asynchronously. Under normal conditions
|
||||
// (i.e., no range request) this Seek is effectively a no-op.
|
||||
//
|
||||
// Note that in Electron we also need to add file offset.
|
||||
file.Seek(base::File::FROM_BEGIN,
|
||||
static_cast<int64_t>(first_byte_to_send) + info.offset);
|
||||
|
||||
data_producer_ = std::make_unique<mojo::FileDataPipeProducer>(
|
||||
std::move(pipe.producer_handle), nullptr);
|
||||
data_producer_->WriteFromFile(
|
||||
std::move(file), total_bytes_to_send,
|
||||
base::BindOnce(&AsarURLLoader::OnFileWritten, base::Unretained(this)));
|
||||
}
|
||||
|
||||
void OnConnectionError() {
|
||||
binding_.Close();
|
||||
MaybeDeleteSelf();
|
||||
}
|
||||
|
||||
void OnClientComplete(net::Error net_error) {
|
||||
client_->OnComplete(network::URLLoaderCompletionStatus(net_error));
|
||||
client_.reset();
|
||||
MaybeDeleteSelf();
|
||||
}
|
||||
|
||||
void MaybeDeleteSelf() {
|
||||
if (!binding_.is_bound() && !client_.is_bound())
|
||||
delete this;
|
||||
}
|
||||
|
||||
void OnFileWritten(MojoResult result) {
|
||||
// All the data has been written now. Close the data pipe. The consumer will
|
||||
// be notified that there will be no more data to read from now.
|
||||
data_producer_.reset();
|
||||
|
||||
if (result == MOJO_RESULT_OK) {
|
||||
network::URLLoaderCompletionStatus status(net::OK);
|
||||
status.encoded_data_length = total_bytes_written_;
|
||||
status.encoded_body_length = total_bytes_written_;
|
||||
status.decoded_body_length = total_bytes_written_;
|
||||
client_->OnComplete(status);
|
||||
} else {
|
||||
client_->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED));
|
||||
}
|
||||
client_.reset();
|
||||
MaybeDeleteSelf();
|
||||
}
|
||||
|
||||
std::unique_ptr<mojo::FileDataPipeProducer> data_producer_;
|
||||
mojo::Binding<network::mojom::URLLoader> binding_;
|
||||
network::mojom::URLLoaderClientPtr client_;
|
||||
|
||||
// In case of successful loads, this holds the total number of bytes written
|
||||
// to the response (this may be smaller than the total size of the file when
|
||||
// a byte range was requested).
|
||||
// It is used to set some of the URLLoaderCompletionStatus data passed back
|
||||
// to the URLLoaderClients (eg SimpleURLLoader).
|
||||
size_t total_bytes_written_ = 0;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AsarURLLoader);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void CreateAsarURLLoader(
|
||||
const network::ResourceRequest& request,
|
||||
network::mojom::URLLoaderRequest loader,
|
||||
network::mojom::URLLoaderClientPtr client,
|
||||
scoped_refptr<net::HttpResponseHeaders> extra_response_headers) {
|
||||
auto task_runner = base::CreateSequencedTaskRunnerWithTraits(
|
||||
{base::MayBlock(), base::TaskPriority::USER_VISIBLE,
|
||||
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
|
||||
task_runner->PostTask(
|
||||
FROM_HERE, base::BindOnce(&AsarURLLoader::CreateAndStart, request,
|
||||
std::move(loader), client.PassInterface(),
|
||||
std::move(extra_response_headers)));
|
||||
}
|
||||
|
||||
} // namespace asar
|
20
shell/browser/net/asar/asar_url_loader.h
Normal file
20
shell/browser/net/asar/asar_url_loader.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) 2019 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_ASAR_ASAR_URL_LOADER_H_
|
||||
#define ATOM_BROWSER_NET_ASAR_ASAR_URL_LOADER_H_
|
||||
|
||||
#include "services/network/public/mojom/url_loader.mojom.h"
|
||||
|
||||
namespace asar {
|
||||
|
||||
void CreateAsarURLLoader(
|
||||
const network::ResourceRequest& request,
|
||||
network::mojom::URLLoaderRequest loader,
|
||||
network::mojom::URLLoaderClientPtr client,
|
||||
scoped_refptr<net::HttpResponseHeaders> extra_response_headers);
|
||||
|
||||
} // namespace asar
|
||||
|
||||
#endif // ATOM_BROWSER_NET_ASAR_ASAR_URL_LOADER_H_
|
337
shell/browser/net/asar/url_request_asar_job.cc
Normal file
337
shell/browser/net/asar/url_request_asar_job.cc
Normal file
|
@ -0,0 +1,337 @@
|
|||
// Copyright (c) 2014 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/asar/url_request_asar_job.h"
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "atom/common/asar/archive.h"
|
||||
#include "atom/common/asar/asar_util.h"
|
||||
#include "atom/common/atom_constants.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
#include "base/task_runner.h"
|
||||
#include "base/threading/thread_task_runner_handle.h"
|
||||
#include "net/base/file_stream.h"
|
||||
#include "net/base/filename_util.h"
|
||||
#include "net/base/io_buffer.h"
|
||||
#include "net/base/load_flags.h"
|
||||
#include "net/base/mime_util.h"
|
||||
#include "net/base/net_errors.h"
|
||||
#include "net/filter/gzip_source_stream.h"
|
||||
#include "net/http/http_util.h"
|
||||
#include "net/url_request/url_request_status.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "base/win/shortcut.h"
|
||||
#endif
|
||||
|
||||
namespace asar {
|
||||
|
||||
URLRequestAsarJob::FileMetaInfo::FileMetaInfo() = default;
|
||||
|
||||
URLRequestAsarJob::URLRequestAsarJob(net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate)
|
||||
: net::URLRequestJob(request, network_delegate), weak_ptr_factory_(this) {}
|
||||
|
||||
URLRequestAsarJob::~URLRequestAsarJob() {}
|
||||
|
||||
void URLRequestAsarJob::Initialize(
|
||||
const scoped_refptr<base::TaskRunner> file_task_runner,
|
||||
const base::FilePath& file_path) {
|
||||
// Determine whether it is an asar file.
|
||||
base::FilePath asar_path, relative_path;
|
||||
if (!GetAsarArchivePath(file_path, &asar_path, &relative_path)) {
|
||||
InitializeFileJob(file_task_runner, file_path);
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<Archive> archive = GetOrCreateAsarArchive(asar_path);
|
||||
Archive::FileInfo file_info;
|
||||
if (!archive || !archive->GetFileInfo(relative_path, &file_info)) {
|
||||
type_ = JobType::kError;
|
||||
return;
|
||||
}
|
||||
|
||||
if (file_info.unpacked) {
|
||||
base::FilePath real_path;
|
||||
archive->CopyFileOut(relative_path, &real_path);
|
||||
InitializeFileJob(file_task_runner, real_path);
|
||||
return;
|
||||
}
|
||||
|
||||
InitializeAsarJob(file_task_runner, archive, relative_path, file_info);
|
||||
}
|
||||
|
||||
void URLRequestAsarJob::InitializeAsarJob(
|
||||
const scoped_refptr<base::TaskRunner> file_task_runner,
|
||||
std::shared_ptr<Archive> archive,
|
||||
const base::FilePath& file_path,
|
||||
const Archive::FileInfo& file_info) {
|
||||
type_ = JobType::kAsar;
|
||||
file_task_runner_ = file_task_runner;
|
||||
stream_.reset(new net::FileStream(file_task_runner_));
|
||||
archive_ = archive;
|
||||
file_path_ = file_path;
|
||||
file_info_ = file_info;
|
||||
}
|
||||
|
||||
void URLRequestAsarJob::InitializeFileJob(
|
||||
const scoped_refptr<base::TaskRunner> file_task_runner,
|
||||
const base::FilePath& file_path) {
|
||||
type_ = JobType::kFile;
|
||||
file_task_runner_ = file_task_runner;
|
||||
stream_.reset(new net::FileStream(file_task_runner_));
|
||||
file_path_ = file_path;
|
||||
}
|
||||
|
||||
void URLRequestAsarJob::Start() {
|
||||
if (type_ == JobType::kAsar || type_ == JobType::kFile) {
|
||||
auto* meta_info = new FileMetaInfo();
|
||||
if (type_ == JobType::kAsar) {
|
||||
meta_info->file_path = archive_->path();
|
||||
meta_info->file_exists = true;
|
||||
meta_info->is_directory = false;
|
||||
meta_info->file_size = file_info_.size;
|
||||
}
|
||||
file_task_runner_->PostTaskAndReply(
|
||||
FROM_HERE,
|
||||
base::BindOnce(&URLRequestAsarJob::FetchMetaInfo, file_path_, type_,
|
||||
base::Unretained(meta_info)),
|
||||
base::BindOnce(&URLRequestAsarJob::DidFetchMetaInfo,
|
||||
weak_ptr_factory_.GetWeakPtr(), base::Owned(meta_info)));
|
||||
} else {
|
||||
base::ThreadTaskRunnerHandle::Get()->PostTask(
|
||||
FROM_HERE, base::BindOnce(&URLRequestAsarJob::DidOpen,
|
||||
weak_ptr_factory_.GetWeakPtr(),
|
||||
net::ERR_FILE_NOT_FOUND));
|
||||
}
|
||||
}
|
||||
|
||||
void URLRequestAsarJob::Kill() {
|
||||
stream_.reset();
|
||||
weak_ptr_factory_.InvalidateWeakPtrs();
|
||||
|
||||
URLRequestJob::Kill();
|
||||
}
|
||||
|
||||
int URLRequestAsarJob::ReadRawData(net::IOBuffer* dest, int dest_size) {
|
||||
if (remaining_bytes_ < dest_size)
|
||||
dest_size = static_cast<int>(remaining_bytes_);
|
||||
|
||||
// If we should copy zero bytes because |remaining_bytes_| is zero, short
|
||||
// circuit here.
|
||||
if (!dest_size)
|
||||
return 0;
|
||||
|
||||
int rv = stream_->Read(
|
||||
dest, dest_size,
|
||||
base::BindOnce(&URLRequestAsarJob::DidRead,
|
||||
weak_ptr_factory_.GetWeakPtr(), WrapRefCounted(dest)));
|
||||
if (rv >= 0) {
|
||||
remaining_bytes_ -= rv;
|
||||
DCHECK_GE(remaining_bytes_, 0);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool URLRequestAsarJob::IsRedirectResponse(GURL* location,
|
||||
int* http_status_code,
|
||||
bool* insecure_scheme_was_upgraded) {
|
||||
if (type_ != JobType::kFile)
|
||||
return false;
|
||||
#if defined(OS_WIN)
|
||||
// Follow a Windows shortcut.
|
||||
// We just resolve .lnk file, ignore others.
|
||||
if (!base::LowerCaseEqualsASCII(file_path_.Extension(), ".lnk"))
|
||||
return false;
|
||||
|
||||
base::FilePath new_path = file_path_;
|
||||
bool resolved;
|
||||
resolved = base::win::ResolveShortcut(new_path, &new_path, NULL);
|
||||
|
||||
// If shortcut is not resolved succesfully, do not redirect.
|
||||
if (!resolved)
|
||||
return false;
|
||||
|
||||
*location = net::FilePathToFileURL(new_path);
|
||||
*http_status_code = 301;
|
||||
*insecure_scheme_was_upgraded = false;
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::unique_ptr<net::SourceStream> URLRequestAsarJob::SetUpSourceStream() {
|
||||
std::unique_ptr<net::SourceStream> source =
|
||||
net::URLRequestJob::SetUpSourceStream();
|
||||
// Bug 9936 - .svgz files needs to be decompressed.
|
||||
return base::LowerCaseEqualsASCII(file_path_.Extension(), ".svgz")
|
||||
? net::GzipSourceStream::Create(std::move(source),
|
||||
net::SourceStream::TYPE_GZIP)
|
||||
: std::move(source);
|
||||
}
|
||||
|
||||
bool URLRequestAsarJob::GetMimeType(std::string* mime_type) const {
|
||||
if (meta_info_.mime_type_result) {
|
||||
*mime_type = meta_info_.mime_type;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void URLRequestAsarJob::SetExtraRequestHeaders(
|
||||
const net::HttpRequestHeaders& headers) {
|
||||
std::string range_header;
|
||||
if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) {
|
||||
// This job only cares about the Range header. This method stashes the value
|
||||
// for later use in DidOpen(), which is responsible for some of the range
|
||||
// validation as well. NotifyStartError is not legal to call here since
|
||||
// the job has not started.
|
||||
std::vector<net::HttpByteRange> ranges;
|
||||
if (net::HttpUtil::ParseRangeHeader(range_header, &ranges)) {
|
||||
if (ranges.size() == 1) {
|
||||
byte_range_ = ranges[0];
|
||||
} else {
|
||||
range_parse_result_ = net::ERR_REQUEST_RANGE_NOT_SATISFIABLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int URLRequestAsarJob::GetResponseCode() const {
|
||||
// Request Job gets created only if path exists.
|
||||
return 200;
|
||||
}
|
||||
|
||||
void URLRequestAsarJob::GetResponseInfo(net::HttpResponseInfo* info) {
|
||||
std::string status("HTTP/1.1 200 OK");
|
||||
auto* headers = new net::HttpResponseHeaders(status);
|
||||
|
||||
headers->AddHeader(atom::kCORSHeader);
|
||||
info->headers = headers;
|
||||
}
|
||||
|
||||
void URLRequestAsarJob::FetchMetaInfo(const base::FilePath& file_path,
|
||||
JobType type,
|
||||
FileMetaInfo* meta_info) {
|
||||
if (type == JobType::kFile) {
|
||||
base::File::Info file_info;
|
||||
meta_info->file_exists = base::GetFileInfo(file_path, &file_info);
|
||||
if (meta_info->file_exists) {
|
||||
meta_info->file_path = file_path;
|
||||
meta_info->file_size = file_info.size;
|
||||
meta_info->is_directory = file_info.is_directory;
|
||||
}
|
||||
}
|
||||
|
||||
// We use GetWellKnownMimeTypeFromExtension() to ensure that configurations
|
||||
// that may have been set by other programs on a user's machine don't affect
|
||||
// the mime type returned (in particular, JS should always be
|
||||
// (application/javascript). See https://crbug.com/797712. Using an accurate
|
||||
// mime type is necessary at least for modules and sw, which enforce strict
|
||||
// mime type requirements.
|
||||
// TODO(deepak1556): Revert this when sw support is removed for file scheme.
|
||||
base::FilePath::StringType file_extension = file_path.Extension();
|
||||
if (file_extension.empty()) {
|
||||
meta_info->mime_type_result = false;
|
||||
} else {
|
||||
meta_info->mime_type_result = net::GetWellKnownMimeTypeFromExtension(
|
||||
file_extension.substr(1), &meta_info->mime_type);
|
||||
}
|
||||
}
|
||||
|
||||
void URLRequestAsarJob::DidFetchMetaInfo(const FileMetaInfo* meta_info) {
|
||||
meta_info_ = *meta_info;
|
||||
if (!meta_info_.file_exists || meta_info_.is_directory) {
|
||||
DidOpen(net::ERR_FILE_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
int flags =
|
||||
base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_ASYNC;
|
||||
int rv = stream_->Open(meta_info_.file_path, flags,
|
||||
base::BindOnce(&URLRequestAsarJob::DidOpen,
|
||||
weak_ptr_factory_.GetWeakPtr()));
|
||||
if (rv != net::ERR_IO_PENDING)
|
||||
DidOpen(rv);
|
||||
}
|
||||
|
||||
void URLRequestAsarJob::DidOpen(int result) {
|
||||
if (result != net::OK) {
|
||||
NotifyStartError(
|
||||
net::URLRequestStatus(net::URLRequestStatus::FAILED, result));
|
||||
return;
|
||||
}
|
||||
|
||||
if (range_parse_result_ != net::OK) {
|
||||
NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED,
|
||||
range_parse_result_));
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t file_size, read_offset;
|
||||
if (type_ == JobType::kAsar) {
|
||||
file_size = file_info_.size;
|
||||
read_offset = file_info_.offset;
|
||||
} else {
|
||||
file_size = meta_info_.file_size;
|
||||
read_offset = 0;
|
||||
}
|
||||
|
||||
if (!byte_range_.ComputeBounds(file_size)) {
|
||||
NotifyStartError(net::URLRequestStatus(
|
||||
net::URLRequestStatus::FAILED, net::ERR_REQUEST_RANGE_NOT_SATISFIABLE));
|
||||
return;
|
||||
}
|
||||
|
||||
remaining_bytes_ =
|
||||
byte_range_.last_byte_position() - byte_range_.first_byte_position() + 1;
|
||||
seek_offset_ = byte_range_.first_byte_position() + read_offset;
|
||||
|
||||
if (remaining_bytes_ > 0 && seek_offset_ != 0) {
|
||||
int rv = stream_->Seek(seek_offset_,
|
||||
base::BindOnce(&URLRequestAsarJob::DidSeek,
|
||||
weak_ptr_factory_.GetWeakPtr()));
|
||||
if (rv != net::ERR_IO_PENDING) {
|
||||
// stream_->Seek() failed, so pass an intentionally erroneous value
|
||||
// into DidSeek().
|
||||
DidSeek(-1);
|
||||
}
|
||||
} else {
|
||||
// We didn't need to call stream_->Seek() at all, so we pass to DidSeek()
|
||||
// the value that would mean seek success. This way we skip the code
|
||||
// handling seek failure.
|
||||
DidSeek(seek_offset_);
|
||||
}
|
||||
}
|
||||
|
||||
void URLRequestAsarJob::DidSeek(int64_t result) {
|
||||
if (result != seek_offset_) {
|
||||
NotifyStartError(net::URLRequestStatus(
|
||||
net::URLRequestStatus::FAILED, net::ERR_REQUEST_RANGE_NOT_SATISFIABLE));
|
||||
return;
|
||||
}
|
||||
set_expected_content_size(remaining_bytes_);
|
||||
NotifyHeadersComplete();
|
||||
}
|
||||
|
||||
void URLRequestAsarJob::DidRead(scoped_refptr<net::IOBuffer> buf, int result) {
|
||||
if (result >= 0) {
|
||||
remaining_bytes_ -= result;
|
||||
DCHECK_GE(remaining_bytes_, 0);
|
||||
}
|
||||
|
||||
buf = nullptr;
|
||||
|
||||
ReadRawDataComplete(result);
|
||||
}
|
||||
|
||||
} // namespace asar
|
137
shell/browser/net/asar/url_request_asar_job.h
Normal file
137
shell/browser/net/asar/url_request_asar_job.h
Normal file
|
@ -0,0 +1,137 @@
|
|||
// Copyright (c) 2014 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_ASAR_URL_REQUEST_ASAR_JOB_H_
|
||||
#define ATOM_BROWSER_NET_ASAR_URL_REQUEST_ASAR_JOB_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "atom/browser/net/js_asker.h"
|
||||
#include "atom/common/asar/archive.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "net/http/http_byte_range.h"
|
||||
#include "net/url_request/url_request_job.h"
|
||||
|
||||
namespace base {
|
||||
class TaskRunner;
|
||||
}
|
||||
|
||||
namespace net {
|
||||
class FileStream;
|
||||
}
|
||||
|
||||
namespace asar {
|
||||
|
||||
// Createa a request job according to the file path.
|
||||
net::URLRequestJob* CreateJobFromPath(
|
||||
const base::FilePath& full_path,
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate,
|
||||
const scoped_refptr<base::TaskRunner> file_task_runner);
|
||||
|
||||
class URLRequestAsarJob : public net::URLRequestJob {
|
||||
public:
|
||||
URLRequestAsarJob(net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate);
|
||||
|
||||
void Initialize(const scoped_refptr<base::TaskRunner> file_task_runner,
|
||||
const base::FilePath& file_path);
|
||||
|
||||
protected:
|
||||
~URLRequestAsarJob() override;
|
||||
|
||||
void InitializeAsarJob(const scoped_refptr<base::TaskRunner> file_task_runner,
|
||||
std::shared_ptr<Archive> archive,
|
||||
const base::FilePath& file_path,
|
||||
const Archive::FileInfo& file_info);
|
||||
void InitializeFileJob(const scoped_refptr<base::TaskRunner> file_task_runner,
|
||||
const base::FilePath& file_path);
|
||||
|
||||
// net::URLRequestJob:
|
||||
void Start() override;
|
||||
void Kill() override;
|
||||
int ReadRawData(net::IOBuffer* buf, int buf_size) override;
|
||||
bool IsRedirectResponse(GURL* location,
|
||||
int* http_status_code,
|
||||
bool* insecure_scheme_was_upgraded) override;
|
||||
std::unique_ptr<net::SourceStream> SetUpSourceStream() override;
|
||||
bool GetMimeType(std::string* mime_type) const override;
|
||||
void SetExtraRequestHeaders(const net::HttpRequestHeaders& headers) override;
|
||||
int GetResponseCode() const override;
|
||||
void GetResponseInfo(net::HttpResponseInfo* info) override;
|
||||
|
||||
private:
|
||||
// The type of this job.
|
||||
enum class JobType {
|
||||
kError,
|
||||
kAsar,
|
||||
kFile,
|
||||
};
|
||||
|
||||
// Meta information about the file. It's used as a member in the
|
||||
// URLRequestFileJob and also passed between threads because disk access is
|
||||
// necessary to obtain it.
|
||||
struct FileMetaInfo {
|
||||
// Size of the file.
|
||||
int64_t file_size = 0;
|
||||
// Mime type associated with the file.
|
||||
std::string mime_type;
|
||||
// Result returned from GetMimeTypeFromFile(), i.e. flag showing whether
|
||||
// obtaining of the mime type was successful.
|
||||
bool mime_type_result = false;
|
||||
// Flag showing whether the file exists.
|
||||
bool file_exists = false;
|
||||
// Flag showing whether the file name actually refers to a directory.
|
||||
bool is_directory = false;
|
||||
// Path to the file.
|
||||
base::FilePath file_path;
|
||||
|
||||
FileMetaInfo();
|
||||
};
|
||||
|
||||
// Fetches file info on a background thread.
|
||||
static void FetchMetaInfo(const base::FilePath& file_path,
|
||||
JobType type,
|
||||
FileMetaInfo* meta_info);
|
||||
|
||||
// Callback after fetching file info on a background thread.
|
||||
void DidFetchMetaInfo(const FileMetaInfo* meta_info);
|
||||
|
||||
// Callback after opening file on a background thread.
|
||||
void DidOpen(int result);
|
||||
|
||||
// Callback after seeking to the beginning of |byte_range_| in the file
|
||||
// on a background thread.
|
||||
void DidSeek(int64_t result);
|
||||
|
||||
// Callback after data is asynchronously read from the file into |buf|.
|
||||
void DidRead(scoped_refptr<net::IOBuffer> buf, int result);
|
||||
|
||||
JobType type_ = JobType::kError;
|
||||
|
||||
std::shared_ptr<Archive> archive_;
|
||||
base::FilePath file_path_;
|
||||
Archive::FileInfo file_info_;
|
||||
|
||||
std::unique_ptr<net::FileStream> stream_;
|
||||
FileMetaInfo meta_info_;
|
||||
scoped_refptr<base::TaskRunner> file_task_runner_;
|
||||
|
||||
net::HttpByteRange byte_range_;
|
||||
int64_t remaining_bytes_ = 0;
|
||||
int64_t seek_offset_ = 0;
|
||||
|
||||
net::Error range_parse_result_ = net::OK;
|
||||
|
||||
base::WeakPtrFactory<URLRequestAsarJob> weak_ptr_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(URLRequestAsarJob);
|
||||
};
|
||||
|
||||
} // namespace asar
|
||||
|
||||
#endif // ATOM_BROWSER_NET_ASAR_URL_REQUEST_ASAR_JOB_H_
|
209
shell/browser/net/atom_cert_verifier.cc
Normal file
209
shell/browser/net/atom_cert_verifier.cc
Normal file
|
@ -0,0 +1,209 @@
|
|||
// Copyright (c) 2015 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/atom_cert_verifier.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "atom/browser/browser.h"
|
||||
#include "atom/browser/net/require_ct_delegate.h"
|
||||
#include "atom/common/native_mate_converters/net_converter.h"
|
||||
#include "base/containers/linked_list.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/task/post_task.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "net/base/net_errors.h"
|
||||
#include "net/cert/cert_verify_result.h"
|
||||
#include "net/cert/x509_certificate.h"
|
||||
|
||||
using content::BrowserThread;
|
||||
|
||||
namespace atom {
|
||||
|
||||
VerifyRequestParams::VerifyRequestParams() = default;
|
||||
VerifyRequestParams::~VerifyRequestParams() = default;
|
||||
VerifyRequestParams::VerifyRequestParams(const VerifyRequestParams&) = default;
|
||||
|
||||
namespace {
|
||||
|
||||
class Response : public base::LinkNode<Response> {
|
||||
public:
|
||||
Response(net::CertVerifyResult* verify_result,
|
||||
net::CompletionOnceCallback callback)
|
||||
: verify_result_(verify_result), callback_(std::move(callback)) {}
|
||||
net::CertVerifyResult* verify_result() { return verify_result_; }
|
||||
net::CompletionOnceCallback callback() { return std::move(callback_); }
|
||||
|
||||
private:
|
||||
net::CertVerifyResult* verify_result_;
|
||||
net::CompletionOnceCallback callback_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Response);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
class CertVerifierRequest : public AtomCertVerifier::Request {
|
||||
public:
|
||||
CertVerifierRequest(const AtomCertVerifier::RequestParams& params,
|
||||
AtomCertVerifier* cert_verifier)
|
||||
: params_(params),
|
||||
cert_verifier_(cert_verifier),
|
||||
weak_ptr_factory_(this) {}
|
||||
|
||||
~CertVerifierRequest() override {
|
||||
cert_verifier_->RemoveRequest(params_);
|
||||
default_verifier_request_.reset();
|
||||
while (!response_list_.empty() && !first_response_) {
|
||||
base::LinkNode<Response>* response_node = response_list_.head();
|
||||
response_node->RemoveFromList();
|
||||
Response* response = response_node->value();
|
||||
RunResponse(response);
|
||||
}
|
||||
cert_verifier_ = nullptr;
|
||||
weak_ptr_factory_.InvalidateWeakPtrs();
|
||||
}
|
||||
|
||||
void RunResponse(Response* response) {
|
||||
if (custom_response_ == net::ERR_ABORTED) {
|
||||
*(response->verify_result()) = result_;
|
||||
response->callback().Run(error_);
|
||||
} else {
|
||||
response->verify_result()->Reset();
|
||||
response->verify_result()->verified_cert = params_.certificate();
|
||||
cert_verifier_->ct_delegate()->AddCTExcludedHost(params_.hostname());
|
||||
response->callback().Run(custom_response_);
|
||||
}
|
||||
delete response;
|
||||
}
|
||||
|
||||
void Start(const net::NetLogWithSource& net_log) {
|
||||
int error = cert_verifier_->default_verifier()->Verify(
|
||||
params_, &result_,
|
||||
base::BindOnce(&CertVerifierRequest::OnDefaultVerificationDone,
|
||||
weak_ptr_factory_.GetWeakPtr()),
|
||||
&default_verifier_request_, net_log);
|
||||
if (error != net::ERR_IO_PENDING)
|
||||
OnDefaultVerificationDone(error);
|
||||
}
|
||||
|
||||
void OnDefaultVerificationDone(int error) {
|
||||
error_ = error;
|
||||
auto request = std::make_unique<VerifyRequestParams>();
|
||||
request->hostname = params_.hostname();
|
||||
request->default_result = net::ErrorToString(error);
|
||||
request->error_code = error;
|
||||
request->certificate = params_.certificate();
|
||||
auto response_callback = base::BindOnce(
|
||||
&CertVerifierRequest::OnResponseInUI, weak_ptr_factory_.GetWeakPtr());
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {BrowserThread::UI},
|
||||
base::BindOnce(&CertVerifierRequest::OnVerifyRequestInUI,
|
||||
cert_verifier_->verify_proc(), std::move(request),
|
||||
std::move(response_callback)));
|
||||
}
|
||||
|
||||
static void OnVerifyRequestInUI(
|
||||
const AtomCertVerifier::VerifyProc& verify_proc,
|
||||
std::unique_ptr<VerifyRequestParams> request,
|
||||
base::OnceCallback<void(int)> response_callback) {
|
||||
verify_proc.Run(*(request.get()), std::move(response_callback));
|
||||
}
|
||||
|
||||
static void OnResponseInUI(base::WeakPtr<CertVerifierRequest> self,
|
||||
int result) {
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {BrowserThread::IO},
|
||||
base::BindOnce(&CertVerifierRequest::NotifyResponseInIO, self, result));
|
||||
}
|
||||
|
||||
void NotifyResponseInIO(int result) {
|
||||
custom_response_ = result;
|
||||
first_response_ = false;
|
||||
// Responding to first request in the list will initiate destruction of
|
||||
// the class, respond to others in the list inside destructor.
|
||||
base::LinkNode<Response>* response_node = response_list_.head();
|
||||
response_node->RemoveFromList();
|
||||
Response* response = response_node->value();
|
||||
RunResponse(response);
|
||||
}
|
||||
|
||||
void AddResponseListener(net::CertVerifyResult* verify_result,
|
||||
net::CompletionOnceCallback callback) {
|
||||
response_list_.Append(new Response(verify_result, std::move(callback)));
|
||||
}
|
||||
|
||||
const AtomCertVerifier::RequestParams& params() const { return params_; }
|
||||
|
||||
private:
|
||||
using ResponseList = base::LinkedList<Response>;
|
||||
|
||||
const AtomCertVerifier::RequestParams params_;
|
||||
AtomCertVerifier* cert_verifier_;
|
||||
int error_ = net::ERR_IO_PENDING;
|
||||
int custom_response_ = net::ERR_IO_PENDING;
|
||||
bool first_response_ = true;
|
||||
ResponseList response_list_;
|
||||
net::CertVerifyResult result_;
|
||||
std::unique_ptr<AtomCertVerifier::Request> default_verifier_request_;
|
||||
base::WeakPtrFactory<CertVerifierRequest> weak_ptr_factory_;
|
||||
};
|
||||
|
||||
AtomCertVerifier::AtomCertVerifier(RequireCTDelegate* ct_delegate)
|
||||
: default_cert_verifier_(net::CertVerifier::CreateDefault(nullptr)),
|
||||
ct_delegate_(ct_delegate) {}
|
||||
|
||||
AtomCertVerifier::~AtomCertVerifier() {}
|
||||
|
||||
void AtomCertVerifier::SetVerifyProc(const VerifyProc& proc) {
|
||||
verify_proc_ = proc;
|
||||
}
|
||||
|
||||
int AtomCertVerifier::Verify(const RequestParams& params,
|
||||
net::CertVerifyResult* verify_result,
|
||||
net::CompletionOnceCallback callback,
|
||||
std::unique_ptr<Request>* out_req,
|
||||
const net::NetLogWithSource& net_log) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
|
||||
if (verify_proc_.is_null()) {
|
||||
ct_delegate_->ClearCTExcludedHostsList();
|
||||
return default_cert_verifier_->Verify(
|
||||
params, verify_result, std::move(callback), out_req, net_log);
|
||||
} else {
|
||||
CertVerifierRequest* request = FindRequest(params);
|
||||
if (!request) {
|
||||
out_req->reset();
|
||||
auto new_request = std::make_unique<CertVerifierRequest>(params, this);
|
||||
new_request->Start(net_log);
|
||||
request = new_request.get();
|
||||
*out_req = std::move(new_request);
|
||||
inflight_requests_[params] = request;
|
||||
}
|
||||
request->AddResponseListener(verify_result, std::move(callback));
|
||||
|
||||
return net::ERR_IO_PENDING;
|
||||
}
|
||||
}
|
||||
|
||||
void AtomCertVerifier::SetConfig(const Config& config) {
|
||||
default_cert_verifier_->SetConfig(config);
|
||||
}
|
||||
|
||||
void AtomCertVerifier::RemoveRequest(const RequestParams& params) {
|
||||
auto it = inflight_requests_.find(params);
|
||||
if (it != inflight_requests_.end())
|
||||
inflight_requests_.erase(it);
|
||||
}
|
||||
|
||||
CertVerifierRequest* AtomCertVerifier::FindRequest(
|
||||
const RequestParams& params) {
|
||||
auto it = inflight_requests_.find(params);
|
||||
if (it != inflight_requests_.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace atom
|
71
shell/browser/net/atom_cert_verifier.h
Normal file
71
shell/browser/net/atom_cert_verifier.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
// Copyright (c) 2015 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_ATOM_CERT_VERIFIER_H_
|
||||
#define ATOM_BROWSER_NET_ATOM_CERT_VERIFIER_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "net/cert/cert_verifier.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class CertVerifierRequest;
|
||||
class RequireCTDelegate;
|
||||
|
||||
struct VerifyRequestParams {
|
||||
std::string hostname;
|
||||
std::string default_result;
|
||||
int error_code;
|
||||
scoped_refptr<net::X509Certificate> certificate;
|
||||
|
||||
VerifyRequestParams();
|
||||
VerifyRequestParams(const VerifyRequestParams&);
|
||||
~VerifyRequestParams();
|
||||
};
|
||||
|
||||
class AtomCertVerifier : public net::CertVerifier {
|
||||
public:
|
||||
explicit AtomCertVerifier(RequireCTDelegate* ct_delegate);
|
||||
~AtomCertVerifier() override;
|
||||
|
||||
using VerifyProc = base::Callback<void(const VerifyRequestParams& request,
|
||||
net::CompletionOnceCallback)>;
|
||||
|
||||
void SetVerifyProc(const VerifyProc& proc);
|
||||
|
||||
const VerifyProc verify_proc() const { return verify_proc_; }
|
||||
RequireCTDelegate* ct_delegate() const { return ct_delegate_; }
|
||||
net::CertVerifier* default_verifier() const {
|
||||
return default_cert_verifier_.get();
|
||||
}
|
||||
|
||||
protected:
|
||||
// net::CertVerifier:
|
||||
int Verify(const RequestParams& params,
|
||||
net::CertVerifyResult* verify_result,
|
||||
net::CompletionOnceCallback callback,
|
||||
std::unique_ptr<Request>* out_req,
|
||||
const net::NetLogWithSource& net_log) override;
|
||||
void SetConfig(const Config& config) override;
|
||||
|
||||
private:
|
||||
friend class CertVerifierRequest;
|
||||
|
||||
void RemoveRequest(const RequestParams& params);
|
||||
CertVerifierRequest* FindRequest(const RequestParams& params);
|
||||
|
||||
std::map<RequestParams, CertVerifierRequest*> inflight_requests_;
|
||||
VerifyProc verify_proc_;
|
||||
std::unique_ptr<net::CertVerifier> default_cert_verifier_;
|
||||
RequireCTDelegate* ct_delegate_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AtomCertVerifier);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_ATOM_CERT_VERIFIER_H_
|
545
shell/browser/net/atom_network_delegate.cc
Normal file
545
shell/browser/net/atom_network_delegate.cc
Normal file
|
@ -0,0 +1,545 @@
|
|||
// Copyright (c) 2015 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/atom_network_delegate.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "atom/browser/api/atom_api_web_contents.h"
|
||||
#include "atom/browser/login_handler.h"
|
||||
#include "atom/common/native_mate_converters/net_converter.h"
|
||||
#include "atom/common/options_switches.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/task/post_task.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/render_frame_host.h"
|
||||
#include "content/public/browser/resource_request_info.h"
|
||||
#include "net/base/load_flags.h"
|
||||
#include "net/base/net_errors.h"
|
||||
#include "net/url_request/url_request.h"
|
||||
|
||||
using content::BrowserThread;
|
||||
|
||||
namespace atom {
|
||||
|
||||
const char* ResourceTypeToString(content::ResourceType type) {
|
||||
switch (type) {
|
||||
case content::ResourceType::kMainFrame:
|
||||
return "mainFrame";
|
||||
case content::ResourceType::kSubFrame:
|
||||
return "subFrame";
|
||||
case content::ResourceType::kStylesheet:
|
||||
return "stylesheet";
|
||||
case content::ResourceType::kScript:
|
||||
return "script";
|
||||
case content::ResourceType::kImage:
|
||||
return "image";
|
||||
case content::ResourceType::kObject:
|
||||
return "object";
|
||||
case content::ResourceType::kXhr:
|
||||
return "xhr";
|
||||
default:
|
||||
return "other";
|
||||
}
|
||||
}
|
||||
|
||||
int32_t GetWebContentsID(int process_id, int frame_id) {
|
||||
auto* webContents = content::WebContents::FromRenderFrameHost(
|
||||
content::RenderFrameHost::FromID(process_id, frame_id));
|
||||
return atom::api::WebContents::GetIDFromWrappedClass(webContents);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
using ResponseHeadersContainer =
|
||||
std::pair<scoped_refptr<net::HttpResponseHeaders>*, const std::string&>;
|
||||
|
||||
void RunSimpleListener(const AtomNetworkDelegate::SimpleListener& listener,
|
||||
std::unique_ptr<base::DictionaryValue> details,
|
||||
int render_process_id,
|
||||
int render_frame_id) {
|
||||
int32_t id = GetWebContentsID(render_process_id, render_frame_id);
|
||||
// id must be greater than zero
|
||||
if (id)
|
||||
details->SetInteger("webContentsId", id);
|
||||
return listener.Run(*(details.get()));
|
||||
}
|
||||
|
||||
void RunResponseListener(const AtomNetworkDelegate::ResponseListener& listener,
|
||||
std::unique_ptr<base::DictionaryValue> details,
|
||||
int render_process_id,
|
||||
int render_frame_id,
|
||||
AtomNetworkDelegate::ResponseCallback callback) {
|
||||
int32_t id = GetWebContentsID(render_process_id, render_frame_id);
|
||||
// id must be greater than zero
|
||||
if (id)
|
||||
details->SetInteger("webContentsId", id);
|
||||
return listener.Run(*(details.get()), std::move(callback));
|
||||
}
|
||||
|
||||
// Test whether the URL of |request| matches |patterns|.
|
||||
bool MatchesFilterCondition(net::URLRequest* request,
|
||||
const URLPatterns& patterns) {
|
||||
if (patterns.empty())
|
||||
return true;
|
||||
|
||||
for (const auto& pattern : patterns) {
|
||||
if (pattern.MatchesURL(request->url()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Overloaded by multiple types to fill the |details| object.
|
||||
void ToDictionary(base::DictionaryValue* details, net::URLRequest* request) {
|
||||
FillRequestDetails(details, request);
|
||||
details->SetInteger("id", request->identifier());
|
||||
details->SetDouble("timestamp", base::Time::Now().ToDoubleT() * 1000);
|
||||
auto* info = content::ResourceRequestInfo::ForRequest(request);
|
||||
if (info) {
|
||||
details->SetString("resourceType",
|
||||
ResourceTypeToString(info->GetResourceType()));
|
||||
} else {
|
||||
details->SetString("resourceType", "other");
|
||||
}
|
||||
}
|
||||
|
||||
void ToDictionary(base::DictionaryValue* details,
|
||||
const net::HttpRequestHeaders& headers) {
|
||||
auto dict = std::make_unique<base::DictionaryValue>();
|
||||
net::HttpRequestHeaders::Iterator it(headers);
|
||||
while (it.GetNext())
|
||||
dict->SetKey(it.name(), base::Value(it.value()));
|
||||
details->Set("requestHeaders", std::move(dict));
|
||||
}
|
||||
|
||||
void ToDictionary(base::DictionaryValue* details,
|
||||
const net::HttpResponseHeaders* headers) {
|
||||
if (!headers)
|
||||
return;
|
||||
|
||||
auto dict = std::make_unique<base::DictionaryValue>();
|
||||
size_t iter = 0;
|
||||
std::string key;
|
||||
std::string value;
|
||||
while (headers->EnumerateHeaderLines(&iter, &key, &value)) {
|
||||
if (dict->FindKey(key)) {
|
||||
base::ListValue* values = nullptr;
|
||||
if (dict->GetList(key, &values))
|
||||
values->AppendString(value);
|
||||
} else {
|
||||
auto values = std::make_unique<base::ListValue>();
|
||||
values->AppendString(value);
|
||||
dict->Set(key, std::move(values));
|
||||
}
|
||||
}
|
||||
details->Set("responseHeaders", std::move(dict));
|
||||
details->SetString("statusLine", headers->GetStatusLine());
|
||||
details->SetInteger("statusCode", headers->response_code());
|
||||
}
|
||||
|
||||
void ToDictionary(base::DictionaryValue* details, const GURL& location) {
|
||||
details->SetString("redirectURL", location.spec());
|
||||
}
|
||||
|
||||
void ToDictionary(base::DictionaryValue* details,
|
||||
const net::IPEndPoint& remote_endpoint) {
|
||||
details->SetString("ip", remote_endpoint.ToStringWithoutPort());
|
||||
}
|
||||
|
||||
void ToDictionary(base::DictionaryValue* details, bool from_cache) {
|
||||
details->SetBoolean("fromCache", from_cache);
|
||||
}
|
||||
|
||||
void ToDictionary(base::DictionaryValue* details,
|
||||
const net::URLRequestStatus& status) {
|
||||
details->SetString("error", net::ErrorToString(status.error()));
|
||||
}
|
||||
|
||||
// Helper function to fill |details| with arbitrary |args|.
|
||||
template <typename Arg>
|
||||
void FillDetailsObject(base::DictionaryValue* details, Arg arg) {
|
||||
ToDictionary(details, arg);
|
||||
}
|
||||
|
||||
template <typename Arg, typename... Args>
|
||||
void FillDetailsObject(base::DictionaryValue* details, Arg arg, Args... args) {
|
||||
ToDictionary(details, arg);
|
||||
FillDetailsObject(details, args...);
|
||||
}
|
||||
|
||||
// Fill the native types with the result from the response object.
|
||||
void ReadFromResponseObject(const base::DictionaryValue& response,
|
||||
GURL* new_location) {
|
||||
std::string url;
|
||||
if (response.GetString("redirectURL", &url))
|
||||
*new_location = GURL(url);
|
||||
}
|
||||
|
||||
void ReadFromResponseObject(const base::DictionaryValue& response,
|
||||
net::HttpRequestHeaders* headers) {
|
||||
const base::DictionaryValue* dict;
|
||||
if (response.GetDictionary("requestHeaders", &dict)) {
|
||||
headers->Clear();
|
||||
for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd();
|
||||
it.Advance()) {
|
||||
if (it.value().is_string()) {
|
||||
std::string value = it.value().GetString();
|
||||
headers->SetHeader(it.key(), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReadFromResponseObject(const base::DictionaryValue& response,
|
||||
const ResponseHeadersContainer& container) {
|
||||
const base::DictionaryValue* dict;
|
||||
std::string status_line;
|
||||
if (!response.GetString("statusLine", &status_line))
|
||||
status_line = container.second;
|
||||
if (response.GetDictionary("responseHeaders", &dict)) {
|
||||
auto* headers = container.first;
|
||||
*headers = new net::HttpResponseHeaders("");
|
||||
(*headers)->ReplaceStatusLine(status_line);
|
||||
for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd();
|
||||
it.Advance()) {
|
||||
const base::ListValue* list;
|
||||
if (it.value().GetAsList(&list)) {
|
||||
(*headers)->RemoveHeader(it.key());
|
||||
for (size_t i = 0; i < list->GetSize(); ++i) {
|
||||
std::string value;
|
||||
if (list->GetString(i, &value))
|
||||
(*headers)->AddHeader(it.key() + " : " + value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AtomNetworkDelegate::SimpleListenerInfo::SimpleListenerInfo(
|
||||
URLPatterns patterns_,
|
||||
SimpleListener listener_)
|
||||
: url_patterns(patterns_), listener(listener_) {}
|
||||
AtomNetworkDelegate::SimpleListenerInfo::SimpleListenerInfo() = default;
|
||||
AtomNetworkDelegate::SimpleListenerInfo::~SimpleListenerInfo() = default;
|
||||
|
||||
AtomNetworkDelegate::ResponseListenerInfo::ResponseListenerInfo(
|
||||
URLPatterns patterns_,
|
||||
ResponseListener listener_)
|
||||
: url_patterns(patterns_), listener(listener_) {}
|
||||
AtomNetworkDelegate::ResponseListenerInfo::ResponseListenerInfo() = default;
|
||||
AtomNetworkDelegate::ResponseListenerInfo::~ResponseListenerInfo() = default;
|
||||
|
||||
AtomNetworkDelegate::AtomNetworkDelegate() {
|
||||
auto* command_line = base::CommandLine::ForCurrentProcess();
|
||||
if (command_line->HasSwitch(switches::kIgnoreConnectionsLimit)) {
|
||||
std::string value =
|
||||
command_line->GetSwitchValueASCII(switches::kIgnoreConnectionsLimit);
|
||||
ignore_connections_limit_domains_ = base::SplitString(
|
||||
value, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
AtomNetworkDelegate::~AtomNetworkDelegate() {}
|
||||
|
||||
void AtomNetworkDelegate::SetSimpleListenerInIO(SimpleEvent type,
|
||||
URLPatterns patterns,
|
||||
SimpleListener callback) {
|
||||
if (callback.is_null())
|
||||
simple_listeners_.erase(type);
|
||||
else
|
||||
simple_listeners_[type] = {std::move(patterns), std::move(callback)};
|
||||
}
|
||||
|
||||
void AtomNetworkDelegate::SetResponseListenerInIO(ResponseEvent type,
|
||||
URLPatterns patterns,
|
||||
ResponseListener callback) {
|
||||
if (callback.is_null())
|
||||
response_listeners_.erase(type);
|
||||
else
|
||||
response_listeners_[type] = {std::move(patterns), std::move(callback)};
|
||||
}
|
||||
|
||||
int AtomNetworkDelegate::OnBeforeURLRequest(
|
||||
net::URLRequest* request,
|
||||
net::CompletionOnceCallback callback,
|
||||
GURL* new_url) {
|
||||
if (!base::ContainsKey(response_listeners_, kOnBeforeRequest)) {
|
||||
for (const auto& domain : ignore_connections_limit_domains_) {
|
||||
if (request->url().DomainIs(domain)) {
|
||||
// Allow unlimited concurrent connections.
|
||||
request->SetPriority(net::MAXIMUM_PRIORITY);
|
||||
request->SetLoadFlags(request->load_flags() | net::LOAD_IGNORE_LIMITS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return net::OK;
|
||||
}
|
||||
|
||||
return HandleResponseEvent(kOnBeforeRequest, request, std::move(callback),
|
||||
new_url);
|
||||
}
|
||||
|
||||
int AtomNetworkDelegate::OnBeforeStartTransaction(
|
||||
net::URLRequest* request,
|
||||
net::CompletionOnceCallback callback,
|
||||
net::HttpRequestHeaders* headers) {
|
||||
if (!base::ContainsKey(response_listeners_, kOnBeforeSendHeaders))
|
||||
return net::OK;
|
||||
|
||||
return HandleResponseEvent(kOnBeforeSendHeaders, request, std::move(callback),
|
||||
headers, *headers);
|
||||
}
|
||||
|
||||
void AtomNetworkDelegate::OnStartTransaction(
|
||||
net::URLRequest* request,
|
||||
const net::HttpRequestHeaders& headers) {
|
||||
if (!base::ContainsKey(simple_listeners_, kOnSendHeaders))
|
||||
return;
|
||||
|
||||
HandleSimpleEvent(kOnSendHeaders, request, headers);
|
||||
}
|
||||
|
||||
int AtomNetworkDelegate::OnHeadersReceived(
|
||||
net::URLRequest* request,
|
||||
net::CompletionOnceCallback callback,
|
||||
const net::HttpResponseHeaders* original,
|
||||
scoped_refptr<net::HttpResponseHeaders>* override,
|
||||
GURL* allowed) {
|
||||
if (!base::ContainsKey(response_listeners_, kOnHeadersReceived))
|
||||
return net::OK;
|
||||
|
||||
return HandleResponseEvent(
|
||||
kOnHeadersReceived, request, std::move(callback),
|
||||
std::make_pair(override, original->GetStatusLine()), original);
|
||||
}
|
||||
|
||||
void AtomNetworkDelegate::OnBeforeRedirect(net::URLRequest* request,
|
||||
const GURL& new_location) {
|
||||
if (!base::ContainsKey(simple_listeners_, kOnBeforeRedirect))
|
||||
return;
|
||||
|
||||
HandleSimpleEvent(
|
||||
kOnBeforeRedirect, request, new_location, request->response_headers(),
|
||||
request->GetResponseRemoteEndpoint(), request->was_cached());
|
||||
}
|
||||
|
||||
void AtomNetworkDelegate::OnResponseStarted(net::URLRequest* request,
|
||||
int net_error) {
|
||||
if (!base::ContainsKey(simple_listeners_, kOnResponseStarted))
|
||||
return;
|
||||
|
||||
if (request->status().status() != net::URLRequestStatus::SUCCESS)
|
||||
return;
|
||||
|
||||
HandleSimpleEvent(kOnResponseStarted, request, request->response_headers(),
|
||||
request->was_cached());
|
||||
}
|
||||
|
||||
void AtomNetworkDelegate::OnCompleted(net::URLRequest* request,
|
||||
bool started,
|
||||
int net_error) {
|
||||
// OnCompleted may happen before other events.
|
||||
callbacks_.erase(request->identifier());
|
||||
|
||||
if (request->status().status() == net::URLRequestStatus::FAILED ||
|
||||
request->status().status() == net::URLRequestStatus::CANCELED) {
|
||||
// Error event.
|
||||
OnErrorOccurred(request, started, net_error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (request->response_headers() &&
|
||||
net::HttpResponseHeaders::IsRedirectResponseCode(
|
||||
request->response_headers()->response_code())) {
|
||||
// Redirect event.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!base::ContainsKey(simple_listeners_, kOnCompleted))
|
||||
return;
|
||||
|
||||
HandleSimpleEvent(kOnCompleted, request, request->response_headers(),
|
||||
request->was_cached());
|
||||
}
|
||||
|
||||
void AtomNetworkDelegate::OnURLRequestDestroyed(net::URLRequest* request) {
|
||||
const auto& it = login_handler_map_.find(request->identifier());
|
||||
if (it != login_handler_map_.end()) {
|
||||
it->second->NotifyRequestDestroyed();
|
||||
it->second = nullptr;
|
||||
login_handler_map_.erase(it);
|
||||
}
|
||||
callbacks_.erase(request->identifier());
|
||||
}
|
||||
|
||||
net::NetworkDelegate::AuthRequiredResponse AtomNetworkDelegate::OnAuthRequired(
|
||||
net::URLRequest* request,
|
||||
const net::AuthChallengeInfo& auth_info,
|
||||
AuthCallback callback,
|
||||
net::AuthCredentials* credentials) {
|
||||
auto* resource_request_info =
|
||||
content::ResourceRequestInfo::ForRequest(request);
|
||||
if (!resource_request_info)
|
||||
return AUTH_REQUIRED_RESPONSE_NO_ACTION;
|
||||
login_handler_map_.emplace(
|
||||
request->identifier(),
|
||||
new LoginHandler(request, auth_info, std::move(callback), credentials,
|
||||
resource_request_info));
|
||||
return AUTH_REQUIRED_RESPONSE_IO_PENDING;
|
||||
}
|
||||
|
||||
bool AtomNetworkDelegate::OnCanGetCookies(const net::URLRequest& request,
|
||||
const net::CookieList& cookie_list,
|
||||
bool allowed_from_caller) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AtomNetworkDelegate::OnCanSetCookie(
|
||||
const net::URLRequest& request,
|
||||
const net::CanonicalCookie& cookie_line,
|
||||
net::CookieOptions* options,
|
||||
bool allowed_from_caller) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AtomNetworkDelegate::OnCanAccessFile(
|
||||
const net::URLRequest& request,
|
||||
const base::FilePath& original_path,
|
||||
const base::FilePath& absolute_path) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AtomNetworkDelegate::OnForcePrivacyMode(
|
||||
const GURL& url,
|
||||
const GURL& first_party_for_cookies) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AtomNetworkDelegate::OnCancelURLRequestWithPolicyViolatingReferrerHeader(
|
||||
const net::URLRequest& request,
|
||||
const GURL& target_url,
|
||||
const GURL& referrer_url) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AtomNetworkDelegate::OnCanQueueReportingReport(
|
||||
const url::Origin& origin) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void AtomNetworkDelegate::OnCanSendReportingReports(
|
||||
std::set<url::Origin> origins,
|
||||
base::OnceCallback<void(std::set<url::Origin>)> result_callback) const {
|
||||
std::move(result_callback).Run(std::move(origins));
|
||||
}
|
||||
|
||||
bool AtomNetworkDelegate::OnCanSetReportingClient(const url::Origin& origin,
|
||||
const GURL& endpoint) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AtomNetworkDelegate::OnCanUseReportingClient(const url::Origin& origin,
|
||||
const GURL& endpoint) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void AtomNetworkDelegate::OnErrorOccurred(net::URLRequest* request,
|
||||
bool started,
|
||||
int net_error) {
|
||||
if (!base::ContainsKey(simple_listeners_, kOnErrorOccurred))
|
||||
return;
|
||||
|
||||
HandleSimpleEvent(kOnErrorOccurred, request, request->was_cached(),
|
||||
request->status());
|
||||
}
|
||||
|
||||
template <typename Out, typename... Args>
|
||||
int AtomNetworkDelegate::HandleResponseEvent(
|
||||
ResponseEvent type,
|
||||
net::URLRequest* request,
|
||||
net::CompletionOnceCallback callback,
|
||||
Out out,
|
||||
Args... args) {
|
||||
const auto& info = response_listeners_[type];
|
||||
if (!MatchesFilterCondition(request, info.url_patterns))
|
||||
return net::OK;
|
||||
|
||||
auto details = std::make_unique<base::DictionaryValue>();
|
||||
FillDetailsObject(details.get(), request, args...);
|
||||
|
||||
int render_process_id, render_frame_id;
|
||||
content::ResourceRequestInfo::GetRenderFrameForRequest(
|
||||
request, &render_process_id, &render_frame_id);
|
||||
|
||||
// The |request| could be destroyed before the |callback| is called.
|
||||
callbacks_[request->identifier()] = std::move(callback);
|
||||
|
||||
ResponseCallback response =
|
||||
base::BindOnce(&AtomNetworkDelegate::OnListenerResultInUI<Out>,
|
||||
base::Unretained(this), request->identifier(), out);
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {BrowserThread::UI},
|
||||
base::BindOnce(RunResponseListener, info.listener, std::move(details),
|
||||
render_process_id, render_frame_id, std::move(response)));
|
||||
return net::ERR_IO_PENDING;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AtomNetworkDelegate::HandleSimpleEvent(SimpleEvent type,
|
||||
net::URLRequest* request,
|
||||
Args... args) {
|
||||
const auto& info = simple_listeners_[type];
|
||||
if (!MatchesFilterCondition(request, info.url_patterns))
|
||||
return;
|
||||
|
||||
auto details = std::make_unique<base::DictionaryValue>();
|
||||
FillDetailsObject(details.get(), request, args...);
|
||||
|
||||
int render_process_id, render_frame_id;
|
||||
content::ResourceRequestInfo::GetRenderFrameForRequest(
|
||||
request, &render_process_id, &render_frame_id);
|
||||
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {BrowserThread::UI},
|
||||
base::BindOnce(RunSimpleListener, info.listener, std::move(details),
|
||||
render_process_id, render_frame_id));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void AtomNetworkDelegate::OnListenerResultInIO(
|
||||
uint64_t id,
|
||||
T out,
|
||||
std::unique_ptr<base::DictionaryValue> response) {
|
||||
// The request has been destroyed.
|
||||
if (!base::ContainsKey(callbacks_, id))
|
||||
return;
|
||||
|
||||
ReadFromResponseObject(*response, out);
|
||||
|
||||
bool cancel = false;
|
||||
response->GetBoolean("cancel", &cancel);
|
||||
std::move(callbacks_[id]).Run(cancel ? net::ERR_BLOCKED_BY_CLIENT : net::OK);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void AtomNetworkDelegate::OnListenerResultInUI(
|
||||
uint64_t id,
|
||||
T out,
|
||||
const base::DictionaryValue& response) {
|
||||
auto copy = base::DictionaryValue::From(
|
||||
base::Value::ToUniquePtrValue(response.Clone()));
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {BrowserThread::IO},
|
||||
base::BindOnce(&AtomNetworkDelegate::OnListenerResultInIO<T>,
|
||||
base::Unretained(this), id, out, std::move(copy)));
|
||||
}
|
||||
|
||||
} // namespace atom
|
183
shell/browser/net/atom_network_delegate.h
Normal file
183
shell/browser/net/atom_network_delegate.h
Normal file
|
@ -0,0 +1,183 @@
|
|||
// Copyright (c) 2015 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_ATOM_NETWORK_DELEGATE_H_
|
||||
#define ATOM_BROWSER_NET_ATOM_NETWORK_DELEGATE_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
#include "base/values.h"
|
||||
#include "content/public/browser/resource_request_info.h"
|
||||
#include "extensions/common/url_pattern.h"
|
||||
#include "net/base/network_delegate.h"
|
||||
#include "net/http/http_request_headers.h"
|
||||
#include "net/http/http_response_headers.h"
|
||||
|
||||
class URLPattern;
|
||||
|
||||
namespace atom {
|
||||
|
||||
using URLPatterns = std::set<URLPattern>;
|
||||
|
||||
const char* ResourceTypeToString(content::ResourceType type);
|
||||
|
||||
class LoginHandler;
|
||||
|
||||
class AtomNetworkDelegate : public net::NetworkDelegate {
|
||||
public:
|
||||
using ResponseCallback =
|
||||
base::OnceCallback<void(const base::DictionaryValue&)>;
|
||||
using SimpleListener =
|
||||
base::RepeatingCallback<void(const base::DictionaryValue&)>;
|
||||
using ResponseListener =
|
||||
base::RepeatingCallback<void(const base::DictionaryValue&,
|
||||
ResponseCallback)>;
|
||||
|
||||
enum SimpleEvent {
|
||||
kOnSendHeaders,
|
||||
kOnBeforeRedirect,
|
||||
kOnResponseStarted,
|
||||
kOnCompleted,
|
||||
kOnErrorOccurred,
|
||||
};
|
||||
|
||||
enum ResponseEvent {
|
||||
kOnBeforeRequest,
|
||||
kOnBeforeSendHeaders,
|
||||
kOnHeadersReceived,
|
||||
};
|
||||
|
||||
struct SimpleListenerInfo {
|
||||
URLPatterns url_patterns;
|
||||
SimpleListener listener;
|
||||
|
||||
SimpleListenerInfo(URLPatterns, SimpleListener);
|
||||
SimpleListenerInfo();
|
||||
~SimpleListenerInfo();
|
||||
};
|
||||
|
||||
struct ResponseListenerInfo {
|
||||
URLPatterns url_patterns;
|
||||
ResponseListener listener;
|
||||
|
||||
ResponseListenerInfo(URLPatterns, ResponseListener);
|
||||
ResponseListenerInfo();
|
||||
~ResponseListenerInfo();
|
||||
};
|
||||
|
||||
AtomNetworkDelegate();
|
||||
~AtomNetworkDelegate() override;
|
||||
|
||||
void SetSimpleListenerInIO(SimpleEvent type,
|
||||
URLPatterns patterns,
|
||||
SimpleListener callback);
|
||||
void SetResponseListenerInIO(ResponseEvent type,
|
||||
URLPatterns patterns,
|
||||
ResponseListener callback);
|
||||
|
||||
protected:
|
||||
// net::NetworkDelegate:
|
||||
int OnBeforeURLRequest(net::URLRequest* request,
|
||||
net::CompletionOnceCallback callback,
|
||||
GURL* new_url) override;
|
||||
int OnBeforeStartTransaction(net::URLRequest* request,
|
||||
net::CompletionOnceCallback callback,
|
||||
net::HttpRequestHeaders* headers) override;
|
||||
void OnBeforeSendHeaders(net::URLRequest* request,
|
||||
const net::ProxyInfo& proxy_info,
|
||||
const net::ProxyRetryInfoMap& proxy_retry_info,
|
||||
net::HttpRequestHeaders* headers) override {}
|
||||
void OnStartTransaction(net::URLRequest* request,
|
||||
const net::HttpRequestHeaders& headers) override;
|
||||
int OnHeadersReceived(
|
||||
net::URLRequest* request,
|
||||
net::CompletionOnceCallback callback,
|
||||
const net::HttpResponseHeaders* original_response_headers,
|
||||
scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
|
||||
GURL* allowed_unsafe_redirect_url) override;
|
||||
void OnBeforeRedirect(net::URLRequest* request,
|
||||
const GURL& new_location) override;
|
||||
void OnResponseStarted(net::URLRequest* request, int net_error) override;
|
||||
void OnNetworkBytesReceived(net::URLRequest* request,
|
||||
int64_t bytes_read) override {}
|
||||
void OnNetworkBytesSent(net::URLRequest* request,
|
||||
int64_t bytes_sent) override {}
|
||||
void OnCompleted(net::URLRequest* request,
|
||||
bool started,
|
||||
int net_error) override;
|
||||
void OnURLRequestDestroyed(net::URLRequest* request) override;
|
||||
void OnPACScriptError(int line_number, const base::string16& error) override {
|
||||
}
|
||||
AuthRequiredResponse OnAuthRequired(
|
||||
net::URLRequest* request,
|
||||
const net::AuthChallengeInfo& auth_info,
|
||||
AuthCallback callback,
|
||||
net::AuthCredentials* credentials) override;
|
||||
bool OnCanGetCookies(const net::URLRequest& request,
|
||||
const net::CookieList& cookie_list,
|
||||
bool allowed_from_caller) override;
|
||||
bool OnCanSetCookie(const net::URLRequest& request,
|
||||
const net::CanonicalCookie& cookie_line,
|
||||
net::CookieOptions* options,
|
||||
bool allowed_from_caller) override;
|
||||
bool OnCanAccessFile(const net::URLRequest& request,
|
||||
const base::FilePath& original_path,
|
||||
const base::FilePath& absolute_path) const override;
|
||||
bool OnForcePrivacyMode(const GURL& url,
|
||||
const GURL& first_party_for_cookies) const override;
|
||||
bool OnCancelURLRequestWithPolicyViolatingReferrerHeader(
|
||||
const net::URLRequest& request,
|
||||
const GURL& target_url,
|
||||
const GURL& referrer_url) const override;
|
||||
bool OnCanQueueReportingReport(const url::Origin& origin) const override;
|
||||
void OnCanSendReportingReports(std::set<url::Origin> origins,
|
||||
base::OnceCallback<void(std::set<url::Origin>)>
|
||||
result_callback) const override;
|
||||
bool OnCanSetReportingClient(const url::Origin& origin,
|
||||
const GURL& endpoint) const override;
|
||||
bool OnCanUseReportingClient(const url::Origin& origin,
|
||||
const GURL& endpoint) const override;
|
||||
|
||||
private:
|
||||
void OnErrorOccurred(net::URLRequest* request, bool started, int net_error);
|
||||
|
||||
template <typename... Args>
|
||||
void HandleSimpleEvent(SimpleEvent type,
|
||||
net::URLRequest* request,
|
||||
Args... args);
|
||||
template <typename Out, typename... Args>
|
||||
int HandleResponseEvent(ResponseEvent type,
|
||||
net::URLRequest* request,
|
||||
net::CompletionOnceCallback callback,
|
||||
Out out,
|
||||
Args... args);
|
||||
|
||||
// Deal with the results of Listener.
|
||||
template <typename T>
|
||||
void OnListenerResultInIO(uint64_t id,
|
||||
T out,
|
||||
std::unique_ptr<base::DictionaryValue> response);
|
||||
template <typename T>
|
||||
void OnListenerResultInUI(uint64_t id,
|
||||
T out,
|
||||
const base::DictionaryValue& response);
|
||||
|
||||
std::map<uint64_t, scoped_refptr<LoginHandler>> login_handler_map_;
|
||||
std::map<SimpleEvent, SimpleListenerInfo> simple_listeners_;
|
||||
std::map<ResponseEvent, ResponseListenerInfo> response_listeners_;
|
||||
std::map<uint64_t, net::CompletionOnceCallback> callbacks_;
|
||||
std::vector<std::string> ignore_connections_limit_domains_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AtomNetworkDelegate);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_ATOM_NETWORK_DELEGATE_H_
|
481
shell/browser/net/atom_url_loader_factory.cc
Normal file
481
shell/browser/net/atom_url_loader_factory.cc
Normal file
|
@ -0,0 +1,481 @@
|
|||
// Copyright (c) 2019 GitHub, Inc.
|
||||
// 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_loader_factory.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "atom/browser/api/atom_api_session.h"
|
||||
#include "atom/browser/atom_browser_context.h"
|
||||
#include "atom/browser/net/asar/asar_url_loader.h"
|
||||
#include "atom/browser/net/node_stream_loader.h"
|
||||
#include "atom/browser/net/url_pipe_loader.h"
|
||||
#include "atom/common/atom_constants.h"
|
||||
#include "atom/common/native_mate_converters/file_path_converter.h"
|
||||
#include "atom/common/native_mate_converters/gurl_converter.h"
|
||||
#include "atom/common/native_mate_converters/net_converter.h"
|
||||
#include "atom/common/native_mate_converters/value_converter.h"
|
||||
#include "base/guid.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/storage_partition.h"
|
||||
#include "mojo/public/cpp/system/string_data_pipe_producer.h"
|
||||
#include "net/base/filename_util.h"
|
||||
#include "net/http/http_status_code.h"
|
||||
#include "services/network/public/cpp/url_loader_completion_status.h"
|
||||
#include "services/network/public/mojom/url_loader_factory.mojom.h"
|
||||
|
||||
#include "atom/common/node_includes.h"
|
||||
|
||||
using content::BrowserThread;
|
||||
|
||||
namespace mate {
|
||||
|
||||
template <>
|
||||
struct Converter<atom::ProtocolType> {
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
atom::ProtocolType* out) {
|
||||
std::string type;
|
||||
if (!ConvertFromV8(isolate, val, &type))
|
||||
return false;
|
||||
if (type == "buffer")
|
||||
*out = atom::ProtocolType::kBuffer;
|
||||
else if (type == "string")
|
||||
*out = atom::ProtocolType::kString;
|
||||
else if (type == "file")
|
||||
*out = atom::ProtocolType::kFile;
|
||||
else if (type == "http")
|
||||
*out = atom::ProtocolType::kHttp;
|
||||
else if (type == "stream")
|
||||
*out = atom::ProtocolType::kStream;
|
||||
else // note "free" is internal type, not allowed to be passed from user
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mate
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
// Determine whether a protocol type can accept non-object response.
|
||||
bool ResponseMustBeObject(ProtocolType type) {
|
||||
switch (type) {
|
||||
case ProtocolType::kString:
|
||||
case ProtocolType::kFile:
|
||||
case ProtocolType::kFree:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to convert value to Dictionary.
|
||||
mate::Dictionary ToDict(v8::Isolate* isolate, v8::Local<v8::Value> value) {
|
||||
if (value->IsObject())
|
||||
return mate::Dictionary(
|
||||
isolate,
|
||||
value->ToObject(isolate->GetCurrentContext()).ToLocalChecked());
|
||||
else
|
||||
return mate::Dictionary();
|
||||
}
|
||||
|
||||
// Parse headers from response object.
|
||||
network::ResourceResponseHead ToResponseHead(const mate::Dictionary& dict) {
|
||||
network::ResourceResponseHead head;
|
||||
head.mime_type = "text/html";
|
||||
head.charset = "utf-8";
|
||||
if (dict.IsEmpty()) {
|
||||
head.headers = new net::HttpResponseHeaders("HTTP/1.1 200 OK");
|
||||
return head;
|
||||
}
|
||||
|
||||
int status_code = 200;
|
||||
dict.Get("statusCode", &status_code);
|
||||
head.headers = new net::HttpResponseHeaders(base::StringPrintf(
|
||||
"HTTP/1.1 %d %s", status_code,
|
||||
net::GetHttpReasonPhrase(static_cast<net::HttpStatusCode>(status_code))));
|
||||
|
||||
dict.Get("charset", &head.charset);
|
||||
bool has_mime_type = dict.Get("mimeType", &head.mime_type);
|
||||
bool has_content_type = false;
|
||||
|
||||
base::DictionaryValue headers;
|
||||
if (dict.Get("headers", &headers)) {
|
||||
for (const auto& iter : headers.DictItems()) {
|
||||
if (iter.second.is_string()) {
|
||||
// key: value
|
||||
head.headers->AddHeader(iter.first + ": " + iter.second.GetString());
|
||||
} else if (iter.second.is_list()) {
|
||||
// key: [values...]
|
||||
for (const auto& item : iter.second.GetList()) {
|
||||
if (item.is_string())
|
||||
head.headers->AddHeader(iter.first + ": " + item.GetString());
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
// Some apps are passing content-type via headers, which is not accepted
|
||||
// in NetworkService.
|
||||
if (base::ToLowerASCII(iter.first) == "content-type" &&
|
||||
iter.second.is_string()) {
|
||||
head.mime_type = iter.second.GetString();
|
||||
has_content_type = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setting |head.mime_type| does not automatically set the "content-type"
|
||||
// header in NetworkService.
|
||||
if (has_mime_type && !has_content_type)
|
||||
head.headers->AddHeader("content-type: " + head.mime_type);
|
||||
return head;
|
||||
}
|
||||
|
||||
// Helper to write string to pipe.
|
||||
struct WriteData {
|
||||
network::mojom::URLLoaderClientPtr client;
|
||||
std::string data;
|
||||
std::unique_ptr<mojo::StringDataPipeProducer> producer;
|
||||
};
|
||||
|
||||
void OnWrite(std::unique_ptr<WriteData> write_data, MojoResult result) {
|
||||
if (result != MOJO_RESULT_OK) {
|
||||
network::URLLoaderCompletionStatus status(net::ERR_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
network::URLLoaderCompletionStatus status(net::OK);
|
||||
status.encoded_data_length = write_data->data.size();
|
||||
status.encoded_body_length = write_data->data.size();
|
||||
status.decoded_body_length = write_data->data.size();
|
||||
write_data->client->OnComplete(status);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AtomURLLoaderFactory::AtomURLLoaderFactory(ProtocolType type,
|
||||
const ProtocolHandler& handler)
|
||||
: type_(type), handler_(handler) {}
|
||||
|
||||
AtomURLLoaderFactory::~AtomURLLoaderFactory() = default;
|
||||
|
||||
void AtomURLLoaderFactory::CreateLoaderAndStart(
|
||||
network::mojom::URLLoaderRequest loader,
|
||||
int32_t routing_id,
|
||||
int32_t request_id,
|
||||
uint32_t options,
|
||||
const network::ResourceRequest& request,
|
||||
network::mojom::URLLoaderClientPtr client,
|
||||
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
handler_.Run(
|
||||
request,
|
||||
base::BindOnce(&AtomURLLoaderFactory::StartLoading, std::move(loader),
|
||||
routing_id, request_id, options, request,
|
||||
std::move(client), traffic_annotation, nullptr, type_));
|
||||
}
|
||||
|
||||
void AtomURLLoaderFactory::Clone(
|
||||
network::mojom::URLLoaderFactoryRequest request) {
|
||||
bindings_.AddBinding(this, std::move(request));
|
||||
}
|
||||
|
||||
// static
|
||||
void AtomURLLoaderFactory::StartLoading(
|
||||
network::mojom::URLLoaderRequest loader,
|
||||
int32_t routing_id,
|
||||
int32_t request_id,
|
||||
uint32_t options,
|
||||
const network::ResourceRequest& request,
|
||||
network::mojom::URLLoaderClientPtr client,
|
||||
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
|
||||
network::mojom::URLLoaderFactory* proxy_factory,
|
||||
ProtocolType type,
|
||||
mate::Arguments* args) {
|
||||
// Send network error when there is no argument passed.
|
||||
//
|
||||
// Note that we should not throw JS error in the callback no matter what is
|
||||
// passed, to keep compatibility with old code.
|
||||
v8::Local<v8::Value> response;
|
||||
if (!args->GetNext(&response)) {
|
||||
client->OnComplete(
|
||||
network::URLLoaderCompletionStatus(net::ERR_NOT_IMPLEMENTED));
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse {error} object.
|
||||
mate::Dictionary dict = ToDict(args->isolate(), response);
|
||||
if (!dict.IsEmpty()) {
|
||||
int error_code;
|
||||
if (dict.Get("error", &error_code)) {
|
||||
client->OnComplete(network::URLLoaderCompletionStatus(error_code));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
network::ResourceResponseHead head = ToResponseHead(dict);
|
||||
|
||||
// Handle redirection.
|
||||
//
|
||||
// Note that with NetworkService, sending the "Location" header no longer
|
||||
// automatically redirects the request, we have explicitly create a new loader
|
||||
// to implement redirection. This is also what Chromium does with WebRequest
|
||||
// API in WebRequestProxyingURLLoaderFactory.
|
||||
std::string location;
|
||||
if (head.headers->IsRedirect(&location)) {
|
||||
network::ResourceRequest new_request = request;
|
||||
new_request.url = GURL(location);
|
||||
// When the redirection comes from an intercepted scheme (which has
|
||||
// |proxy_factory| passed), we askes the proxy factory to create a loader
|
||||
// for new URL, otherwise we call |StartLoadingHttp|, which creates
|
||||
// loader with default factory.
|
||||
//
|
||||
// Note that when handling requests for intercepted scheme, creating loader
|
||||
// with default factory (i.e. calling StartLoadingHttp) would bypass the
|
||||
// ProxyingURLLoaderFactory, we have to explicitly use the proxy factory to
|
||||
// create loader so it is possible to have handlers of intercepted scheme
|
||||
// getting called recursively, which is a behavior expected in protocol
|
||||
// module.
|
||||
//
|
||||
// I'm not sure whether this is an intended behavior in Chromium.
|
||||
if (proxy_factory) {
|
||||
proxy_factory->CreateLoaderAndStart(
|
||||
std::move(loader), routing_id, request_id, options, new_request,
|
||||
std::move(client), traffic_annotation);
|
||||
} else {
|
||||
StartLoadingHttp(std::move(loader), new_request, std::move(client),
|
||||
traffic_annotation,
|
||||
mate::Dictionary::CreateEmpty(args->isolate()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Some protocol accepts non-object responses.
|
||||
if (dict.IsEmpty() && ResponseMustBeObject(type)) {
|
||||
client->OnComplete(
|
||||
network::URLLoaderCompletionStatus(net::ERR_NOT_IMPLEMENTED));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case ProtocolType::kBuffer:
|
||||
StartLoadingBuffer(std::move(client), std::move(head), dict);
|
||||
break;
|
||||
case ProtocolType::kString:
|
||||
StartLoadingString(std::move(client), std::move(head), dict,
|
||||
args->isolate(), response);
|
||||
break;
|
||||
case ProtocolType::kFile:
|
||||
StartLoadingFile(std::move(loader), request, std::move(client),
|
||||
std::move(head), dict, args->isolate(), response);
|
||||
break;
|
||||
case ProtocolType::kHttp:
|
||||
StartLoadingHttp(std::move(loader), request, std::move(client),
|
||||
traffic_annotation, dict);
|
||||
break;
|
||||
case ProtocolType::kStream:
|
||||
StartLoadingStream(std::move(loader), std::move(client), std::move(head),
|
||||
dict);
|
||||
break;
|
||||
case ProtocolType::kFree:
|
||||
ProtocolType type;
|
||||
if (!mate::ConvertFromV8(args->isolate(), response, &type)) {
|
||||
client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED));
|
||||
return;
|
||||
}
|
||||
StartLoading(std::move(loader), routing_id, request_id, options, request,
|
||||
std::move(client), traffic_annotation, proxy_factory, type,
|
||||
args);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void AtomURLLoaderFactory::StartLoadingBuffer(
|
||||
network::mojom::URLLoaderClientPtr client,
|
||||
network::ResourceResponseHead head,
|
||||
const mate::Dictionary& dict) {
|
||||
v8::Local<v8::Value> buffer = dict.GetHandle();
|
||||
dict.Get("data", &buffer);
|
||||
if (!node::Buffer::HasInstance(buffer)) {
|
||||
client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED));
|
||||
return;
|
||||
}
|
||||
|
||||
SendContents(
|
||||
std::move(client), std::move(head),
|
||||
std::string(node::Buffer::Data(buffer), node::Buffer::Length(buffer)));
|
||||
}
|
||||
|
||||
// static
|
||||
void AtomURLLoaderFactory::StartLoadingString(
|
||||
network::mojom::URLLoaderClientPtr client,
|
||||
network::ResourceResponseHead head,
|
||||
const mate::Dictionary& dict,
|
||||
v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> response) {
|
||||
std::string contents;
|
||||
if (response->IsString())
|
||||
contents = gin::V8ToString(isolate, response);
|
||||
else if (!dict.IsEmpty())
|
||||
dict.Get("data", &contents);
|
||||
|
||||
SendContents(std::move(client), std::move(head), std::move(contents));
|
||||
}
|
||||
|
||||
// static
|
||||
void AtomURLLoaderFactory::StartLoadingFile(
|
||||
network::mojom::URLLoaderRequest loader,
|
||||
network::ResourceRequest request,
|
||||
network::mojom::URLLoaderClientPtr client,
|
||||
network::ResourceResponseHead head,
|
||||
const mate::Dictionary& dict,
|
||||
v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> response) {
|
||||
base::FilePath path;
|
||||
if (mate::ConvertFromV8(isolate, response, &path)) {
|
||||
request.url = net::FilePathToFileURL(path);
|
||||
} else if (!dict.IsEmpty()) {
|
||||
dict.Get("referrer", &request.referrer);
|
||||
dict.Get("method", &request.method);
|
||||
if (dict.Get("path", &path))
|
||||
request.url = net::FilePathToFileURL(path);
|
||||
} else {
|
||||
client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED));
|
||||
return;
|
||||
}
|
||||
|
||||
head.headers->AddHeader(kCORSHeader);
|
||||
asar::CreateAsarURLLoader(request, std::move(loader), std::move(client),
|
||||
head.headers);
|
||||
}
|
||||
|
||||
// static
|
||||
void AtomURLLoaderFactory::StartLoadingHttp(
|
||||
network::mojom::URLLoaderRequest loader,
|
||||
const network::ResourceRequest& original_request,
|
||||
network::mojom::URLLoaderClientPtr client,
|
||||
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
|
||||
const mate::Dictionary& dict) {
|
||||
auto request = std::make_unique<network::ResourceRequest>();
|
||||
request->headers = original_request.headers;
|
||||
request->cors_exempt_headers = original_request.cors_exempt_headers;
|
||||
|
||||
dict.Get("url", &request->url);
|
||||
dict.Get("referrer", &request->referrer);
|
||||
if (!dict.Get("method", &request->method))
|
||||
request->method = original_request.method;
|
||||
|
||||
base::DictionaryValue upload_data;
|
||||
if (request->method != "GET" && request->method != "HEAD")
|
||||
dict.Get("uploadData", &upload_data);
|
||||
|
||||
scoped_refptr<AtomBrowserContext> browser_context =
|
||||
AtomBrowserContext::From("", false);
|
||||
v8::Local<v8::Value> value;
|
||||
if (dict.Get("session", &value)) {
|
||||
if (value->IsNull()) {
|
||||
browser_context = AtomBrowserContext::From(base::GenerateGUID(), true);
|
||||
} else {
|
||||
mate::Handle<api::Session> session;
|
||||
if (mate::ConvertFromV8(dict.isolate(), value, &session) &&
|
||||
!session.IsEmpty()) {
|
||||
browser_context = session->browser_context();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory =
|
||||
content::BrowserContext::GetDefaultStoragePartition(browser_context.get())
|
||||
->GetURLLoaderFactoryForBrowserProcess();
|
||||
new URLPipeLoader(
|
||||
url_loader_factory, std::move(request), std::move(loader),
|
||||
std::move(client),
|
||||
static_cast<net::NetworkTrafficAnnotationTag>(traffic_annotation),
|
||||
std::move(upload_data));
|
||||
}
|
||||
|
||||
// static
|
||||
void AtomURLLoaderFactory::StartLoadingStream(
|
||||
network::mojom::URLLoaderRequest loader,
|
||||
network::mojom::URLLoaderClientPtr client,
|
||||
network::ResourceResponseHead head,
|
||||
const mate::Dictionary& dict) {
|
||||
v8::Local<v8::Value> stream;
|
||||
if (!dict.Get("data", &stream)) {
|
||||
// Assume the opts is already a stream.
|
||||
stream = dict.GetHandle();
|
||||
} else if (stream->IsNullOrUndefined()) {
|
||||
// "data" was explicitly passed as null or undefined, assume the user wants
|
||||
// to send an empty body.
|
||||
//
|
||||
// Note that We must submit a empty body otherwise NetworkService would
|
||||
// crash.
|
||||
client->OnReceiveResponse(head);
|
||||
mojo::ScopedDataPipeProducerHandle producer;
|
||||
mojo::ScopedDataPipeConsumerHandle consumer;
|
||||
if (mojo::CreateDataPipe(nullptr, &producer, &consumer) != MOJO_RESULT_OK) {
|
||||
client->OnComplete(
|
||||
network::URLLoaderCompletionStatus(net::ERR_INSUFFICIENT_RESOURCES));
|
||||
return;
|
||||
}
|
||||
producer.reset(); // The data pipe is empty.
|
||||
client->OnStartLoadingResponseBody(std::move(consumer));
|
||||
client->OnComplete(network::URLLoaderCompletionStatus(net::OK));
|
||||
return;
|
||||
} else if (!stream->IsObject()) {
|
||||
client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED));
|
||||
return;
|
||||
}
|
||||
|
||||
mate::Dictionary data = ToDict(dict.isolate(), stream);
|
||||
v8::Local<v8::Value> method;
|
||||
if (!data.Get("on", &method) || !method->IsFunction() ||
|
||||
!data.Get("removeListener", &method) || !method->IsFunction()) {
|
||||
client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED));
|
||||
return;
|
||||
}
|
||||
|
||||
new NodeStreamLoader(std::move(head), std::move(loader), std::move(client),
|
||||
data.isolate(), data.GetHandle());
|
||||
}
|
||||
|
||||
// static
|
||||
void AtomURLLoaderFactory::SendContents(
|
||||
network::mojom::URLLoaderClientPtr client,
|
||||
network::ResourceResponseHead head,
|
||||
std::string data) {
|
||||
head.headers->AddHeader(kCORSHeader);
|
||||
client->OnReceiveResponse(head);
|
||||
|
||||
// Code bellow follows the pattern of data_url_loader_factory.cc.
|
||||
mojo::ScopedDataPipeProducerHandle producer;
|
||||
mojo::ScopedDataPipeConsumerHandle consumer;
|
||||
if (mojo::CreateDataPipe(nullptr, &producer, &consumer) != MOJO_RESULT_OK) {
|
||||
client->OnComplete(
|
||||
network::URLLoaderCompletionStatus(net::ERR_INSUFFICIENT_RESOURCES));
|
||||
return;
|
||||
}
|
||||
|
||||
client->OnStartLoadingResponseBody(std::move(consumer));
|
||||
|
||||
auto write_data = std::make_unique<WriteData>();
|
||||
write_data->client = std::move(client);
|
||||
write_data->data = std::move(data);
|
||||
write_data->producer =
|
||||
std::make_unique<mojo::StringDataPipeProducer>(std::move(producer));
|
||||
|
||||
base::StringPiece string_piece(write_data->data);
|
||||
write_data->producer->Write(string_piece,
|
||||
mojo::StringDataPipeProducer::AsyncWritingMode::
|
||||
STRING_STAYS_VALID_UNTIL_COMPLETION,
|
||||
base::BindOnce(OnWrite, std::move(write_data)));
|
||||
}
|
||||
|
||||
} // namespace atom
|
111
shell/browser/net/atom_url_loader_factory.h
Normal file
111
shell/browser/net/atom_url_loader_factory.h
Normal file
|
@ -0,0 +1,111 @@
|
|||
// Copyright (c) 2019 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_ATOM_URL_LOADER_FACTORY_H_
|
||||
#define ATOM_BROWSER_NET_ATOM_URL_LOADER_FACTORY_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "mojo/public/cpp/bindings/binding_set.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "net/url_request/url_request_job_factory.h"
|
||||
#include "services/network/public/mojom/url_loader_factory.mojom.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
// Old Protocol API can only serve one type of response for one scheme.
|
||||
enum class ProtocolType {
|
||||
kBuffer,
|
||||
kString,
|
||||
kFile,
|
||||
kHttp,
|
||||
kStream,
|
||||
kFree, // special type for returning arbitrary type of response.
|
||||
};
|
||||
|
||||
using StartLoadingCallback = base::OnceCallback<void(mate::Arguments*)>;
|
||||
using ProtocolHandler =
|
||||
base::Callback<void(const network::ResourceRequest&, StartLoadingCallback)>;
|
||||
|
||||
// scheme => (type, handler).
|
||||
using HandlersMap =
|
||||
std::map<std::string, std::pair<ProtocolType, ProtocolHandler>>;
|
||||
|
||||
// Implementation of URLLoaderFactory.
|
||||
class AtomURLLoaderFactory : public network::mojom::URLLoaderFactory {
|
||||
public:
|
||||
AtomURLLoaderFactory(ProtocolType type, const ProtocolHandler& handler);
|
||||
~AtomURLLoaderFactory() override;
|
||||
|
||||
// network::mojom::URLLoaderFactory:
|
||||
void CreateLoaderAndStart(network::mojom::URLLoaderRequest loader,
|
||||
int32_t routing_id,
|
||||
int32_t request_id,
|
||||
uint32_t options,
|
||||
const network::ResourceRequest& request,
|
||||
network::mojom::URLLoaderClientPtr client,
|
||||
const net::MutableNetworkTrafficAnnotationTag&
|
||||
traffic_annotation) override;
|
||||
void Clone(network::mojom::URLLoaderFactoryRequest request) override;
|
||||
|
||||
static void StartLoading(
|
||||
network::mojom::URLLoaderRequest loader,
|
||||
int32_t routing_id,
|
||||
int32_t request_id,
|
||||
uint32_t options,
|
||||
const network::ResourceRequest& request,
|
||||
network::mojom::URLLoaderClientPtr client,
|
||||
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
|
||||
network::mojom::URLLoaderFactory* proxy_factory,
|
||||
ProtocolType type,
|
||||
mate::Arguments* args);
|
||||
|
||||
private:
|
||||
static void StartLoadingBuffer(network::mojom::URLLoaderClientPtr client,
|
||||
network::ResourceResponseHead head,
|
||||
const mate::Dictionary& dict);
|
||||
static void StartLoadingString(network::mojom::URLLoaderClientPtr client,
|
||||
network::ResourceResponseHead head,
|
||||
const mate::Dictionary& dict,
|
||||
v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> response);
|
||||
static void StartLoadingFile(network::mojom::URLLoaderRequest loader,
|
||||
network::ResourceRequest request,
|
||||
network::mojom::URLLoaderClientPtr client,
|
||||
network::ResourceResponseHead head,
|
||||
const mate::Dictionary& dict,
|
||||
v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> response);
|
||||
static void StartLoadingHttp(
|
||||
network::mojom::URLLoaderRequest loader,
|
||||
const network::ResourceRequest& original_request,
|
||||
network::mojom::URLLoaderClientPtr client,
|
||||
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
|
||||
const mate::Dictionary& dict);
|
||||
static void StartLoadingStream(network::mojom::URLLoaderRequest loader,
|
||||
network::mojom::URLLoaderClientPtr client,
|
||||
network::ResourceResponseHead head,
|
||||
const mate::Dictionary& dict);
|
||||
|
||||
// Helper to send string as response.
|
||||
static void SendContents(network::mojom::URLLoaderClientPtr client,
|
||||
network::ResourceResponseHead head,
|
||||
std::string data);
|
||||
|
||||
// TODO(zcbenz): This comes from extensions/browser/extension_protocols.cc
|
||||
// but I don't know what it actually does, find out the meanings of |Clone|
|
||||
// and |bindings_| and add comments for them.
|
||||
mojo::BindingSet<network::mojom::URLLoaderFactory> bindings_;
|
||||
|
||||
ProtocolType type_;
|
||||
ProtocolHandler handler_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AtomURLLoaderFactory);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_ATOM_URL_LOADER_FACTORY_H_
|
521
shell/browser/net/atom_url_request.cc
Normal file
521
shell/browser/net/atom_url_request.cc
Normal file
|
@ -0,0 +1,521 @@
|
|||
// Copyright (c) 2016 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 <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "atom/browser/api/atom_api_url_request.h"
|
||||
#include "atom/browser/atom_browser_context.h"
|
||||
#include "atom/browser/net/atom_url_request_job_factory.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/task/post_task.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "net/base/elements_upload_data_stream.h"
|
||||
#include "net/base/io_buffer.h"
|
||||
#include "net/base/load_flags.h"
|
||||
#include "net/base/upload_bytes_element_reader.h"
|
||||
#include "net/url_request/redirect_info.h"
|
||||
|
||||
namespace {
|
||||
const int kBufferSize = 4096;
|
||||
} // namespace
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace internal {
|
||||
|
||||
class UploadOwnedIOBufferElementReader : public net::UploadBytesElementReader {
|
||||
public:
|
||||
explicit UploadOwnedIOBufferElementReader(
|
||||
scoped_refptr<const net::IOBufferWithSize> buffer)
|
||||
: net::UploadBytesElementReader(buffer->data(), buffer->size()),
|
||||
buffer_(buffer) {}
|
||||
|
||||
~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);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
AtomURLRequest::AtomURLRequest(api::URLRequest* delegate)
|
||||
: delegate_(delegate),
|
||||
response_read_buffer_(new net::IOBuffer(kBufferSize)) {}
|
||||
|
||||
AtomURLRequest::~AtomURLRequest() {
|
||||
DCHECK(!request_context_getter_);
|
||||
DCHECK(!request_);
|
||||
}
|
||||
|
||||
scoped_refptr<AtomURLRequest> AtomURLRequest::Create(
|
||||
AtomBrowserContext* browser_context,
|
||||
const std::string& method,
|
||||
const std::string& url,
|
||||
const std::string& redirect_policy,
|
||||
api::URLRequest* delegate) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
|
||||
DCHECK(browser_context);
|
||||
DCHECK(!url.empty());
|
||||
DCHECK(delegate);
|
||||
if (!browser_context || url.empty() || !delegate) {
|
||||
return nullptr;
|
||||
}
|
||||
scoped_refptr<net::URLRequestContextGetter> request_context_getter(
|
||||
browser_context->GetRequestContext());
|
||||
DCHECK(request_context_getter);
|
||||
scoped_refptr<AtomURLRequest> atom_url_request(new AtomURLRequest(delegate));
|
||||
if (base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::IO},
|
||||
base::BindOnce(&AtomURLRequest::DoInitialize, atom_url_request,
|
||||
request_context_getter, method, url,
|
||||
redirect_policy))) {
|
||||
return atom_url_request;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AtomURLRequest::Terminate() {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
delegate_ = nullptr;
|
||||
base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO},
|
||||
base::BindOnce(&AtomURLRequest::DoTerminate, this));
|
||||
}
|
||||
|
||||
void AtomURLRequest::DoInitialize(
|
||||
scoped_refptr<net::URLRequestContextGetter> request_context_getter,
|
||||
const std::string& method,
|
||||
const std::string& url,
|
||||
const std::string& redirect_policy) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
DCHECK(request_context_getter);
|
||||
|
||||
redirect_policy_ = redirect_policy;
|
||||
request_context_getter_ = request_context_getter;
|
||||
request_context_getter_->AddObserver(this);
|
||||
auto* context = request_context_getter_->GetURLRequestContext();
|
||||
if (!context) {
|
||||
// Called after shutdown.
|
||||
DoCancelWithError("Cannot start a request after shutdown.", true);
|
||||
return;
|
||||
}
|
||||
|
||||
DCHECK(context);
|
||||
request_ = context->CreateRequest(
|
||||
GURL(url), net::RequestPriority::DEFAULT_PRIORITY, this);
|
||||
if (!request_) {
|
||||
DoCancelWithError("Failed to create a net::URLRequest.", true);
|
||||
return;
|
||||
}
|
||||
request_->set_method(method);
|
||||
// Do not send cookies from the cookie store.
|
||||
DoSetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES);
|
||||
// Set a flag to stop custom protocol from intercepting this request.
|
||||
request_->SetUserData(DisableProtocolInterceptFlagKey(),
|
||||
base::WrapUnique(new base::SupportsUserData::Data()));
|
||||
}
|
||||
|
||||
void AtomURLRequest::DoTerminate() {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
request_.reset();
|
||||
if (request_context_getter_) {
|
||||
request_context_getter_->RemoveObserver(this);
|
||||
request_context_getter_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool AtomURLRequest::Write(scoped_refptr<const net::IOBufferWithSize> buffer,
|
||||
bool is_last) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
return base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::IO},
|
||||
base::BindOnce(&AtomURLRequest::DoWriteBuffer, this, buffer, is_last));
|
||||
}
|
||||
|
||||
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_ = is_chunked_upload;
|
||||
}
|
||||
|
||||
void AtomURLRequest::Cancel() {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO},
|
||||
base::BindOnce(&AtomURLRequest::DoCancel, this));
|
||||
}
|
||||
|
||||
void AtomURLRequest::FollowRedirect() {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::IO},
|
||||
base::BindOnce(&AtomURLRequest::DoFollowRedirect, this));
|
||||
}
|
||||
|
||||
void AtomURLRequest::SetExtraHeader(const std::string& name,
|
||||
const std::string& value) const {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::IO},
|
||||
base::BindOnce(&AtomURLRequest::DoSetExtraHeader, this, name, value));
|
||||
}
|
||||
|
||||
void AtomURLRequest::RemoveExtraHeader(const std::string& name) const {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::IO},
|
||||
base::BindOnce(&AtomURLRequest::DoRemoveExtraHeader, this, name));
|
||||
}
|
||||
|
||||
void AtomURLRequest::PassLoginInformation(
|
||||
const base::string16& username,
|
||||
const base::string16& password) const {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
if (username.empty() || password.empty()) {
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::IO},
|
||||
base::BindOnce(&AtomURLRequest::DoCancelAuth, this));
|
||||
} else {
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::IO},
|
||||
base::BindOnce(&AtomURLRequest::DoSetAuth, this, username, password));
|
||||
}
|
||||
}
|
||||
|
||||
void AtomURLRequest::SetLoadFlags(int flags) const {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::IO},
|
||||
base::BindOnce(&AtomURLRequest::DoSetLoadFlags, this, flags));
|
||||
}
|
||||
|
||||
void AtomURLRequest::DoWriteBuffer(
|
||||
scoped_refptr<const net::IOBufferWithSize> buffer,
|
||||
bool is_last) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
if (!request_) {
|
||||
return;
|
||||
}
|
||||
|
||||
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.
|
||||
chunked_stream_writer_->AppendData(buffer->data(), buffer->size(),
|
||||
is_last);
|
||||
else if (is_last)
|
||||
// Empty buffer and last chunk, i.e. request.end().
|
||||
chunked_stream_writer_->AppendData(nullptr, 0, true);
|
||||
|
||||
if (first_call) {
|
||||
request_->Start();
|
||||
}
|
||||
} else {
|
||||
if (buffer) {
|
||||
// Handling potential empty buffers.
|
||||
using internal::UploadOwnedIOBufferElementReader;
|
||||
auto* element_reader =
|
||||
UploadOwnedIOBufferElementReader::CreateWithBuffer(std::move(buffer));
|
||||
upload_element_readers_.push_back(
|
||||
std::unique_ptr<net::UploadElementReader>(element_reader));
|
||||
}
|
||||
|
||||
if (is_last) {
|
||||
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));
|
||||
request_->Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AtomURLRequest::DoCancel() {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
if (request_) {
|
||||
request_->Cancel();
|
||||
}
|
||||
DoTerminate();
|
||||
}
|
||||
|
||||
void AtomURLRequest::DoFollowRedirect() {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
if (request_ && request_->is_redirecting() && redirect_policy_ == "manual") {
|
||||
request_->FollowDeferredRedirect(base::nullopt /* removed_headers */,
|
||||
base::nullopt /* modified_headers */);
|
||||
}
|
||||
}
|
||||
|
||||
void AtomURLRequest::DoSetExtraHeader(const std::string& name,
|
||||
const std::string& value) const {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
if (!request_) {
|
||||
return;
|
||||
}
|
||||
request_->SetExtraRequestHeaderByName(name, value, true);
|
||||
}
|
||||
|
||||
void AtomURLRequest::DoRemoveExtraHeader(const std::string& name) const {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
if (!request_) {
|
||||
return;
|
||||
}
|
||||
request_->RemoveRequestHeaderByName(name);
|
||||
}
|
||||
|
||||
void AtomURLRequest::DoSetAuth(const base::string16& username,
|
||||
const base::string16& password) const {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
if (!request_) {
|
||||
return;
|
||||
}
|
||||
request_->SetAuth(net::AuthCredentials(username, password));
|
||||
}
|
||||
|
||||
void AtomURLRequest::DoCancelAuth() const {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
if (!request_) {
|
||||
return;
|
||||
}
|
||||
request_->CancelAuth();
|
||||
}
|
||||
|
||||
void AtomURLRequest::DoCancelWithError(const std::string& error,
|
||||
bool isRequestError) {
|
||||
DoCancel();
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::UI},
|
||||
base::BindOnce(&AtomURLRequest::InformDelegateErrorOccured, this, error,
|
||||
isRequestError));
|
||||
}
|
||||
|
||||
void AtomURLRequest::DoSetLoadFlags(int flags) const {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
if (!request_) {
|
||||
return;
|
||||
}
|
||||
request_->SetLoadFlags(request_->load_flags() | flags);
|
||||
}
|
||||
|
||||
void AtomURLRequest::OnReceivedRedirect(net::URLRequest* request,
|
||||
const net::RedirectInfo& info,
|
||||
bool* defer_redirect) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
if (!request_ || redirect_policy_ == "follow")
|
||||
return;
|
||||
|
||||
if (redirect_policy_ == "error") {
|
||||
request->Cancel();
|
||||
DoCancelWithError(
|
||||
"Request cannot follow redirect with the current redirect mode", true);
|
||||
} else if (redirect_policy_ == "manual") {
|
||||
*defer_redirect = true;
|
||||
scoped_refptr<net::HttpResponseHeaders> response_headers =
|
||||
request->response_headers();
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::UI},
|
||||
base::BindOnce(&AtomURLRequest::InformDelegateReceivedRedirect, this,
|
||||
info.status_code, info.new_method, info.new_url,
|
||||
response_headers));
|
||||
}
|
||||
}
|
||||
|
||||
void AtomURLRequest::OnAuthRequired(net::URLRequest* request,
|
||||
const net::AuthChallengeInfo& auth_info) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::UI},
|
||||
base::BindOnce(&AtomURLRequest::InformDelegateAuthenticationRequired,
|
||||
this, auth_info));
|
||||
}
|
||||
|
||||
void AtomURLRequest::OnResponseStarted(net::URLRequest* request,
|
||||
int net_error) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
if (!request_) {
|
||||
return;
|
||||
}
|
||||
DCHECK_EQ(request, request_.get());
|
||||
|
||||
scoped_refptr<net::HttpResponseHeaders> response_headers =
|
||||
request->response_headers();
|
||||
const auto& status = request_->status();
|
||||
if (status.is_success()) {
|
||||
// Success or pending trigger a Read.
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::UI},
|
||||
base::BindOnce(&AtomURLRequest::InformDelegateResponseStarted, this,
|
||||
response_headers));
|
||||
ReadResponse();
|
||||
} else if (status.status() == net::URLRequestStatus::Status::FAILED) {
|
||||
// Report error on Start.
|
||||
DoCancelWithError(net::ErrorToString(net_error), true);
|
||||
}
|
||||
// We don't report an error is the request is canceled.
|
||||
}
|
||||
|
||||
void AtomURLRequest::ReadResponse() {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
|
||||
int bytes_read = -1;
|
||||
if (request_->Read(response_read_buffer_.get(), kBufferSize, &bytes_read)) {
|
||||
OnReadCompleted(request_.get(), bytes_read);
|
||||
}
|
||||
}
|
||||
|
||||
void AtomURLRequest::OnReadCompleted(net::URLRequest* request, int bytes_read) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
if (!request_) {
|
||||
return;
|
||||
}
|
||||
DCHECK_EQ(request, request_.get());
|
||||
|
||||
const auto status = request_->status();
|
||||
if (status.error() == bytes_read &&
|
||||
bytes_read == net::ERR_CONTENT_DECODING_INIT_FAILED) {
|
||||
// When the request job is unable to create a source stream for the
|
||||
// content encoding, we fail the request.
|
||||
DoCancelWithError(net::ErrorToString(net::ERR_CONTENT_DECODING_INIT_FAILED),
|
||||
true);
|
||||
return;
|
||||
}
|
||||
|
||||
bool response_error = false;
|
||||
bool data_ended = false;
|
||||
bool data_transfer_error = false;
|
||||
do {
|
||||
if (!status.is_success()) {
|
||||
response_error = true;
|
||||
break;
|
||||
}
|
||||
if (bytes_read == 0) {
|
||||
data_ended = true;
|
||||
break;
|
||||
}
|
||||
if (bytes_read < 0 || !CopyAndPostBuffer(bytes_read)) {
|
||||
data_transfer_error = true;
|
||||
break;
|
||||
}
|
||||
} while (
|
||||
request_->Read(response_read_buffer_.get(), kBufferSize, &bytes_read));
|
||||
if (response_error) {
|
||||
DoCancelWithError(net::ErrorToString(status.ToNetError()), false);
|
||||
} else if (data_ended) {
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::UI},
|
||||
base::BindOnce(&AtomURLRequest::InformDelegateResponseCompleted, this));
|
||||
DoTerminate();
|
||||
} else if (data_transfer_error) {
|
||||
// We abort the request on corrupted data transfer.
|
||||
DoCancelWithError("Failed to transfer data from IO to UI thread.", false);
|
||||
}
|
||||
}
|
||||
|
||||
void AtomURLRequest::OnContextShuttingDown() {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
DoCancel();
|
||||
}
|
||||
|
||||
bool AtomURLRequest::CopyAndPostBuffer(int bytes_read) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
|
||||
// data is only a wrapper for the asynchronous response_read_buffer_.
|
||||
// Make a deep copy of payload and transfer ownership to the UI thread.
|
||||
auto buffer_copy = WrapRefCounted(new net::IOBufferWithSize(bytes_read));
|
||||
memcpy(buffer_copy->data(), response_read_buffer_->data(), bytes_read);
|
||||
|
||||
return base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::UI},
|
||||
base::BindOnce(&AtomURLRequest::InformDelegateResponseData, this,
|
||||
buffer_copy));
|
||||
}
|
||||
|
||||
void AtomURLRequest::InformDelegateReceivedRedirect(
|
||||
int status_code,
|
||||
const std::string& method,
|
||||
const GURL& url,
|
||||
scoped_refptr<net::HttpResponseHeaders> response_headers) const {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
if (delegate_)
|
||||
delegate_->OnReceivedRedirect(status_code, method, url, response_headers);
|
||||
}
|
||||
|
||||
void AtomURLRequest::InformDelegateAuthenticationRequired(
|
||||
const net::AuthChallengeInfo& auth_info) const {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
if (delegate_)
|
||||
delegate_->OnAuthenticationRequired(auth_info);
|
||||
}
|
||||
|
||||
void AtomURLRequest::InformDelegateResponseStarted(
|
||||
scoped_refptr<net::HttpResponseHeaders> response_headers) const {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
if (delegate_)
|
||||
delegate_->OnResponseStarted(response_headers);
|
||||
}
|
||||
|
||||
void AtomURLRequest::InformDelegateResponseData(
|
||||
scoped_refptr<net::IOBufferWithSize> data) const {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
|
||||
// Transfer ownership of the data buffer, data will be released
|
||||
// by the delegate's OnResponseData.
|
||||
if (delegate_)
|
||||
delegate_->OnResponseData(data);
|
||||
}
|
||||
|
||||
void AtomURLRequest::InformDelegateResponseCompleted() const {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
|
||||
if (delegate_)
|
||||
delegate_->OnResponseCompleted();
|
||||
}
|
||||
|
||||
void AtomURLRequest::InformDelegateErrorOccured(const std::string& error,
|
||||
bool isRequestError) const {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
|
||||
if (delegate_)
|
||||
delegate_->OnError(error, isRequestError);
|
||||
}
|
||||
|
||||
void AtomURLRequest::GetUploadProgress(mate::Dictionary* progress) const {
|
||||
net::UploadProgress upload_progress;
|
||||
if (request_) {
|
||||
progress->Set("started", true);
|
||||
upload_progress = request_->GetUploadProgress();
|
||||
} else {
|
||||
progress->Set("started", false);
|
||||
}
|
||||
progress->Set("current", upload_progress.position());
|
||||
progress->Set("total", upload_progress.size());
|
||||
}
|
||||
|
||||
} // namespace atom
|
122
shell/browser/net/atom_url_request.h
Normal file
122
shell/browser/net/atom_url_request.h
Normal file
|
@ -0,0 +1,122 @@
|
|||
// Copyright (c) 2016 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.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_ATOM_URL_REQUEST_H_
|
||||
#define ATOM_BROWSER_NET_ATOM_URL_REQUEST_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "atom/browser/api/atom_api_url_request.h"
|
||||
#include "atom/browser/atom_browser_context.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "net/base/auth.h"
|
||||
#include "net/base/chunked_upload_data_stream.h"
|
||||
#include "net/base/io_buffer.h"
|
||||
#include "net/base/upload_element_reader.h"
|
||||
#include "net/http/http_response_headers.h"
|
||||
#include "net/url_request/url_request.h"
|
||||
#include "net/url_request/url_request_context_getter_observer.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class AtomURLRequest : public base::RefCountedThreadSafe<AtomURLRequest>,
|
||||
public net::URLRequest::Delegate,
|
||||
public net::URLRequestContextGetterObserver {
|
||||
public:
|
||||
static scoped_refptr<AtomURLRequest> Create(
|
||||
AtomBrowserContext* browser_context,
|
||||
const std::string& method,
|
||||
const std::string& url,
|
||||
const std::string& redirect_policy,
|
||||
api::URLRequest* delegate);
|
||||
void Terminate();
|
||||
|
||||
bool Write(scoped_refptr<const net::IOBufferWithSize> buffer, bool is_last);
|
||||
void SetChunkedUpload(bool is_chunked_upload);
|
||||
void Cancel();
|
||||
void FollowRedirect();
|
||||
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;
|
||||
void SetLoadFlags(int flags) const;
|
||||
void GetUploadProgress(mate::Dictionary* progress) const;
|
||||
|
||||
protected:
|
||||
// Overrides of net::URLRequest::Delegate
|
||||
void OnReceivedRedirect(net::URLRequest* request,
|
||||
const net::RedirectInfo& info,
|
||||
bool* defer_redirect) override;
|
||||
void OnAuthRequired(net::URLRequest* request,
|
||||
const net::AuthChallengeInfo& auth_info) override;
|
||||
void OnResponseStarted(net::URLRequest* request, int net_error) override;
|
||||
void OnReadCompleted(net::URLRequest* request, int bytes_read) override;
|
||||
|
||||
// Overrides of net::URLRequestContextGetterObserver
|
||||
void OnContextShuttingDown() override;
|
||||
|
||||
private:
|
||||
friend class base::RefCountedThreadSafe<AtomURLRequest>;
|
||||
|
||||
explicit AtomURLRequest(api::URLRequest* delegate);
|
||||
~AtomURLRequest() override;
|
||||
|
||||
void DoInitialize(scoped_refptr<net::URLRequestContextGetter>,
|
||||
const std::string& method,
|
||||
const std::string& url,
|
||||
const std::string& redirect_policy);
|
||||
void DoTerminate();
|
||||
void DoWriteBuffer(scoped_refptr<const net::IOBufferWithSize> buffer,
|
||||
bool is_last);
|
||||
void DoCancel();
|
||||
void DoFollowRedirect();
|
||||
void DoSetExtraHeader(const std::string& name,
|
||||
const std::string& value) const;
|
||||
void DoRemoveExtraHeader(const std::string& name) const;
|
||||
void DoSetAuth(const base::string16& username,
|
||||
const base::string16& password) const;
|
||||
void DoCancelAuth() const;
|
||||
void DoCancelWithError(const std::string& error, bool isRequestError);
|
||||
void DoSetLoadFlags(int flags) const;
|
||||
|
||||
void ReadResponse();
|
||||
bool CopyAndPostBuffer(int bytes_read);
|
||||
|
||||
void InformDelegateReceivedRedirect(
|
||||
int status_code,
|
||||
const std::string& method,
|
||||
const GURL& url,
|
||||
scoped_refptr<net::HttpResponseHeaders> response_headers) const;
|
||||
void InformDelegateAuthenticationRequired(
|
||||
const net::AuthChallengeInfo& auth_info) const;
|
||||
void InformDelegateResponseStarted(
|
||||
scoped_refptr<net::HttpResponseHeaders>) const;
|
||||
void InformDelegateResponseData(
|
||||
scoped_refptr<net::IOBufferWithSize> data) const;
|
||||
void InformDelegateResponseCompleted() const;
|
||||
void InformDelegateErrorOccured(const std::string& error,
|
||||
bool isRequestError) const;
|
||||
|
||||
api::URLRequest* delegate_;
|
||||
std::unique_ptr<net::URLRequest> request_;
|
||||
scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
|
||||
|
||||
bool is_chunked_upload_ = false;
|
||||
std::string redirect_policy_;
|
||||
std::unique_ptr<net::ChunkedUploadDataStream> chunked_stream_;
|
||||
std::unique_ptr<net::ChunkedUploadDataStream::Writer> chunked_stream_writer_;
|
||||
std::vector<std::unique_ptr<net::UploadElementReader>>
|
||||
upload_element_readers_;
|
||||
scoped_refptr<net::IOBuffer> response_read_buffer_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AtomURLRequest);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_ATOM_URL_REQUEST_H_
|
147
shell/browser/net/atom_url_request_job_factory.cc
Normal file
147
shell/browser/net/atom_url_request_job_factory.cc
Normal file
|
@ -0,0 +1,147 @@
|
|||
// 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_job_factory.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "net/base/load_flags.h"
|
||||
#include "net/url_request/url_request.h"
|
||||
|
||||
using content::BrowserThread;
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
int disable_protocol_intercept_flag_key = 0;
|
||||
|
||||
} // namespace
|
||||
|
||||
typedef net::URLRequestJobFactory::ProtocolHandler ProtocolHandler;
|
||||
|
||||
const void* DisableProtocolInterceptFlagKey() {
|
||||
return &disable_protocol_intercept_flag_key;
|
||||
}
|
||||
|
||||
AtomURLRequestJobFactory::AtomURLRequestJobFactory() {}
|
||||
|
||||
AtomURLRequestJobFactory::~AtomURLRequestJobFactory() {
|
||||
Clear();
|
||||
}
|
||||
|
||||
void AtomURLRequestJobFactory::Chain(
|
||||
std::unique_ptr<net::URLRequestJobFactory> job_factory) {
|
||||
job_factory_ = std::move(job_factory);
|
||||
}
|
||||
|
||||
bool AtomURLRequestJobFactory::SetProtocolHandler(
|
||||
const std::string& scheme,
|
||||
std::unique_ptr<ProtocolHandler> protocol_handler) {
|
||||
if (!protocol_handler) {
|
||||
auto it = protocol_handler_map_.find(scheme);
|
||||
if (it == protocol_handler_map_.end())
|
||||
return false;
|
||||
|
||||
delete it->second;
|
||||
protocol_handler_map_.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (base::ContainsKey(protocol_handler_map_, scheme))
|
||||
return false;
|
||||
protocol_handler_map_[scheme] = protocol_handler.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AtomURLRequestJobFactory::InterceptProtocol(
|
||||
const std::string& scheme,
|
||||
std::unique_ptr<ProtocolHandler> protocol_handler) {
|
||||
if (!base::ContainsKey(protocol_handler_map_, scheme) ||
|
||||
base::ContainsKey(original_protocols_, scheme))
|
||||
return false;
|
||||
ProtocolHandler* original_protocol_handler = protocol_handler_map_[scheme];
|
||||
protocol_handler_map_[scheme] = protocol_handler.release();
|
||||
original_protocols_[scheme].reset(original_protocol_handler);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AtomURLRequestJobFactory::UninterceptProtocol(const std::string& scheme) {
|
||||
auto it = original_protocols_.find(scheme);
|
||||
if (it == original_protocols_.end())
|
||||
return false;
|
||||
protocol_handler_map_[scheme] = it->second.release();
|
||||
original_protocols_.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AtomURLRequestJobFactory::HasProtocolHandler(
|
||||
const std::string& scheme) const {
|
||||
return base::ContainsKey(protocol_handler_map_, scheme);
|
||||
}
|
||||
|
||||
void AtomURLRequestJobFactory::Clear() {
|
||||
for (auto& it : protocol_handler_map_)
|
||||
delete it.second;
|
||||
protocol_handler_map_.clear();
|
||||
original_protocols_.clear();
|
||||
}
|
||||
|
||||
net::URLRequestJob* AtomURLRequestJobFactory::MaybeCreateJobWithProtocolHandler(
|
||||
const std::string& scheme,
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate) const {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
|
||||
auto* job = job_factory_->MaybeCreateJobWithProtocolHandler(scheme, request,
|
||||
network_delegate);
|
||||
if (job)
|
||||
return job;
|
||||
|
||||
auto it = protocol_handler_map_.find(scheme);
|
||||
if (it == protocol_handler_map_.end())
|
||||
return nullptr;
|
||||
|
||||
if (request->GetUserData(DisableProtocolInterceptFlagKey()))
|
||||
return nullptr;
|
||||
|
||||
return it->second->MaybeCreateJob(request, network_delegate);
|
||||
}
|
||||
|
||||
net::URLRequestJob* AtomURLRequestJobFactory::MaybeInterceptRedirect(
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate,
|
||||
const GURL& location) const {
|
||||
return job_factory_->MaybeInterceptRedirect(request, network_delegate,
|
||||
location);
|
||||
}
|
||||
|
||||
net::URLRequestJob* AtomURLRequestJobFactory::MaybeInterceptResponse(
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate) const {
|
||||
return job_factory_->MaybeInterceptResponse(request, network_delegate);
|
||||
}
|
||||
|
||||
bool AtomURLRequestJobFactory::IsHandledProtocol(
|
||||
const std::string& scheme) const {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
|
||||
return HasProtocolHandler(scheme) ||
|
||||
net::URLRequest::IsHandledProtocol(scheme);
|
||||
}
|
||||
|
||||
bool AtomURLRequestJobFactory::IsSafeRedirectTarget(
|
||||
const GURL& location) const {
|
||||
if (!location.is_valid()) {
|
||||
// We handle error cases.
|
||||
return true;
|
||||
}
|
||||
return IsHandledProtocol(location.scheme());
|
||||
}
|
||||
|
||||
} // namespace atom
|
79
shell/browser/net/atom_url_request_job_factory.h
Normal file
79
shell/browser/net/atom_url_request_job_factory.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
// 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.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_ATOM_URL_REQUEST_JOB_FACTORY_H_
|
||||
#define ATOM_BROWSER_NET_ATOM_URL_REQUEST_JOB_FACTORY_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "net/url_request/url_request_job_factory.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
const void* DisableProtocolInterceptFlagKey();
|
||||
|
||||
class AtomURLRequestJobFactory : public net::URLRequestJobFactory {
|
||||
public:
|
||||
AtomURLRequestJobFactory();
|
||||
~AtomURLRequestJobFactory() override;
|
||||
|
||||
// Requests are forwarded to the chained job factory first.
|
||||
void Chain(std::unique_ptr<net::URLRequestJobFactory> job_factory);
|
||||
|
||||
// Sets the ProtocolHandler for a scheme. Returns true on success, false on
|
||||
// failure (a ProtocolHandler already exists for |scheme|). On success,
|
||||
// URLRequestJobFactory takes ownership of |protocol_handler|.
|
||||
bool SetProtocolHandler(const std::string& scheme,
|
||||
std::unique_ptr<ProtocolHandler> protocol_handler);
|
||||
|
||||
// Intercepts the ProtocolHandler for a scheme.
|
||||
bool InterceptProtocol(const std::string& scheme,
|
||||
std::unique_ptr<ProtocolHandler> protocol_handler);
|
||||
bool UninterceptProtocol(const std::string& scheme);
|
||||
|
||||
// Whether the protocol handler is registered by the job factory.
|
||||
bool HasProtocolHandler(const std::string& scheme) const;
|
||||
|
||||
// Clear all protocol handlers.
|
||||
void Clear();
|
||||
|
||||
// URLRequestJobFactory implementation
|
||||
net::URLRequestJob* MaybeCreateJobWithProtocolHandler(
|
||||
const std::string& scheme,
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate) const override;
|
||||
net::URLRequestJob* MaybeInterceptRedirect(
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate,
|
||||
const GURL& location) const override;
|
||||
net::URLRequestJob* MaybeInterceptResponse(
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate) const override;
|
||||
bool IsHandledProtocol(const std::string& scheme) const override;
|
||||
bool IsSafeRedirectTarget(const GURL& location) const override;
|
||||
|
||||
private:
|
||||
using ProtocolHandlerMap = std::map<std::string, ProtocolHandler*>;
|
||||
|
||||
ProtocolHandlerMap protocol_handler_map_;
|
||||
|
||||
// Map that stores the original protocols of schemes.
|
||||
using OriginalProtocolsMap =
|
||||
std::unordered_map<std::string, std::unique_ptr<ProtocolHandler>>;
|
||||
// Can only be accessed in IO thread.
|
||||
OriginalProtocolsMap original_protocols_;
|
||||
|
||||
std::unique_ptr<net::URLRequestJobFactory> job_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AtomURLRequestJobFactory);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_ATOM_URL_REQUEST_JOB_FACTORY_H_
|
31
shell/browser/net/cookie_details.h
Normal file
31
shell/browser/net/cookie_details.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) 2017 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_COOKIE_DETAILS_H_
|
||||
#define ATOM_BROWSER_NET_COOKIE_DETAILS_H_
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "services/network/public/mojom/cookie_manager.mojom.h"
|
||||
|
||||
namespace net {
|
||||
class CanonicalCookie;
|
||||
}
|
||||
|
||||
namespace atom {
|
||||
|
||||
struct CookieDetails {
|
||||
public:
|
||||
CookieDetails(const net::CanonicalCookie* cookie_copy,
|
||||
bool is_removed,
|
||||
network::mojom::CookieChangeCause cause)
|
||||
: cookie(cookie_copy), removed(is_removed), cause(cause) {}
|
||||
|
||||
const net::CanonicalCookie* cookie;
|
||||
bool removed;
|
||||
network::mojom::CookieChangeCause cause;
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_COOKIE_DETAILS_H_
|
22
shell/browser/net/http_protocol_handler.cc
Normal file
22
shell/browser/net/http_protocol_handler.cc
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) 2015 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/http_protocol_handler.h"
|
||||
|
||||
#include "net/url_request/url_request_http_job.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
HttpProtocolHandler::HttpProtocolHandler(const std::string& scheme)
|
||||
: scheme_(scheme) {}
|
||||
|
||||
HttpProtocolHandler::~HttpProtocolHandler() {}
|
||||
|
||||
net::URLRequestJob* HttpProtocolHandler::MaybeCreateJob(
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate) const {
|
||||
return net::URLRequestHttpJob::Factory(request, network_delegate, scheme_);
|
||||
}
|
||||
|
||||
} // namespace atom
|
30
shell/browser/net/http_protocol_handler.h
Normal file
30
shell/browser/net/http_protocol_handler.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) 2015 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_HTTP_PROTOCOL_HANDLER_H_
|
||||
#define ATOM_BROWSER_NET_HTTP_PROTOCOL_HANDLER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "net/url_request/url_request_job_factory.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class HttpProtocolHandler : public net::URLRequestJobFactory::ProtocolHandler {
|
||||
public:
|
||||
explicit HttpProtocolHandler(const std::string&);
|
||||
~HttpProtocolHandler() override;
|
||||
|
||||
// net::URLRequestJobFactory::ProtocolHandler:
|
||||
net::URLRequestJob* MaybeCreateJob(
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate) const override;
|
||||
|
||||
private:
|
||||
std::string scheme_;
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_HTTP_PROTOCOL_HANDLER_H_
|
55
shell/browser/net/js_asker.cc
Normal file
55
shell/browser/net/js_asker.cc
Normal file
|
@ -0,0 +1,55 @@
|
|||
// Copyright (c) 2015 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/js_asker.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "atom/common/native_mate_converters/once_callback.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
JsAsker::JsAsker() = default;
|
||||
|
||||
JsAsker::~JsAsker() = default;
|
||||
|
||||
void JsAsker::SetHandlerInfo(
|
||||
v8::Isolate* isolate,
|
||||
net::URLRequestContextGetter* request_context_getter,
|
||||
const JavaScriptHandler& handler) {
|
||||
isolate_ = isolate;
|
||||
request_context_getter_ = request_context_getter;
|
||||
handler_ = handler;
|
||||
}
|
||||
|
||||
// static
|
||||
void JsAsker::AskForOptions(
|
||||
v8::Isolate* isolate,
|
||||
const JavaScriptHandler& handler,
|
||||
std::unique_ptr<base::DictionaryValue> request_details,
|
||||
BeforeStartCallback before_start) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
v8::Locker locker(isolate);
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
||||
v8::Context::Scope context_scope(context);
|
||||
handler.Run(*(request_details.get()),
|
||||
mate::ConvertToV8(isolate, std::move(before_start)));
|
||||
}
|
||||
|
||||
// static
|
||||
bool JsAsker::IsErrorOptions(base::Value* value, int* error) {
|
||||
if (value->is_dict()) {
|
||||
base::DictionaryValue* dict = static_cast<base::DictionaryValue*>(value);
|
||||
if (dict->GetInteger("error", error))
|
||||
return true;
|
||||
} else if (value->is_int()) {
|
||||
*error = value->GetInt();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace atom
|
58
shell/browser/net/js_asker.h
Normal file
58
shell/browser/net/js_asker.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
// Copyright (c) 2015 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_JS_ASKER_H_
|
||||
#define ATOM_BROWSER_NET_JS_ASKER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "base/values.h"
|
||||
#include "native_mate/arguments.h"
|
||||
#include "net/url_request/url_request_context_getter.h"
|
||||
#include "v8/include/v8.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
using JavaScriptHandler =
|
||||
base::Callback<void(const base::DictionaryValue&, v8::Local<v8::Value>)>;
|
||||
using BeforeStartCallback = base::OnceCallback<void(mate::Arguments* args)>;
|
||||
|
||||
class JsAsker {
|
||||
public:
|
||||
JsAsker();
|
||||
~JsAsker();
|
||||
|
||||
// Called by |CustomProtocolHandler| to store handler related information.
|
||||
void SetHandlerInfo(v8::Isolate* isolate,
|
||||
net::URLRequestContextGetter* request_context_getter,
|
||||
const JavaScriptHandler& handler);
|
||||
|
||||
// Ask handler for options in UI thread.
|
||||
static void AskForOptions(
|
||||
v8::Isolate* isolate,
|
||||
const JavaScriptHandler& handler,
|
||||
std::unique_ptr<base::DictionaryValue> request_details,
|
||||
BeforeStartCallback before_start);
|
||||
|
||||
// Test whether the |options| means an error.
|
||||
static bool IsErrorOptions(base::Value* value, int* error);
|
||||
|
||||
net::URLRequestContextGetter* request_context_getter() const {
|
||||
return request_context_getter_;
|
||||
}
|
||||
v8::Isolate* isolate() { return isolate_; }
|
||||
JavaScriptHandler handler() { return handler_; }
|
||||
|
||||
private:
|
||||
v8::Isolate* isolate_;
|
||||
net::URLRequestContextGetter* request_context_getter_;
|
||||
JavaScriptHandler handler_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(JsAsker);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_JS_ASKER_H_
|
91
shell/browser/net/network_context_service.cc
Normal file
91
shell/browser/net/network_context_service.cc
Normal file
|
@ -0,0 +1,91 @@
|
|||
// Copyright (c) 2019 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/network_context_service.h"
|
||||
|
||||
#include "atom/browser/atom_browser_client.h"
|
||||
#include "atom/browser/browser_process_impl.h"
|
||||
#include "atom/browser/net/system_network_context_manager.h"
|
||||
#include "chrome/common/chrome_constants.h"
|
||||
#include "content/public/browser/network_service_instance.h"
|
||||
#include "services/network/network_service.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
NetworkContextService::NetworkContextService(content::BrowserContext* context)
|
||||
: browser_context_(static_cast<AtomBrowserContext*>(context)),
|
||||
proxy_config_monitor_(browser_context_->prefs()) {}
|
||||
|
||||
NetworkContextService::~NetworkContextService() = default;
|
||||
|
||||
network::mojom::NetworkContextPtr
|
||||
NetworkContextService::CreateNetworkContext() {
|
||||
network::mojom::NetworkContextPtr network_context;
|
||||
|
||||
content::GetNetworkService()->CreateNetworkContext(
|
||||
MakeRequest(&network_context),
|
||||
CreateNetworkContextParams(browser_context_->IsOffTheRecord(),
|
||||
browser_context_->GetPath()));
|
||||
|
||||
return network_context;
|
||||
}
|
||||
|
||||
network::mojom::NetworkContextParamsPtr
|
||||
NetworkContextService::CreateNetworkContextParams(bool in_memory,
|
||||
const base::FilePath& path) {
|
||||
network::mojom::NetworkContextParamsPtr network_context_params =
|
||||
g_browser_process->system_network_context_manager()
|
||||
->CreateDefaultNetworkContextParams();
|
||||
|
||||
network_context_params->user_agent = browser_context_->GetUserAgent();
|
||||
|
||||
network_context_params->accept_language =
|
||||
net::HttpUtil::GenerateAcceptLanguageHeader(
|
||||
AtomBrowserClient::Get()->GetApplicationLocale());
|
||||
|
||||
// Enable the HTTP cache.
|
||||
network_context_params->http_cache_enabled =
|
||||
browser_context_->CanUseHttpCache();
|
||||
|
||||
network_context_params->cookie_manager_params =
|
||||
network::mojom::CookieManagerParams::New();
|
||||
|
||||
// Configure on-disk storage for persistent sessions.
|
||||
if (!in_memory) {
|
||||
// Configure the HTTP cache path and size.
|
||||
network_context_params->http_cache_path =
|
||||
path.Append(chrome::kCacheDirname);
|
||||
network_context_params->http_cache_max_size =
|
||||
browser_context_->GetMaxCacheSize();
|
||||
|
||||
// Currently this just contains HttpServerProperties
|
||||
network_context_params->http_server_properties_path =
|
||||
path.Append(chrome::kNetworkPersistentStateFilename);
|
||||
|
||||
// Configure persistent cookie path.
|
||||
network_context_params->cookie_path = path.Append(chrome::kCookieFilename);
|
||||
|
||||
network_context_params->restore_old_session_cookies = false;
|
||||
network_context_params->persist_session_cookies = false;
|
||||
|
||||
// TODO(deepak1556): Matches the existing behavior https://git.io/fxHMl,
|
||||
// enable encryption as a followup.
|
||||
network_context_params->enable_encrypted_cookies = false;
|
||||
|
||||
network_context_params->transport_security_persister_path = path;
|
||||
}
|
||||
|
||||
#if !BUILDFLAG(DISABLE_FTP_SUPPORT)
|
||||
network_context_params->enable_ftp_url_support = true;
|
||||
#endif // !BUILDFLAG(DISABLE_FTP_SUPPORT)
|
||||
|
||||
proxy_config_monitor_.AddToNetworkContextParams(network_context_params.get());
|
||||
|
||||
BrowserProcessImpl::ApplyProxyModeFromCommandLine(
|
||||
browser_context_->in_memory_pref_store());
|
||||
|
||||
return network_context_params;
|
||||
}
|
||||
|
||||
} // namespace atom
|
41
shell/browser/net/network_context_service.h
Normal file
41
shell/browser/net/network_context_service.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
// Copyright (c) 2019 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_NETWORK_CONTEXT_SERVICE_H_
|
||||
#define ATOM_BROWSER_NET_NETWORK_CONTEXT_SERVICE_H_
|
||||
|
||||
#include "atom/browser/atom_browser_context.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "chrome/browser/net/proxy_config_monitor.h"
|
||||
#include "components/keyed_service/core/keyed_service.h"
|
||||
#include "services/network/public/mojom/network_context.mojom.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
// KeyedService that initializes and provides access to the NetworkContexts for
|
||||
// a BrowserContext.
|
||||
class NetworkContextService : public KeyedService {
|
||||
public:
|
||||
explicit NetworkContextService(content::BrowserContext* context);
|
||||
~NetworkContextService() override;
|
||||
|
||||
NetworkContextService(const NetworkContextService&) = delete;
|
||||
NetworkContextService& operator=(const NetworkContextService&) = delete;
|
||||
|
||||
// Creates a NetworkContext for the BrowserContext.
|
||||
network::mojom::NetworkContextPtr CreateNetworkContext();
|
||||
|
||||
private:
|
||||
// Creates parameters for the NetworkContext.
|
||||
network::mojom::NetworkContextParamsPtr CreateNetworkContextParams(
|
||||
bool in_memory,
|
||||
const base::FilePath& path);
|
||||
|
||||
AtomBrowserContext* browser_context_;
|
||||
ProxyConfigMonitor proxy_config_monitor_;
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_NETWORK_CONTEXT_SERVICE_H_
|
40
shell/browser/net/network_context_service_factory.cc
Normal file
40
shell/browser/net/network_context_service_factory.cc
Normal file
|
@ -0,0 +1,40 @@
|
|||
// Copyright (c) 2019 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/network_context_service_factory.h"
|
||||
|
||||
#include "atom/browser/net/network_context_service.h"
|
||||
#include "components/keyed_service/content/browser_context_dependency_manager.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
NetworkContextService* NetworkContextServiceFactory::GetForContext(
|
||||
content::BrowserContext* browser_context) {
|
||||
return static_cast<NetworkContextService*>(
|
||||
GetInstance()->GetServiceForBrowserContext(browser_context, true));
|
||||
}
|
||||
|
||||
NetworkContextServiceFactory* NetworkContextServiceFactory::GetInstance() {
|
||||
return base::Singleton<NetworkContextServiceFactory>::get();
|
||||
}
|
||||
|
||||
NetworkContextServiceFactory::NetworkContextServiceFactory()
|
||||
: BrowserContextKeyedServiceFactory(
|
||||
"ElectronNetworkContextService",
|
||||
BrowserContextDependencyManager::GetInstance()) {}
|
||||
|
||||
NetworkContextServiceFactory::~NetworkContextServiceFactory() {}
|
||||
|
||||
KeyedService* NetworkContextServiceFactory::BuildServiceInstanceFor(
|
||||
content::BrowserContext* context) const {
|
||||
return new NetworkContextService(static_cast<AtomBrowserContext*>(context));
|
||||
}
|
||||
|
||||
content::BrowserContext* NetworkContextServiceFactory::GetBrowserContextToUse(
|
||||
content::BrowserContext* context) const {
|
||||
// Create separate service for temporary sessions.
|
||||
return context;
|
||||
}
|
||||
|
||||
} // namespace atom
|
50
shell/browser/net/network_context_service_factory.h
Normal file
50
shell/browser/net/network_context_service_factory.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
// Copyright (c) 2019 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_NETWORK_CONTEXT_SERVICE_FACTORY_H_
|
||||
#define ATOM_BROWSER_NET_NETWORK_CONTEXT_SERVICE_FACTORY_H_
|
||||
|
||||
#include "base/memory/singleton.h"
|
||||
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
|
||||
|
||||
class KeyedService;
|
||||
|
||||
namespace contenet {
|
||||
class BrowserContext;
|
||||
}
|
||||
|
||||
namespace atom {
|
||||
|
||||
class NetworkContextService;
|
||||
|
||||
class NetworkContextServiceFactory : public BrowserContextKeyedServiceFactory {
|
||||
public:
|
||||
// Returns the NetworkContextService that supports NetworkContexts for
|
||||
// |browser_context|.
|
||||
static NetworkContextService* GetForContext(
|
||||
content::BrowserContext* browser_context);
|
||||
|
||||
// Returns the NetworkContextServiceFactory singleton.
|
||||
static NetworkContextServiceFactory* GetInstance();
|
||||
|
||||
NetworkContextServiceFactory(const NetworkContextServiceFactory&) = delete;
|
||||
NetworkContextServiceFactory& operator=(const NetworkContextServiceFactory&) =
|
||||
delete;
|
||||
|
||||
private:
|
||||
friend struct base::DefaultSingletonTraits<NetworkContextServiceFactory>;
|
||||
|
||||
NetworkContextServiceFactory();
|
||||
~NetworkContextServiceFactory() override;
|
||||
|
||||
// BrowserContextKeyedServiceFactory implementation:
|
||||
KeyedService* BuildServiceInstanceFor(
|
||||
content::BrowserContext* context) const override;
|
||||
content::BrowserContext* GetBrowserContextToUse(
|
||||
content::BrowserContext* context) const override;
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_NETWORK_CONTEXT_SERVICE_FACTORY_H_
|
142
shell/browser/net/node_stream_loader.cc
Normal file
142
shell/browser/net/node_stream_loader.cc
Normal file
|
@ -0,0 +1,142 @@
|
|||
// Copyright (c) 2019 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/node_stream_loader.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "atom/common/api/event_emitter_caller.h"
|
||||
#include "atom/common/native_mate_converters/callback.h"
|
||||
|
||||
#include "atom/common/node_includes.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
NodeStreamLoader::NodeStreamLoader(network::ResourceResponseHead head,
|
||||
network::mojom::URLLoaderRequest loader,
|
||||
network::mojom::URLLoaderClientPtr client,
|
||||
v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> emitter)
|
||||
: binding_(this, std::move(loader)),
|
||||
client_(std::move(client)),
|
||||
isolate_(isolate),
|
||||
emitter_(isolate, emitter),
|
||||
weak_factory_(this) {
|
||||
binding_.set_connection_error_handler(
|
||||
base::BindOnce(&NodeStreamLoader::NotifyComplete,
|
||||
weak_factory_.GetWeakPtr(), net::ERR_FAILED));
|
||||
|
||||
// PostTask since it might destruct.
|
||||
base::SequencedTaskRunnerHandle::Get()->PostTask(
|
||||
FROM_HERE, base::BindOnce(&NodeStreamLoader::Start,
|
||||
weak_factory_.GetWeakPtr(), std::move(head)));
|
||||
}
|
||||
|
||||
NodeStreamLoader::~NodeStreamLoader() {
|
||||
v8::Locker locker(isolate_);
|
||||
v8::Isolate::Scope isolate_scope(isolate_);
|
||||
v8::HandleScope handle_scope(isolate_);
|
||||
|
||||
// Unsubscribe all handlers.
|
||||
for (const auto& it : handlers_) {
|
||||
v8::Local<v8::Value> args[] = {mate::StringToV8(isolate_, it.first),
|
||||
it.second.Get(isolate_)};
|
||||
node::MakeCallback(isolate_, emitter_.Get(isolate_), "removeListener",
|
||||
node::arraysize(args), args, {0, 0});
|
||||
}
|
||||
|
||||
// Release references.
|
||||
emitter_.Reset();
|
||||
buffer_.Reset();
|
||||
}
|
||||
|
||||
void NodeStreamLoader::Start(network::ResourceResponseHead head) {
|
||||
mojo::ScopedDataPipeProducerHandle producer;
|
||||
mojo::ScopedDataPipeConsumerHandle consumer;
|
||||
MojoResult rv = mojo::CreateDataPipe(nullptr, &producer, &consumer);
|
||||
if (rv != MOJO_RESULT_OK) {
|
||||
NotifyComplete(net::ERR_INSUFFICIENT_RESOURCES);
|
||||
return;
|
||||
}
|
||||
|
||||
producer_ =
|
||||
std::make_unique<mojo::StringDataPipeProducer>(std::move(producer));
|
||||
|
||||
client_->OnReceiveResponse(head);
|
||||
client_->OnStartLoadingResponseBody(std::move(consumer));
|
||||
|
||||
auto weak = weak_factory_.GetWeakPtr();
|
||||
On("end",
|
||||
base::BindRepeating(&NodeStreamLoader::NotifyComplete, weak, net::OK));
|
||||
On("error", base::BindRepeating(&NodeStreamLoader::NotifyComplete, weak,
|
||||
net::ERR_FAILED));
|
||||
On("readable", base::BindRepeating(&NodeStreamLoader::ReadMore, weak));
|
||||
}
|
||||
|
||||
void NodeStreamLoader::NotifyComplete(int result) {
|
||||
// Wait until write finishes or fails.
|
||||
if (is_writing_) {
|
||||
ended_ = true;
|
||||
result_ = result;
|
||||
return;
|
||||
}
|
||||
|
||||
client_->OnComplete(network::URLLoaderCompletionStatus(result));
|
||||
delete this;
|
||||
}
|
||||
|
||||
void NodeStreamLoader::ReadMore() {
|
||||
// buffer = emitter.read()
|
||||
v8::MaybeLocal<v8::Value> ret = node::MakeCallback(
|
||||
isolate_, emitter_.Get(isolate_), "read", 0, nullptr, {0, 0});
|
||||
|
||||
// If there is no buffer read, wait until |readable| is emitted again.
|
||||
v8::Local<v8::Value> buffer;
|
||||
if (!ret.ToLocal(&buffer) || !node::Buffer::HasInstance(buffer))
|
||||
return;
|
||||
|
||||
// Hold the buffer until the write is done.
|
||||
buffer_.Reset(isolate_, buffer);
|
||||
|
||||
// Write buffer to mojo pipe asyncronously.
|
||||
is_writing_ = true;
|
||||
producer_->Write(
|
||||
base::StringPiece(node::Buffer::Data(buffer),
|
||||
node::Buffer::Length(buffer)),
|
||||
mojo::StringDataPipeProducer::AsyncWritingMode::
|
||||
STRING_STAYS_VALID_UNTIL_COMPLETION,
|
||||
base::BindOnce(&NodeStreamLoader::DidWrite, weak_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
void NodeStreamLoader::DidWrite(MojoResult result) {
|
||||
is_writing_ = false;
|
||||
// We were told to end streaming.
|
||||
if (ended_) {
|
||||
NotifyComplete(result_);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result == MOJO_RESULT_OK)
|
||||
ReadMore();
|
||||
else
|
||||
NotifyComplete(net::ERR_FAILED);
|
||||
}
|
||||
|
||||
void NodeStreamLoader::On(const char* event, EventCallback callback) {
|
||||
v8::Locker locker(isolate_);
|
||||
v8::Isolate::Scope isolate_scope(isolate_);
|
||||
v8::HandleScope handle_scope(isolate_);
|
||||
|
||||
// emitter.on(event, callback)
|
||||
v8::Local<v8::Value> args[] = {
|
||||
mate::StringToV8(isolate_, event),
|
||||
mate::CallbackToV8(isolate_, std::move(callback)),
|
||||
};
|
||||
handlers_[event].Reset(isolate_, args[1]);
|
||||
node::MakeCallback(isolate_, emitter_.Get(isolate_), "on",
|
||||
node::arraysize(args), args, {0, 0});
|
||||
// No more code bellow, as this class may destruct when subscribing.
|
||||
}
|
||||
|
||||
} // namespace atom
|
87
shell/browser/net/node_stream_loader.h
Normal file
87
shell/browser/net/node_stream_loader.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
// Copyright (c) 2019 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_NODE_STREAM_LOADER_H_
|
||||
#define ATOM_BROWSER_NET_NODE_STREAM_LOADER_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "mojo/public/cpp/bindings/strong_binding.h"
|
||||
#include "mojo/public/cpp/system/string_data_pipe_producer.h"
|
||||
#include "services/network/public/mojom/url_loader.mojom.h"
|
||||
#include "v8/include/v8.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
// Read data from node Stream and feed it to NetworkService.
|
||||
//
|
||||
// This class manages its own lifetime and should delete itself when the
|
||||
// connection is lost or finished.
|
||||
//
|
||||
// We use |paused mode| to read data from |Readable| stream, so we don't need to
|
||||
// copy data from buffer and hold it in memory, and we only need to make sure
|
||||
// the passed |Buffer| is alive while writing data to pipe.
|
||||
class NodeStreamLoader : public network::mojom::URLLoader {
|
||||
public:
|
||||
NodeStreamLoader(network::ResourceResponseHead head,
|
||||
network::mojom::URLLoaderRequest loader,
|
||||
network::mojom::URLLoaderClientPtr client,
|
||||
v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> emitter);
|
||||
|
||||
private:
|
||||
~NodeStreamLoader() override;
|
||||
|
||||
using EventCallback = base::RepeatingCallback<void()>;
|
||||
|
||||
void Start(network::ResourceResponseHead head);
|
||||
void NotifyComplete(int result);
|
||||
void ReadMore();
|
||||
void DidWrite(MojoResult result);
|
||||
|
||||
// Subscribe to events of |emitter|.
|
||||
void On(const char* event, EventCallback callback);
|
||||
|
||||
// URLLoader:
|
||||
void FollowRedirect(const std::vector<std::string>& removed_headers,
|
||||
const net::HttpRequestHeaders& modified_headers,
|
||||
const base::Optional<GURL>& new_url) override {}
|
||||
void ProceedWithResponse() override {}
|
||||
void SetPriority(net::RequestPriority priority,
|
||||
int32_t intra_priority_value) override {}
|
||||
void PauseReadingBodyFromNet() override {}
|
||||
void ResumeReadingBodyFromNet() override {}
|
||||
|
||||
mojo::Binding<network::mojom::URLLoader> binding_;
|
||||
network::mojom::URLLoaderClientPtr client_;
|
||||
|
||||
v8::Isolate* isolate_;
|
||||
v8::Global<v8::Object> emitter_;
|
||||
v8::Global<v8::Value> buffer_;
|
||||
|
||||
// Mojo data pipe where the data that is being read is written to.
|
||||
std::unique_ptr<mojo::StringDataPipeProducer> producer_;
|
||||
|
||||
// Whether we are in the middle of write.
|
||||
bool is_writing_ = false;
|
||||
|
||||
// When NotifyComplete is called while writing, we will save the result and
|
||||
// quit with it after the write is done.
|
||||
bool ended_ = false;
|
||||
int result_ = net::OK;
|
||||
|
||||
// Store the V8 callbacks to unsubscribe them later.
|
||||
std::map<std::string, v8::Global<v8::Value>> handlers_;
|
||||
|
||||
base::WeakPtrFactory<NodeStreamLoader> weak_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(NodeStreamLoader);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_NODE_STREAM_LOADER_H_
|
77
shell/browser/net/proxying_url_loader_factory.cc
Normal file
77
shell/browser/net/proxying_url_loader_factory.cc
Normal file
|
@ -0,0 +1,77 @@
|
|||
// Copyright (c) 2019 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/proxying_url_loader_factory.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "atom/browser/net/asar/asar_url_loader.h"
|
||||
#include "mojo/public/cpp/bindings/binding.h"
|
||||
#include "services/network/public/mojom/url_loader.mojom.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
ProxyingURLLoaderFactory::ProxyingURLLoaderFactory(
|
||||
const HandlersMap& handlers,
|
||||
network::mojom::URLLoaderFactoryRequest loader_request,
|
||||
network::mojom::URLLoaderFactoryPtrInfo target_factory_info)
|
||||
: handlers_(handlers) {
|
||||
target_factory_.Bind(std::move(target_factory_info));
|
||||
target_factory_.set_connection_error_handler(base::BindOnce(
|
||||
&ProxyingURLLoaderFactory::OnTargetFactoryError, base::Unretained(this)));
|
||||
proxy_bindings_.AddBinding(this, std::move(loader_request));
|
||||
proxy_bindings_.set_connection_error_handler(base::BindRepeating(
|
||||
&ProxyingURLLoaderFactory::OnProxyBindingError, base::Unretained(this)));
|
||||
}
|
||||
|
||||
ProxyingURLLoaderFactory::~ProxyingURLLoaderFactory() = default;
|
||||
|
||||
void ProxyingURLLoaderFactory::CreateLoaderAndStart(
|
||||
network::mojom::URLLoaderRequest loader,
|
||||
int32_t routing_id,
|
||||
int32_t request_id,
|
||||
uint32_t options,
|
||||
const network::ResourceRequest& request,
|
||||
network::mojom::URLLoaderClientPtr client,
|
||||
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
|
||||
// Check if user has intercepted this scheme.
|
||||
auto it = handlers_.find(request.url.scheme());
|
||||
if (it != handlers_.end()) {
|
||||
// <scheme, <type, handler>>
|
||||
it->second.second.Run(
|
||||
request, base::BindOnce(&AtomURLLoaderFactory::StartLoading,
|
||||
std::move(loader), routing_id, request_id,
|
||||
options, request, std::move(client),
|
||||
traffic_annotation, this, it->second.first));
|
||||
return;
|
||||
}
|
||||
|
||||
// Intercept file:// protocol to support asar archives.
|
||||
if (request.url.SchemeIsFile()) {
|
||||
asar::CreateAsarURLLoader(request, std::move(loader), std::move(client),
|
||||
nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
// Pass-through to the original factory.
|
||||
target_factory_->CreateLoaderAndStart(std::move(loader), routing_id,
|
||||
request_id, options, request,
|
||||
std::move(client), traffic_annotation);
|
||||
}
|
||||
|
||||
void ProxyingURLLoaderFactory::Clone(
|
||||
network::mojom::URLLoaderFactoryRequest loader_request) {
|
||||
proxy_bindings_.AddBinding(this, std::move(loader_request));
|
||||
}
|
||||
|
||||
void ProxyingURLLoaderFactory::OnTargetFactoryError() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
void ProxyingURLLoaderFactory::OnProxyBindingError() {
|
||||
if (proxy_bindings_.empty())
|
||||
delete this;
|
||||
}
|
||||
|
||||
} // namespace atom
|
52
shell/browser/net/proxying_url_loader_factory.h
Normal file
52
shell/browser/net/proxying_url_loader_factory.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) 2019 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_PROXYING_URL_LOADER_FACTORY_H_
|
||||
#define ATOM_BROWSER_NET_PROXYING_URL_LOADER_FACTORY_H_
|
||||
|
||||
#include "atom/browser/net/atom_url_loader_factory.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class ProxyingURLLoaderFactory : public network::mojom::URLLoaderFactory {
|
||||
public:
|
||||
ProxyingURLLoaderFactory(
|
||||
const HandlersMap& handlers,
|
||||
network::mojom::URLLoaderFactoryRequest loader_request,
|
||||
network::mojom::URLLoaderFactoryPtrInfo target_factory_info);
|
||||
~ProxyingURLLoaderFactory() override;
|
||||
|
||||
// network::mojom::URLLoaderFactory:
|
||||
void CreateLoaderAndStart(network::mojom::URLLoaderRequest loader,
|
||||
int32_t routing_id,
|
||||
int32_t request_id,
|
||||
uint32_t options,
|
||||
const network::ResourceRequest& request,
|
||||
network::mojom::URLLoaderClientPtr client,
|
||||
const net::MutableNetworkTrafficAnnotationTag&
|
||||
traffic_annotation) override;
|
||||
void Clone(network::mojom::URLLoaderFactoryRequest request) override;
|
||||
|
||||
private:
|
||||
void OnTargetFactoryError();
|
||||
void OnProxyBindingError();
|
||||
|
||||
// This is passed from api::ProtocolNS.
|
||||
//
|
||||
// The ProtocolNS instance lives through the lifetime of BrowserContenxt,
|
||||
// which is guarenteed to cover the lifetime of URLLoaderFactory, so the
|
||||
// reference is guarenteed to be valid.
|
||||
//
|
||||
// In this way we can avoid using code from api namespace in this file.
|
||||
const HandlersMap& handlers_;
|
||||
|
||||
mojo::BindingSet<network::mojom::URLLoaderFactory> proxy_bindings_;
|
||||
network::mojom::URLLoaderFactoryPtr target_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ProxyingURLLoaderFactory);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_PROXYING_URL_LOADER_FACTORY_H_
|
36
shell/browser/net/require_ct_delegate.cc
Normal file
36
shell/browser/net/require_ct_delegate.cc
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) 2017 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/require_ct_delegate.h"
|
||||
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
RequireCTDelegate::RequireCTDelegate() {}
|
||||
|
||||
RequireCTDelegate::~RequireCTDelegate() {}
|
||||
|
||||
void RequireCTDelegate::AddCTExcludedHost(const std::string& host) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
ct_excluded_hosts_.insert(host);
|
||||
}
|
||||
|
||||
void RequireCTDelegate::ClearCTExcludedHostsList() {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
ct_excluded_hosts_.clear();
|
||||
}
|
||||
|
||||
RequireCTDelegate::CTRequirementLevel RequireCTDelegate::IsCTRequiredForHost(
|
||||
const std::string& host,
|
||||
const net::X509Certificate* chain,
|
||||
const net::HashValueVector& hashes) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
if (!ct_excluded_hosts_.empty() &&
|
||||
(ct_excluded_hosts_.find(host) != ct_excluded_hosts_.end()))
|
||||
return CTRequirementLevel::NOT_REQUIRED;
|
||||
return CTRequirementLevel::DEFAULT;
|
||||
}
|
||||
|
||||
} // namespace atom
|
37
shell/browser/net/require_ct_delegate.h
Normal file
37
shell/browser/net/require_ct_delegate.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) 2017 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_REQUIRE_CT_DELEGATE_H_
|
||||
#define ATOM_BROWSER_NET_REQUIRE_CT_DELEGATE_H_
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "net/http/transport_security_state.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class RequireCTDelegate
|
||||
: public net::TransportSecurityState::RequireCTDelegate {
|
||||
public:
|
||||
RequireCTDelegate();
|
||||
~RequireCTDelegate() override;
|
||||
|
||||
void AddCTExcludedHost(const std::string& host);
|
||||
void ClearCTExcludedHostsList();
|
||||
|
||||
// net::TransportSecurityState::RequireCTDelegate:
|
||||
CTRequirementLevel IsCTRequiredForHost(
|
||||
const std::string& host,
|
||||
const net::X509Certificate* chain,
|
||||
const net::HashValueVector& hashes) override;
|
||||
|
||||
private:
|
||||
std::set<std::string> ct_excluded_hosts_;
|
||||
DISALLOW_COPY_AND_ASSIGN(RequireCTDelegate);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_REQUIRE_CT_DELEGATE_H_
|
100
shell/browser/net/resolve_proxy_helper.cc
Normal file
100
shell/browser/net/resolve_proxy_helper.cc
Normal file
|
@ -0,0 +1,100 @@
|
|||
// Copyright (c) 2018 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/resolve_proxy_helper.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "atom/browser/atom_browser_context.h"
|
||||
#include "base/bind.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/storage_partition.h"
|
||||
#include "mojo/public/cpp/bindings/interface_request.h"
|
||||
#include "net/proxy_resolution/proxy_info.h"
|
||||
#include "services/network/public/mojom/network_context.mojom.h"
|
||||
|
||||
using content::BrowserThread;
|
||||
|
||||
namespace atom {
|
||||
|
||||
ResolveProxyHelper::ResolveProxyHelper(AtomBrowserContext* browser_context)
|
||||
: binding_(this), browser_context_(browser_context) {}
|
||||
|
||||
ResolveProxyHelper::~ResolveProxyHelper() {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
DCHECK(!owned_self_);
|
||||
DCHECK(!binding_.is_bound());
|
||||
// Clear all pending requests if the ProxyService is still alive.
|
||||
pending_requests_.clear();
|
||||
}
|
||||
|
||||
void ResolveProxyHelper::ResolveProxy(const GURL& url,
|
||||
ResolveProxyCallback callback) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
// Enqueue the pending request.
|
||||
pending_requests_.push_back(PendingRequest(url, std::move(callback)));
|
||||
|
||||
// If nothing is in progress, start.
|
||||
if (!binding_.is_bound()) {
|
||||
DCHECK_EQ(1u, pending_requests_.size());
|
||||
StartPendingRequest();
|
||||
}
|
||||
}
|
||||
|
||||
void ResolveProxyHelper::StartPendingRequest() {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
DCHECK(!binding_.is_bound());
|
||||
DCHECK(!pending_requests_.empty());
|
||||
|
||||
// Start the request.
|
||||
network::mojom::ProxyLookupClientPtr proxy_lookup_client;
|
||||
binding_.Bind(mojo::MakeRequest(&proxy_lookup_client));
|
||||
binding_.set_connection_error_handler(
|
||||
base::BindOnce(&ResolveProxyHelper::OnProxyLookupComplete,
|
||||
base::Unretained(this), net::ERR_ABORTED, base::nullopt));
|
||||
content::BrowserContext::GetDefaultStoragePartition(browser_context_)
|
||||
->GetNetworkContext()
|
||||
->LookUpProxyForURL(pending_requests_.front().url,
|
||||
std::move(proxy_lookup_client));
|
||||
}
|
||||
|
||||
void ResolveProxyHelper::OnProxyLookupComplete(
|
||||
int32_t net_error,
|
||||
const base::Optional<net::ProxyInfo>& proxy_info) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
DCHECK(!pending_requests_.empty());
|
||||
|
||||
binding_.Close();
|
||||
|
||||
// Clear the current (completed) request.
|
||||
PendingRequest completed_request = std::move(pending_requests_.front());
|
||||
pending_requests_.pop_front();
|
||||
|
||||
std::string proxy;
|
||||
if (proxy_info)
|
||||
proxy = proxy_info->ToPacString();
|
||||
|
||||
if (!completed_request.callback.is_null())
|
||||
std::move(completed_request.callback).Run(proxy);
|
||||
|
||||
// Start the next request.
|
||||
if (!pending_requests_.empty())
|
||||
StartPendingRequest();
|
||||
}
|
||||
|
||||
ResolveProxyHelper::PendingRequest::PendingRequest(
|
||||
const GURL& url,
|
||||
ResolveProxyCallback callback)
|
||||
: url(url), callback(std::move(callback)) {}
|
||||
|
||||
ResolveProxyHelper::PendingRequest::PendingRequest(
|
||||
ResolveProxyHelper::PendingRequest&& pending_request) = default;
|
||||
|
||||
ResolveProxyHelper::PendingRequest::~PendingRequest() noexcept = default;
|
||||
|
||||
ResolveProxyHelper::PendingRequest& ResolveProxyHelper::PendingRequest::
|
||||
operator=(ResolveProxyHelper::PendingRequest&& pending_request) noexcept =
|
||||
default;
|
||||
|
||||
} // namespace atom
|
75
shell/browser/net/resolve_proxy_helper.h
Normal file
75
shell/browser/net/resolve_proxy_helper.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
// Copyright (c) 2018 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_RESOLVE_PROXY_HELPER_H_
|
||||
#define ATOM_BROWSER_NET_RESOLVE_PROXY_HELPER_H_
|
||||
|
||||
#include <deque>
|
||||
#include <string>
|
||||
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/optional.h"
|
||||
#include "mojo/public/cpp/bindings/binding.h"
|
||||
#include "services/network/public/mojom/proxy_lookup_client.mojom.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class AtomBrowserContext;
|
||||
|
||||
class ResolveProxyHelper
|
||||
: public base::RefCountedThreadSafe<ResolveProxyHelper>,
|
||||
network::mojom::ProxyLookupClient {
|
||||
public:
|
||||
using ResolveProxyCallback = base::OnceCallback<void(std::string)>;
|
||||
|
||||
explicit ResolveProxyHelper(AtomBrowserContext* browser_context);
|
||||
|
||||
void ResolveProxy(const GURL& url, ResolveProxyCallback callback);
|
||||
|
||||
protected:
|
||||
~ResolveProxyHelper() override;
|
||||
|
||||
private:
|
||||
friend class base::RefCountedThreadSafe<ResolveProxyHelper>;
|
||||
// A PendingRequest is a resolve request that is in progress, or queued.
|
||||
struct PendingRequest {
|
||||
public:
|
||||
PendingRequest(const GURL& url, ResolveProxyCallback callback);
|
||||
PendingRequest(PendingRequest&& pending_request) noexcept;
|
||||
~PendingRequest();
|
||||
|
||||
PendingRequest& operator=(PendingRequest&& pending_request) noexcept;
|
||||
|
||||
GURL url;
|
||||
ResolveProxyCallback callback;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(PendingRequest);
|
||||
};
|
||||
|
||||
// Starts the first pending request.
|
||||
void StartPendingRequest();
|
||||
|
||||
// network::mojom::ProxyLookupClient implementation.
|
||||
void OnProxyLookupComplete(
|
||||
int32_t net_error,
|
||||
const base::Optional<net::ProxyInfo>& proxy_info) override;
|
||||
|
||||
// Self-reference. Owned as long as there's an outstanding proxy lookup.
|
||||
scoped_refptr<ResolveProxyHelper> owned_self_;
|
||||
|
||||
std::deque<PendingRequest> pending_requests_;
|
||||
// Binding for the currently in-progress request, if any.
|
||||
mojo::Binding<network::mojom::ProxyLookupClient> binding_;
|
||||
|
||||
// Weak Ref
|
||||
AtomBrowserContext* browser_context_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ResolveProxyHelper);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_RESOLVE_PROXY_HELPER_H_
|
268
shell/browser/net/system_network_context_manager.cc
Normal file
268
shell/browser/net/system_network_context_manager.cc
Normal file
|
@ -0,0 +1,268 @@
|
|||
// Copyright (c) 2018 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/system_network_context_manager.h"
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "atom/browser/atom_browser_client.h"
|
||||
#include "atom/browser/io_thread.h"
|
||||
#include "atom/common/application_info.h"
|
||||
#include "atom/common/options_switches.h"
|
||||
#include "base/command_line.h"
|
||||
#include "chrome/browser/browser_process.h"
|
||||
#include "chrome/browser/net/chrome_mojo_proxy_resolver_factory.h"
|
||||
#include "components/net_log/net_export_file_writer.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/network_service_instance.h"
|
||||
#include "content/public/common/content_features.h"
|
||||
#include "content/public/common/service_names.mojom.h"
|
||||
#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
|
||||
#include "net/net_buildflags.h"
|
||||
#include "services/network/network_service.h"
|
||||
#include "services/network/public/cpp/cross_thread_shared_url_loader_factory_info.h"
|
||||
#include "services/network/public/cpp/features.h"
|
||||
#include "services/network/public/cpp/shared_url_loader_factory.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// The global instance of the SystemNetworkContextmanager.
|
||||
SystemNetworkContextManager* g_system_network_context_manager = nullptr;
|
||||
|
||||
network::mojom::HttpAuthStaticParamsPtr CreateHttpAuthStaticParams() {
|
||||
network::mojom::HttpAuthStaticParamsPtr auth_static_params =
|
||||
network::mojom::HttpAuthStaticParams::New();
|
||||
|
||||
auth_static_params->supported_schemes = {"basic", "digest", "ntlm",
|
||||
"negotiate"};
|
||||
|
||||
return auth_static_params;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace atom {
|
||||
|
||||
network::mojom::HttpAuthDynamicParamsPtr CreateHttpAuthDynamicParams() {
|
||||
auto* command_line = base::CommandLine::ForCurrentProcess();
|
||||
network::mojom::HttpAuthDynamicParamsPtr auth_dynamic_params =
|
||||
network::mojom::HttpAuthDynamicParams::New();
|
||||
|
||||
auth_dynamic_params->server_whitelist =
|
||||
command_line->GetSwitchValueASCII(atom::switches::kAuthServerWhitelist);
|
||||
auth_dynamic_params->delegate_whitelist = command_line->GetSwitchValueASCII(
|
||||
atom::switches::kAuthNegotiateDelegateWhitelist);
|
||||
auth_dynamic_params->enable_negotiate_port =
|
||||
command_line->HasSwitch(atom::switches::kEnableAuthNegotiatePort);
|
||||
|
||||
return auth_dynamic_params;
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
|
||||
// SharedURLLoaderFactory backed by a SystemNetworkContextManager and its
|
||||
// network context. Transparently handles crashes.
|
||||
class SystemNetworkContextManager::URLLoaderFactoryForSystem
|
||||
: public network::SharedURLLoaderFactory {
|
||||
public:
|
||||
explicit URLLoaderFactoryForSystem(SystemNetworkContextManager* manager)
|
||||
: manager_(manager) {
|
||||
DETACH_FROM_SEQUENCE(sequence_checker_);
|
||||
}
|
||||
|
||||
// mojom::URLLoaderFactory implementation:
|
||||
void CreateLoaderAndStart(network::mojom::URLLoaderRequest request,
|
||||
int32_t routing_id,
|
||||
int32_t request_id,
|
||||
uint32_t options,
|
||||
const network::ResourceRequest& url_request,
|
||||
network::mojom::URLLoaderClientPtr client,
|
||||
const net::MutableNetworkTrafficAnnotationTag&
|
||||
traffic_annotation) override {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
if (!manager_)
|
||||
return;
|
||||
manager_->GetURLLoaderFactory()->CreateLoaderAndStart(
|
||||
std::move(request), routing_id, request_id, options, url_request,
|
||||
std::move(client), traffic_annotation);
|
||||
}
|
||||
|
||||
void Clone(network::mojom::URLLoaderFactoryRequest request) override {
|
||||
if (!manager_)
|
||||
return;
|
||||
manager_->GetURLLoaderFactory()->Clone(std::move(request));
|
||||
}
|
||||
|
||||
// SharedURLLoaderFactory implementation:
|
||||
std::unique_ptr<network::SharedURLLoaderFactoryInfo> Clone() override {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
return std::make_unique<network::CrossThreadSharedURLLoaderFactoryInfo>(
|
||||
this);
|
||||
}
|
||||
|
||||
void Shutdown() { manager_ = nullptr; }
|
||||
|
||||
private:
|
||||
friend class base::RefCounted<URLLoaderFactoryForSystem>;
|
||||
~URLLoaderFactoryForSystem() override {}
|
||||
|
||||
SEQUENCE_CHECKER(sequence_checker_);
|
||||
SystemNetworkContextManager* manager_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(URLLoaderFactoryForSystem);
|
||||
};
|
||||
|
||||
network::mojom::NetworkContext* SystemNetworkContextManager::GetContext() {
|
||||
if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) {
|
||||
// SetUp should already have been called.
|
||||
DCHECK(io_thread_network_context_);
|
||||
return io_thread_network_context_.get();
|
||||
}
|
||||
|
||||
if (!network_service_network_context_ ||
|
||||
network_service_network_context_.encountered_error()) {
|
||||
// This should call into OnNetworkServiceCreated(), which will re-create
|
||||
// the network service, if needed. There's a chance that it won't be
|
||||
// invoked, if the NetworkContext has encountered an error but the
|
||||
// NetworkService has not yet noticed its pipe was closed. In that case,
|
||||
// trying to create a new NetworkContext would fail, anyways, and hopefully
|
||||
// a new NetworkContext will be created on the next GetContext() call.
|
||||
content::GetNetworkService();
|
||||
DCHECK(network_service_network_context_);
|
||||
}
|
||||
return network_service_network_context_.get();
|
||||
}
|
||||
|
||||
network::mojom::URLLoaderFactory*
|
||||
SystemNetworkContextManager::GetURLLoaderFactory() {
|
||||
// Create the URLLoaderFactory as needed.
|
||||
if (url_loader_factory_ && !url_loader_factory_.encountered_error()) {
|
||||
return url_loader_factory_.get();
|
||||
}
|
||||
|
||||
network::mojom::URLLoaderFactoryParamsPtr params =
|
||||
network::mojom::URLLoaderFactoryParams::New();
|
||||
params->process_id = network::mojom::kBrowserProcessId;
|
||||
params->is_corb_enabled = false;
|
||||
GetContext()->CreateURLLoaderFactory(mojo::MakeRequest(&url_loader_factory_),
|
||||
std::move(params));
|
||||
return url_loader_factory_.get();
|
||||
}
|
||||
|
||||
scoped_refptr<network::SharedURLLoaderFactory>
|
||||
SystemNetworkContextManager::GetSharedURLLoaderFactory() {
|
||||
return shared_url_loader_factory_;
|
||||
}
|
||||
|
||||
net_log::NetExportFileWriter*
|
||||
SystemNetworkContextManager::GetNetExportFileWriter() {
|
||||
if (!net_export_file_writer_) {
|
||||
net_export_file_writer_ = std::make_unique<net_log::NetExportFileWriter>();
|
||||
}
|
||||
return net_export_file_writer_.get();
|
||||
}
|
||||
|
||||
network::mojom::NetworkContextParamsPtr
|
||||
SystemNetworkContextManager::CreateDefaultNetworkContextParams() {
|
||||
network::mojom::NetworkContextParamsPtr network_context_params =
|
||||
network::mojom::NetworkContextParams::New();
|
||||
|
||||
network_context_params->enable_brotli = true;
|
||||
|
||||
network_context_params->enable_referrers = true;
|
||||
|
||||
network_context_params->proxy_resolver_factory =
|
||||
ChromeMojoProxyResolverFactory::CreateWithSelfOwnedReceiver();
|
||||
|
||||
return network_context_params;
|
||||
}
|
||||
|
||||
void SystemNetworkContextManager::SetUp(
|
||||
network::mojom::NetworkContextRequest* network_context_request,
|
||||
network::mojom::NetworkContextParamsPtr* network_context_params,
|
||||
network::mojom::HttpAuthStaticParamsPtr* http_auth_static_params,
|
||||
network::mojom::HttpAuthDynamicParamsPtr* http_auth_dynamic_params) {
|
||||
if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) {
|
||||
*network_context_request = mojo::MakeRequest(&io_thread_network_context_);
|
||||
*network_context_params = CreateNetworkContextParams();
|
||||
} else {
|
||||
// Just use defaults if the network service is enabled, since
|
||||
// CreateNetworkContextParams() can only be called once.
|
||||
*network_context_params = CreateDefaultNetworkContextParams();
|
||||
}
|
||||
*http_auth_static_params = CreateHttpAuthStaticParams();
|
||||
*http_auth_dynamic_params = atom::CreateHttpAuthDynamicParams();
|
||||
}
|
||||
|
||||
// static
|
||||
SystemNetworkContextManager* SystemNetworkContextManager::CreateInstance(
|
||||
PrefService* pref_service) {
|
||||
DCHECK(!g_system_network_context_manager);
|
||||
g_system_network_context_manager =
|
||||
new SystemNetworkContextManager(pref_service);
|
||||
return g_system_network_context_manager;
|
||||
}
|
||||
|
||||
// static
|
||||
SystemNetworkContextManager* SystemNetworkContextManager::GetInstance() {
|
||||
return g_system_network_context_manager;
|
||||
}
|
||||
|
||||
// static
|
||||
void SystemNetworkContextManager::DeleteInstance() {
|
||||
DCHECK(g_system_network_context_manager);
|
||||
delete g_system_network_context_manager;
|
||||
}
|
||||
|
||||
SystemNetworkContextManager::SystemNetworkContextManager(
|
||||
PrefService* pref_service)
|
||||
: proxy_config_monitor_(pref_service) {
|
||||
shared_url_loader_factory_ = new URLLoaderFactoryForSystem(this);
|
||||
}
|
||||
|
||||
SystemNetworkContextManager::~SystemNetworkContextManager() {
|
||||
shared_url_loader_factory_->Shutdown();
|
||||
}
|
||||
|
||||
void SystemNetworkContextManager::OnNetworkServiceCreated(
|
||||
network::mojom::NetworkService* network_service) {
|
||||
if (!base::FeatureList::IsEnabled(network::features::kNetworkService))
|
||||
return;
|
||||
|
||||
network_service->SetUpHttpAuth(CreateHttpAuthStaticParams());
|
||||
network_service->ConfigureHttpAuthPrefs(atom::CreateHttpAuthDynamicParams());
|
||||
|
||||
// The system NetworkContext must be created first, since it sets
|
||||
// |primary_network_context| to true.
|
||||
network_service->CreateNetworkContext(
|
||||
MakeRequest(&network_service_network_context_),
|
||||
CreateNetworkContextParams());
|
||||
}
|
||||
|
||||
network::mojom::NetworkContextParamsPtr
|
||||
SystemNetworkContextManager::CreateNetworkContextParams() {
|
||||
// TODO(mmenke): Set up parameters here (in memory cookie store, etc).
|
||||
network::mojom::NetworkContextParamsPtr network_context_params =
|
||||
CreateDefaultNetworkContextParams();
|
||||
|
||||
network_context_params->context_name = std::string("system");
|
||||
|
||||
network_context_params->user_agent =
|
||||
atom::AtomBrowserClient::Get()->GetUserAgent();
|
||||
|
||||
network_context_params->http_cache_enabled = false;
|
||||
|
||||
#if !BUILDFLAG(DISABLE_FTP_SUPPORT)
|
||||
network_context_params->enable_ftp_url_support = true;
|
||||
#endif
|
||||
|
||||
network_context_params->primary_network_context = true;
|
||||
|
||||
proxy_config_monitor_.AddToNetworkContextParams(network_context_params.get());
|
||||
|
||||
return network_context_params;
|
||||
}
|
128
shell/browser/net/system_network_context_manager.h
Normal file
128
shell/browser/net/system_network_context_manager.h
Normal file
|
@ -0,0 +1,128 @@
|
|||
// Copyright (c) 2018 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_SYSTEM_NETWORK_CONTEXT_MANAGER_H_
|
||||
#define ATOM_BROWSER_NET_SYSTEM_NETWORK_CONTEXT_MANAGER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/optional.h"
|
||||
#include "chrome/browser/net/proxy_config_monitor.h"
|
||||
#include "services/network/public/mojom/network_context.mojom.h"
|
||||
#include "services/network/public/mojom/network_service.mojom.h"
|
||||
|
||||
namespace network {
|
||||
namespace mojom {
|
||||
class URLLoaderFactory;
|
||||
}
|
||||
class SharedURLLoaderFactory;
|
||||
} // namespace network
|
||||
|
||||
namespace net_log {
|
||||
class NetExportFileWriter;
|
||||
}
|
||||
|
||||
namespace atom {
|
||||
network::mojom::HttpAuthDynamicParamsPtr CreateHttpAuthDynamicParams();
|
||||
}
|
||||
|
||||
// Responsible for creating and managing access to the system NetworkContext.
|
||||
// Lives on the UI thread. The NetworkContext this owns is intended for requests
|
||||
// not associated with a session. It stores no data on disk, and has no HTTP
|
||||
// cache, but it does have ephemeral cookie and channel ID stores.
|
||||
//
|
||||
// This class is also responsible for configuring global NetworkService state.
|
||||
//
|
||||
// The "system" NetworkContext will either share a URLRequestContext with
|
||||
// IOThread's SystemURLRequestContext and be part of IOThread's NetworkService
|
||||
// (If the network service is disabled) or be an independent NetworkContext
|
||||
// using the actual network service.
|
||||
class SystemNetworkContextManager {
|
||||
public:
|
||||
~SystemNetworkContextManager();
|
||||
|
||||
// Creates the global instance of SystemNetworkContextManager. If an
|
||||
// instance already exists, this will cause a DCHECK failure.
|
||||
static SystemNetworkContextManager* CreateInstance(PrefService* pref_service);
|
||||
|
||||
// Gets the global SystemNetworkContextManager instance.
|
||||
static SystemNetworkContextManager* GetInstance();
|
||||
|
||||
// Destroys the global SystemNetworkContextManager instance.
|
||||
static void DeleteInstance();
|
||||
|
||||
// Returns default set of parameters for configuring the network service.
|
||||
network::mojom::NetworkContextParamsPtr CreateDefaultNetworkContextParams();
|
||||
|
||||
// Initializes |network_context_params| as needed to set up a system
|
||||
// NetworkContext. If the network service is disabled,
|
||||
// |network_context_request| will be for the NetworkContext used by the
|
||||
// SystemNetworkContextManager. Otherwise, this method can still be used to
|
||||
// help set up the IOThread's in-process URLRequestContext.
|
||||
//
|
||||
// Must be called before the system NetworkContext is first used.
|
||||
void SetUp(
|
||||
network::mojom::NetworkContextRequest* network_context_request,
|
||||
network::mojom::NetworkContextParamsPtr* network_context_params,
|
||||
network::mojom::HttpAuthStaticParamsPtr* http_auth_static_params,
|
||||
network::mojom::HttpAuthDynamicParamsPtr* http_auth_dynamic_params);
|
||||
|
||||
// Returns the System NetworkContext. May only be called after SetUp(). Does
|
||||
// any initialization of the NetworkService that may be needed when first
|
||||
// called.
|
||||
network::mojom::NetworkContext* GetContext();
|
||||
|
||||
// Returns a URLLoaderFactory owned by the SystemNetworkContextManager that is
|
||||
// backed by the SystemNetworkContext. Allows sharing of the URLLoaderFactory.
|
||||
// Prefer this to creating a new one. Call Clone() on the value returned by
|
||||
// this method to get a URLLoaderFactory that can be used on other threads.
|
||||
network::mojom::URLLoaderFactory* GetURLLoaderFactory();
|
||||
|
||||
// Returns a SharedURLLoaderFactory owned by the SystemNetworkContextManager
|
||||
// that is backed by the SystemNetworkContext.
|
||||
scoped_refptr<network::SharedURLLoaderFactory> GetSharedURLLoaderFactory();
|
||||
|
||||
// Returns a shared global NetExportFileWriter instance.
|
||||
net_log::NetExportFileWriter* GetNetExportFileWriter();
|
||||
|
||||
// Called when content creates a NetworkService. Creates the
|
||||
// SystemNetworkContext, if the network service is enabled.
|
||||
void OnNetworkServiceCreated(network::mojom::NetworkService* network_service);
|
||||
|
||||
private:
|
||||
class URLLoaderFactoryForSystem;
|
||||
|
||||
explicit SystemNetworkContextManager(PrefService* pref_service);
|
||||
|
||||
// Creates parameters for the NetworkContext. May only be called once, since
|
||||
// it initializes some class members.
|
||||
network::mojom::NetworkContextParamsPtr CreateNetworkContextParams();
|
||||
|
||||
ProxyConfigMonitor proxy_config_monitor_;
|
||||
|
||||
// NetworkContext using the network service, if the network service is
|
||||
// enabled. nullptr, otherwise.
|
||||
network::mojom::NetworkContextPtr network_service_network_context_;
|
||||
|
||||
// This is a NetworkContext that wraps the IOThread's SystemURLRequestContext.
|
||||
// Always initialized in SetUp, but it's only returned by Context() when the
|
||||
// network service is disabled.
|
||||
network::mojom::NetworkContextPtr io_thread_network_context_;
|
||||
|
||||
// URLLoaderFactory backed by the NetworkContext returned by GetContext(), so
|
||||
// consumers don't all need to create their own factory.
|
||||
scoped_refptr<URLLoaderFactoryForSystem> shared_url_loader_factory_;
|
||||
network::mojom::URLLoaderFactoryPtr url_loader_factory_;
|
||||
|
||||
// Initialized on first access.
|
||||
std::unique_ptr<net_log::NetExportFileWriter> net_export_file_writer_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(SystemNetworkContextManager);
|
||||
};
|
||||
|
||||
#endif // ATOM_BROWSER_NET_SYSTEM_NETWORK_CONTEXT_MANAGER_H_
|
102
shell/browser/net/url_pipe_loader.cc
Normal file
102
shell/browser/net/url_pipe_loader.cc
Normal file
|
@ -0,0 +1,102 @@
|
|||
// Copyright (c) 2019 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/url_pipe_loader.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "services/network/public/cpp/shared_url_loader_factory.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
URLPipeLoader::URLPipeLoader(
|
||||
scoped_refptr<network::SharedURLLoaderFactory> factory,
|
||||
std::unique_ptr<network::ResourceRequest> request,
|
||||
network::mojom::URLLoaderRequest loader,
|
||||
network::mojom::URLLoaderClientPtr client,
|
||||
const net::NetworkTrafficAnnotationTag& annotation,
|
||||
base::DictionaryValue upload_data)
|
||||
: binding_(this, std::move(loader)),
|
||||
client_(std::move(client)),
|
||||
weak_factory_(this) {
|
||||
binding_.set_connection_error_handler(base::BindOnce(
|
||||
&URLPipeLoader::NotifyComplete, base::Unretained(this), net::ERR_FAILED));
|
||||
|
||||
// PostTask since it might destruct.
|
||||
base::SequencedTaskRunnerHandle::Get()->PostTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce(&URLPipeLoader::Start, weak_factory_.GetWeakPtr(), factory,
|
||||
std::move(request), annotation, std::move(upload_data)));
|
||||
}
|
||||
|
||||
URLPipeLoader::~URLPipeLoader() = default;
|
||||
|
||||
void URLPipeLoader::Start(
|
||||
scoped_refptr<network::SharedURLLoaderFactory> factory,
|
||||
std::unique_ptr<network::ResourceRequest> request,
|
||||
const net::NetworkTrafficAnnotationTag& annotation,
|
||||
base::DictionaryValue upload_data) {
|
||||
loader_ = network::SimpleURLLoader::Create(std::move(request), annotation);
|
||||
loader_->SetOnResponseStartedCallback(base::Bind(
|
||||
&URLPipeLoader::OnResponseStarted, weak_factory_.GetWeakPtr()));
|
||||
|
||||
// TODO(zcbenz): The old protocol API only supports string as upload data,
|
||||
// we should seek to support more types in future.
|
||||
std::string content_type, data;
|
||||
if (upload_data.GetString("contentType", &content_type) &&
|
||||
upload_data.GetString("data", &data))
|
||||
loader_->AttachStringForUpload(data, content_type);
|
||||
|
||||
loader_->DownloadAsStream(factory.get(), this);
|
||||
}
|
||||
|
||||
void URLPipeLoader::NotifyComplete(int result) {
|
||||
client_->OnComplete(network::URLLoaderCompletionStatus(result));
|
||||
delete this;
|
||||
}
|
||||
|
||||
void URLPipeLoader::OnResponseStarted(
|
||||
const GURL& final_url,
|
||||
const network::ResourceResponseHead& response_head) {
|
||||
mojo::ScopedDataPipeProducerHandle producer;
|
||||
mojo::ScopedDataPipeConsumerHandle consumer;
|
||||
MojoResult rv = mojo::CreateDataPipe(nullptr, &producer, &consumer);
|
||||
if (rv != MOJO_RESULT_OK) {
|
||||
NotifyComplete(net::ERR_INSUFFICIENT_RESOURCES);
|
||||
return;
|
||||
}
|
||||
|
||||
producer_ =
|
||||
std::make_unique<mojo::StringDataPipeProducer>(std::move(producer));
|
||||
|
||||
client_->OnReceiveResponse(response_head);
|
||||
client_->OnStartLoadingResponseBody(std::move(consumer));
|
||||
}
|
||||
|
||||
void URLPipeLoader::OnWrite(base::OnceClosure resume, MojoResult result) {
|
||||
if (result == MOJO_RESULT_OK)
|
||||
std::move(resume).Run();
|
||||
else
|
||||
NotifyComplete(net::ERR_FAILED);
|
||||
}
|
||||
|
||||
void URLPipeLoader::OnDataReceived(base::StringPiece string_piece,
|
||||
base::OnceClosure resume) {
|
||||
producer_->Write(
|
||||
string_piece,
|
||||
mojo::StringDataPipeProducer::AsyncWritingMode::
|
||||
STRING_STAYS_VALID_UNTIL_COMPLETION,
|
||||
base::BindOnce(&URLPipeLoader::OnWrite, weak_factory_.GetWeakPtr(),
|
||||
std::move(resume)));
|
||||
}
|
||||
|
||||
void URLPipeLoader::OnRetry(base::OnceClosure start_retry) {
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
void URLPipeLoader::OnComplete(bool success) {
|
||||
NotifyComplete(loader_->NetError());
|
||||
}
|
||||
|
||||
} // namespace atom
|
82
shell/browser/net/url_pipe_loader.h
Normal file
82
shell/browser/net/url_pipe_loader.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
// Copyright (c) 2019 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_URL_PIPE_LOADER_H_
|
||||
#define ATOM_BROWSER_NET_URL_PIPE_LOADER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "mojo/public/cpp/bindings/strong_binding.h"
|
||||
#include "mojo/public/cpp/system/string_data_pipe_producer.h"
|
||||
#include "services/network/public/cpp/simple_url_loader.h"
|
||||
#include "services/network/public/cpp/simple_url_loader_stream_consumer.h"
|
||||
#include "services/network/public/mojom/url_loader.mojom.h"
|
||||
|
||||
namespace network {
|
||||
class SharedURLLoaderFactory;
|
||||
}
|
||||
|
||||
namespace atom {
|
||||
|
||||
// Read data from URL and pipe it to NetworkService.
|
||||
//
|
||||
// Different from creating a new loader for the URL directly, protocol handlers
|
||||
// using this loader can work around CORS restrictions.
|
||||
//
|
||||
// This class manages its own lifetime and should delete itself when the
|
||||
// connection is lost or finished.
|
||||
class URLPipeLoader : public network::mojom::URLLoader,
|
||||
public network::SimpleURLLoaderStreamConsumer {
|
||||
public:
|
||||
URLPipeLoader(scoped_refptr<network::SharedURLLoaderFactory> factory,
|
||||
std::unique_ptr<network::ResourceRequest> request,
|
||||
network::mojom::URLLoaderRequest loader,
|
||||
network::mojom::URLLoaderClientPtr client,
|
||||
const net::NetworkTrafficAnnotationTag& annotation,
|
||||
base::DictionaryValue upload_data);
|
||||
|
||||
private:
|
||||
~URLPipeLoader() override;
|
||||
|
||||
void Start(scoped_refptr<network::SharedURLLoaderFactory> factory,
|
||||
std::unique_ptr<network::ResourceRequest> request,
|
||||
const net::NetworkTrafficAnnotationTag& annotation,
|
||||
base::DictionaryValue upload_data);
|
||||
void NotifyComplete(int result);
|
||||
void OnResponseStarted(const GURL& final_url,
|
||||
const network::ResourceResponseHead& response_head);
|
||||
void OnWrite(base::OnceClosure resume, MojoResult result);
|
||||
|
||||
// SimpleURLLoaderStreamConsumer:
|
||||
void OnDataReceived(base::StringPiece string_piece,
|
||||
base::OnceClosure resume) override;
|
||||
void OnComplete(bool success) override;
|
||||
void OnRetry(base::OnceClosure start_retry) override;
|
||||
|
||||
// URLLoader:
|
||||
void FollowRedirect(const std::vector<std::string>& removed_headers,
|
||||
const net::HttpRequestHeaders& modified_headers,
|
||||
const base::Optional<GURL>& new_url) override {}
|
||||
void ProceedWithResponse() override {}
|
||||
void SetPriority(net::RequestPriority priority,
|
||||
int32_t intra_priority_value) override {}
|
||||
void PauseReadingBodyFromNet() override {}
|
||||
void ResumeReadingBodyFromNet() override {}
|
||||
|
||||
mojo::Binding<network::mojom::URLLoader> binding_;
|
||||
network::mojom::URLLoaderClientPtr client_;
|
||||
|
||||
std::unique_ptr<mojo::StringDataPipeProducer> producer_;
|
||||
std::unique_ptr<network::SimpleURLLoader> loader_;
|
||||
|
||||
base::WeakPtrFactory<URLPipeLoader> weak_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(URLPipeLoader);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_URL_PIPE_LOADER_H_
|
37
shell/browser/net/url_request_about_job.cc
Normal file
37
shell/browser/net/url_request_about_job.cc
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) 2016 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/url_request_about_job.h"
|
||||
|
||||
#include "base/threading/thread_task_runner_handle.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
URLRequestAboutJob::URLRequestAboutJob(net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate)
|
||||
: net::URLRequestJob(request, network_delegate), weak_ptr_factory_(this) {}
|
||||
|
||||
void URLRequestAboutJob::Start() {
|
||||
base::ThreadTaskRunnerHandle::Get()->PostTask(
|
||||
FROM_HERE, base::BindOnce(&URLRequestAboutJob::StartAsync,
|
||||
weak_ptr_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
void URLRequestAboutJob::Kill() {
|
||||
weak_ptr_factory_.InvalidateWeakPtrs();
|
||||
URLRequestJob::Kill();
|
||||
}
|
||||
|
||||
bool URLRequestAboutJob::GetMimeType(std::string* mime_type) const {
|
||||
*mime_type = "text/html";
|
||||
return true;
|
||||
}
|
||||
|
||||
URLRequestAboutJob::~URLRequestAboutJob() {}
|
||||
|
||||
void URLRequestAboutJob::StartAsync() {
|
||||
NotifyHeadersComplete();
|
||||
}
|
||||
|
||||
} // namespace atom
|
35
shell/browser/net/url_request_about_job.h
Normal file
35
shell/browser/net/url_request_about_job.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) 2016 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_URL_REQUEST_ABOUT_JOB_H_
|
||||
#define ATOM_BROWSER_NET_URL_REQUEST_ABOUT_JOB_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "net/url_request/url_request_job.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class URLRequestAboutJob : public net::URLRequestJob {
|
||||
public:
|
||||
URLRequestAboutJob(net::URLRequest*, net::NetworkDelegate*);
|
||||
|
||||
// URLRequestJob:
|
||||
void Start() override;
|
||||
void Kill() override;
|
||||
bool GetMimeType(std::string* mime_type) const override;
|
||||
|
||||
private:
|
||||
~URLRequestAboutJob() override;
|
||||
void StartAsync();
|
||||
|
||||
base::WeakPtrFactory<URLRequestAboutJob> weak_ptr_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(URLRequestAboutJob);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_URL_REQUEST_ABOUT_JOB_H_
|
124
shell/browser/net/url_request_async_asar_job.cc
Normal file
124
shell/browser/net/url_request_async_asar_job.cc
Normal file
|
@ -0,0 +1,124 @@
|
|||
// Copyright (c) 2014 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/url_request_async_asar_job.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "atom/common/atom_constants.h"
|
||||
#include "atom/common/native_mate_converters/net_converter.h"
|
||||
#include "atom/common/native_mate_converters/v8_value_converter.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/task/post_task.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
void BeforeStartInUI(base::WeakPtr<URLRequestAsyncAsarJob> job,
|
||||
mate::Arguments* args) {
|
||||
v8::Local<v8::Value> value;
|
||||
int error = net::OK;
|
||||
std::unique_ptr<base::Value> request_options = nullptr;
|
||||
|
||||
if (args->GetNext(&value)) {
|
||||
V8ValueConverter converter;
|
||||
v8::Local<v8::Context> context = args->isolate()->GetCurrentContext();
|
||||
request_options = converter.FromV8Value(value, context);
|
||||
}
|
||||
|
||||
if (request_options) {
|
||||
JsAsker::IsErrorOptions(request_options.get(), &error);
|
||||
} else {
|
||||
error = net::ERR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::IO},
|
||||
base::BindOnce(&URLRequestAsyncAsarJob::StartAsync, job,
|
||||
std::move(request_options), error));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
URLRequestAsyncAsarJob::URLRequestAsyncAsarJob(
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate)
|
||||
: asar::URLRequestAsarJob(request, network_delegate), weak_factory_(this) {}
|
||||
|
||||
URLRequestAsyncAsarJob::~URLRequestAsyncAsarJob() = default;
|
||||
|
||||
void URLRequestAsyncAsarJob::Start() {
|
||||
auto request_details = std::make_unique<base::DictionaryValue>();
|
||||
FillRequestDetails(request_details.get(), request());
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::UI},
|
||||
base::BindOnce(
|
||||
&JsAsker::AskForOptions, base::Unretained(isolate()), handler(),
|
||||
std::move(request_details),
|
||||
base::BindOnce(&BeforeStartInUI, weak_factory_.GetWeakPtr())));
|
||||
}
|
||||
|
||||
void URLRequestAsyncAsarJob::StartAsync(std::unique_ptr<base::Value> options,
|
||||
int error) {
|
||||
if (error != net::OK) {
|
||||
NotifyStartError(
|
||||
net::URLRequestStatus(net::URLRequestStatus::FAILED, error));
|
||||
return;
|
||||
}
|
||||
|
||||
std::string file_path;
|
||||
response_headers_ = new net::HttpResponseHeaders("HTTP/1.1 200 OK");
|
||||
if (options->is_dict()) {
|
||||
base::DictionaryValue* dict =
|
||||
static_cast<base::DictionaryValue*>(options.get());
|
||||
base::Value* pathValue =
|
||||
dict->FindKeyOfType("path", base::Value::Type::STRING);
|
||||
if (pathValue) {
|
||||
file_path = pathValue->GetString();
|
||||
}
|
||||
base::Value* headersValue =
|
||||
dict->FindKeyOfType("headers", base::Value::Type::DICTIONARY);
|
||||
if (headersValue) {
|
||||
for (const auto& iter : headersValue->DictItems()) {
|
||||
response_headers_->AddHeader(iter.first + ": " +
|
||||
iter.second.GetString());
|
||||
}
|
||||
}
|
||||
} else if (options->is_string()) {
|
||||
file_path = options->GetString();
|
||||
}
|
||||
response_headers_->AddHeader(kCORSHeader);
|
||||
|
||||
if (file_path.empty()) {
|
||||
NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED,
|
||||
net::ERR_NOT_IMPLEMENTED));
|
||||
} else {
|
||||
asar::URLRequestAsarJob::Initialize(
|
||||
base::CreateSequencedTaskRunnerWithTraits(
|
||||
{base::MayBlock(), base::TaskPriority::USER_VISIBLE,
|
||||
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}),
|
||||
#if defined(OS_WIN)
|
||||
base::FilePath(base::UTF8ToWide(file_path)));
|
||||
#else
|
||||
base::FilePath(file_path));
|
||||
#endif
|
||||
asar::URLRequestAsarJob::Start();
|
||||
}
|
||||
}
|
||||
|
||||
void URLRequestAsyncAsarJob::Kill() {
|
||||
weak_factory_.InvalidateWeakPtrs();
|
||||
URLRequestAsarJob::Kill();
|
||||
}
|
||||
|
||||
void URLRequestAsyncAsarJob::GetResponseInfo(net::HttpResponseInfo* info) {
|
||||
info->headers = response_headers_;
|
||||
}
|
||||
|
||||
} // namespace atom
|
37
shell/browser/net/url_request_async_asar_job.h
Normal file
37
shell/browser/net/url_request_async_asar_job.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) 2015 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_URL_REQUEST_ASYNC_ASAR_JOB_H_
|
||||
#define ATOM_BROWSER_NET_URL_REQUEST_ASYNC_ASAR_JOB_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "atom/browser/net/asar/url_request_asar_job.h"
|
||||
#include "atom/browser/net/js_asker.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
// Like URLRequestAsarJob, but asks the JavaScript handler for file path.
|
||||
class URLRequestAsyncAsarJob : public asar::URLRequestAsarJob, public JsAsker {
|
||||
public:
|
||||
URLRequestAsyncAsarJob(net::URLRequest*, net::NetworkDelegate*);
|
||||
~URLRequestAsyncAsarJob() override;
|
||||
|
||||
void StartAsync(std::unique_ptr<base::Value> options, int error);
|
||||
|
||||
// URLRequestJob:
|
||||
void Start() override;
|
||||
void GetResponseInfo(net::HttpResponseInfo* info) override;
|
||||
void Kill() override;
|
||||
|
||||
private:
|
||||
scoped_refptr<net::HttpResponseHeaders> response_headers_;
|
||||
base::WeakPtrFactory<URLRequestAsyncAsarJob> weak_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(URLRequestAsyncAsarJob);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_URL_REQUEST_ASYNC_ASAR_JOB_H_
|
155
shell/browser/net/url_request_buffer_job.cc
Normal file
155
shell/browser/net/url_request_buffer_job.cc
Normal file
|
@ -0,0 +1,155 @@
|
|||
// Copyright (c) 2013 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/url_request_buffer_job.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "atom/common/atom_constants.h"
|
||||
#include "atom/common/native_mate_converters/net_converter.h"
|
||||
#include "atom/common/native_mate_converters/v8_value_converter.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/task/post_task.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "net/base/mime_util.h"
|
||||
#include "net/base/net_errors.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
std::string GetExtFromURL(const GURL& url) {
|
||||
std::string spec = url.spec();
|
||||
size_t index = spec.find_last_of('.');
|
||||
if (index == std::string::npos || index == spec.size())
|
||||
return std::string();
|
||||
return spec.substr(index + 1, spec.size() - index - 1);
|
||||
}
|
||||
|
||||
void BeforeStartInUI(base::WeakPtr<URLRequestBufferJob> job,
|
||||
mate::Arguments* args) {
|
||||
v8::Local<v8::Value> value;
|
||||
int error = net::OK;
|
||||
std::unique_ptr<base::Value> request_options = nullptr;
|
||||
|
||||
if (args->GetNext(&value)) {
|
||||
V8ValueConverter converter;
|
||||
v8::Local<v8::Context> context = args->isolate()->GetCurrentContext();
|
||||
request_options = converter.FromV8Value(value, context);
|
||||
}
|
||||
|
||||
if (request_options) {
|
||||
JsAsker::IsErrorOptions(request_options.get(), &error);
|
||||
} else {
|
||||
error = net::ERR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO},
|
||||
base::BindOnce(&URLRequestBufferJob::StartAsync, job,
|
||||
std::move(request_options), error));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
URLRequestBufferJob::URLRequestBufferJob(net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate)
|
||||
: net::URLRequestSimpleJob(request, network_delegate),
|
||||
status_code_(net::HTTP_NOT_IMPLEMENTED),
|
||||
weak_factory_(this) {}
|
||||
|
||||
URLRequestBufferJob::~URLRequestBufferJob() = default;
|
||||
|
||||
void URLRequestBufferJob::Start() {
|
||||
auto request_details = std::make_unique<base::DictionaryValue>();
|
||||
FillRequestDetails(request_details.get(), request());
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::UI},
|
||||
base::BindOnce(
|
||||
&JsAsker::AskForOptions, base::Unretained(isolate()), handler(),
|
||||
std::move(request_details),
|
||||
base::BindOnce(&BeforeStartInUI, weak_factory_.GetWeakPtr())));
|
||||
}
|
||||
|
||||
void URLRequestBufferJob::StartAsync(std::unique_ptr<base::Value> options,
|
||||
int error) {
|
||||
if (error != net::OK) {
|
||||
NotifyStartError(
|
||||
net::URLRequestStatus(net::URLRequestStatus::FAILED, error));
|
||||
return;
|
||||
}
|
||||
|
||||
const base::Value* binary = nullptr;
|
||||
if (options->is_dict()) {
|
||||
base::DictionaryValue* dict =
|
||||
static_cast<base::DictionaryValue*>(options.get());
|
||||
dict->GetString("mimeType", &mime_type_);
|
||||
dict->GetString("charset", &charset_);
|
||||
dict->GetBinary("data", &binary);
|
||||
} else if (options->is_blob()) {
|
||||
binary = options.get();
|
||||
}
|
||||
|
||||
if (mime_type_.empty()) {
|
||||
std::string ext = GetExtFromURL(request()->url());
|
||||
#if defined(OS_WIN)
|
||||
net::GetWellKnownMimeTypeFromExtension(base::UTF8ToUTF16(ext), &mime_type_);
|
||||
#else
|
||||
net::GetWellKnownMimeTypeFromExtension(ext, &mime_type_);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!binary) {
|
||||
NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED,
|
||||
net::ERR_NOT_IMPLEMENTED));
|
||||
return;
|
||||
}
|
||||
|
||||
data_ = new base::RefCountedBytes(
|
||||
reinterpret_cast<const unsigned char*>(binary->GetBlob().data()),
|
||||
binary->GetBlob().size());
|
||||
status_code_ = net::HTTP_OK;
|
||||
net::URLRequestSimpleJob::Start();
|
||||
}
|
||||
|
||||
void URLRequestBufferJob::Kill() {
|
||||
weak_factory_.InvalidateWeakPtrs();
|
||||
net::URLRequestSimpleJob::Kill();
|
||||
}
|
||||
|
||||
void URLRequestBufferJob::GetResponseInfo(net::HttpResponseInfo* info) {
|
||||
std::string status("HTTP/1.1 ");
|
||||
status.append(base::NumberToString(status_code_));
|
||||
status.append(" ");
|
||||
status.append(net::GetHttpReasonPhrase(status_code_));
|
||||
status.append("\0\0", 2);
|
||||
auto* headers = new net::HttpResponseHeaders(status);
|
||||
|
||||
headers->AddHeader(kCORSHeader);
|
||||
|
||||
if (!mime_type_.empty()) {
|
||||
std::string content_type_header(net::HttpRequestHeaders::kContentType);
|
||||
content_type_header.append(": ");
|
||||
content_type_header.append(mime_type_);
|
||||
headers->AddHeader(content_type_header);
|
||||
}
|
||||
|
||||
info->headers = headers;
|
||||
}
|
||||
|
||||
int URLRequestBufferJob::GetRefCountedData(
|
||||
std::string* mime_type,
|
||||
std::string* charset,
|
||||
scoped_refptr<base::RefCountedMemory>* data,
|
||||
net::CompletionOnceCallback callback) const {
|
||||
*mime_type = mime_type_;
|
||||
*charset = charset_;
|
||||
*data = data_;
|
||||
return net::OK;
|
||||
}
|
||||
|
||||
} // namespace atom
|
49
shell/browser/net/url_request_buffer_job.h
Normal file
49
shell/browser/net/url_request_buffer_job.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Copyright (c) 2013 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_URL_REQUEST_BUFFER_JOB_H_
|
||||
#define ATOM_BROWSER_NET_URL_REQUEST_BUFFER_JOB_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "atom/browser/net/js_asker.h"
|
||||
#include "base/memory/ref_counted_memory.h"
|
||||
#include "net/http/http_status_code.h"
|
||||
#include "net/url_request/url_request_simple_job.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class URLRequestBufferJob : public JsAsker, public net::URLRequestSimpleJob {
|
||||
public:
|
||||
URLRequestBufferJob(net::URLRequest*, net::NetworkDelegate*);
|
||||
~URLRequestBufferJob() override;
|
||||
|
||||
void StartAsync(std::unique_ptr<base::Value> options, int error);
|
||||
|
||||
// URLRequestJob:
|
||||
void Start() override;
|
||||
void GetResponseInfo(net::HttpResponseInfo* info) override;
|
||||
void Kill() override;
|
||||
|
||||
// URLRequestSimpleJob:
|
||||
int GetRefCountedData(std::string* mime_type,
|
||||
std::string* charset,
|
||||
scoped_refptr<base::RefCountedMemory>* data,
|
||||
net::CompletionOnceCallback callback) const override;
|
||||
|
||||
private:
|
||||
std::string mime_type_;
|
||||
std::string charset_;
|
||||
scoped_refptr<base::RefCountedBytes> data_;
|
||||
net::HttpStatusCode status_code_;
|
||||
|
||||
base::WeakPtrFactory<URLRequestBufferJob> weak_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(URLRequestBufferJob);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_URL_REQUEST_BUFFER_JOB_H_
|
352
shell/browser/net/url_request_context_getter.cc
Normal file
352
shell/browser/net/url_request_context_getter.cc
Normal file
|
@ -0,0 +1,352 @@
|
|||
// Copyright (c) 2018 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/url_request_context_getter.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "atom/browser/api/atom_api_protocol.h"
|
||||
#include "atom/browser/atom_browser_client.h"
|
||||
#include "atom/browser/atom_browser_context.h"
|
||||
#include "atom/browser/browser_process_impl.h"
|
||||
#include "atom/browser/net/about_protocol_handler.h"
|
||||
#include "atom/browser/net/asar/asar_protocol_handler.h"
|
||||
#include "atom/browser/net/atom_cert_verifier.h"
|
||||
#include "atom/browser/net/atom_network_delegate.h"
|
||||
#include "atom/browser/net/atom_url_request_job_factory.h"
|
||||
#include "atom/browser/net/http_protocol_handler.h"
|
||||
#include "atom/browser/net/require_ct_delegate.h"
|
||||
#include "atom/browser/net/system_network_context_manager.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/task/post_task.h"
|
||||
#include "chrome/common/chrome_constants.h"
|
||||
#include "chrome/common/pref_names.h"
|
||||
#include "components/network_session_configurator/common/network_switches.h"
|
||||
#include "components/prefs/value_map_pref_store.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/devtools_network_transaction_factory.h"
|
||||
#include "content/public/browser/network_service_instance.h"
|
||||
#include "net/base/host_mapping_rules.h"
|
||||
#include "net/cert/multi_log_ct_verifier.h"
|
||||
#include "net/cookies/cookie_monster.h"
|
||||
#include "net/dns/mapped_host_resolver.h" // nogncheck
|
||||
#include "net/http/http_auth_handler_factory.h"
|
||||
#include "net/http/http_auth_preferences.h"
|
||||
#include "net/http/http_auth_scheme.h"
|
||||
#include "net/http/http_transaction_factory.h"
|
||||
#include "net/log/net_log.h"
|
||||
#include "net/traffic_annotation/network_traffic_annotation.h"
|
||||
#include "net/url_request/data_protocol_handler.h"
|
||||
#include "net/url_request/static_http_user_agent_settings.h"
|
||||
#include "net/url_request/url_request_intercepting_job_factory.h"
|
||||
#include "net/url_request/url_request_job_factory_impl.h"
|
||||
#include "services/network/ignore_errors_cert_verifier.h"
|
||||
#include "services/network/network_service.h"
|
||||
#include "services/network/public/cpp/features.h"
|
||||
#include "services/network/public/cpp/network_switches.h"
|
||||
#include "services/network/url_request_context_builder_mojo.h"
|
||||
#include "url/url_constants.h"
|
||||
|
||||
#if !BUILDFLAG(DISABLE_FTP_SUPPORT)
|
||||
#include "net/url_request/ftp_protocol_handler.h"
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(ENABLE_REPORTING)
|
||||
#include "net/reporting/reporting_policy.h"
|
||||
#include "net/reporting/reporting_service.h"
|
||||
#endif // BUILDFLAG(ENABLE_REPORTING)
|
||||
|
||||
using content::BrowserThread;
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
void SetupAtomURLRequestJobFactory(
|
||||
content::ProtocolHandlerMap* protocol_handlers,
|
||||
net::URLRequestContext* url_request_context,
|
||||
AtomURLRequestJobFactory* job_factory) {
|
||||
for (auto& protocol_handler : *protocol_handlers) {
|
||||
job_factory->SetProtocolHandler(protocol_handler.first,
|
||||
std::move(protocol_handler.second));
|
||||
}
|
||||
protocol_handlers->clear();
|
||||
|
||||
job_factory->SetProtocolHandler(url::kAboutScheme,
|
||||
std::make_unique<AboutProtocolHandler>());
|
||||
job_factory->SetProtocolHandler(url::kDataScheme,
|
||||
std::make_unique<net::DataProtocolHandler>());
|
||||
job_factory->SetProtocolHandler(
|
||||
url::kFileScheme,
|
||||
std::make_unique<asar::AsarProtocolHandler>(
|
||||
base::CreateTaskRunnerWithTraits(
|
||||
{base::MayBlock(), base::TaskPriority::USER_BLOCKING,
|
||||
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})));
|
||||
job_factory->SetProtocolHandler(
|
||||
url::kHttpScheme,
|
||||
std::make_unique<HttpProtocolHandler>(url::kHttpScheme));
|
||||
job_factory->SetProtocolHandler(
|
||||
url::kHttpsScheme,
|
||||
std::make_unique<HttpProtocolHandler>(url::kHttpsScheme));
|
||||
job_factory->SetProtocolHandler(
|
||||
url::kWsScheme, std::make_unique<HttpProtocolHandler>(url::kWsScheme));
|
||||
job_factory->SetProtocolHandler(
|
||||
url::kWssScheme, std::make_unique<HttpProtocolHandler>(url::kWssScheme));
|
||||
|
||||
#if !BUILDFLAG(DISABLE_FTP_SUPPORT)
|
||||
auto* host_resolver = url_request_context->host_resolver();
|
||||
job_factory->SetProtocolHandler(
|
||||
url::kFtpScheme, net::FtpProtocolHandler::Create(host_resolver));
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
URLRequestContextGetter::Handle::Handle(
|
||||
base::WeakPtr<AtomBrowserContext> browser_context)
|
||||
: resource_context_(new content::ResourceContext),
|
||||
browser_context_(browser_context),
|
||||
initialized_(false) {}
|
||||
|
||||
URLRequestContextGetter::Handle::~Handle() {}
|
||||
|
||||
content::ResourceContext*
|
||||
URLRequestContextGetter::Handle::GetResourceContext() {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
LazyInitialize();
|
||||
return resource_context_.get();
|
||||
}
|
||||
|
||||
scoped_refptr<URLRequestContextGetter>
|
||||
URLRequestContextGetter::Handle::CreateMainRequestContextGetter(
|
||||
content::ProtocolHandlerMap* protocol_handlers,
|
||||
content::URLRequestInterceptorScopedVector protocol_interceptors) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
DCHECK(!main_request_context_getter_.get());
|
||||
DCHECK(g_browser_process->io_thread());
|
||||
|
||||
LazyInitialize();
|
||||
main_request_context_getter_ = new URLRequestContextGetter(
|
||||
this, protocol_handlers, std::move(protocol_interceptors));
|
||||
g_browser_process->io_thread()->RegisterURLRequestContextGetter(
|
||||
main_request_context_getter_.get());
|
||||
return main_request_context_getter_;
|
||||
}
|
||||
|
||||
scoped_refptr<URLRequestContextGetter>
|
||||
URLRequestContextGetter::Handle::GetMainRequestContextGetter() {
|
||||
return main_request_context_getter_;
|
||||
}
|
||||
|
||||
network::mojom::NetworkContextPtr
|
||||
URLRequestContextGetter::Handle::GetNetworkContext() {
|
||||
if (!main_network_context_) {
|
||||
main_network_context_request_ = mojo::MakeRequest(&main_network_context_);
|
||||
}
|
||||
return std::move(main_network_context_);
|
||||
}
|
||||
|
||||
network::mojom::NetworkContextParamsPtr
|
||||
URLRequestContextGetter::Handle::CreateNetworkContextParams() {
|
||||
network::mojom::NetworkContextParamsPtr network_context_params =
|
||||
SystemNetworkContextManager::GetInstance()
|
||||
->CreateDefaultNetworkContextParams();
|
||||
|
||||
network_context_params->user_agent = browser_context_->GetUserAgent();
|
||||
|
||||
network_context_params->http_cache_enabled =
|
||||
browser_context_->CanUseHttpCache();
|
||||
|
||||
network_context_params->accept_language =
|
||||
net::HttpUtil::GenerateAcceptLanguageHeader(
|
||||
AtomBrowserClient::Get()->GetApplicationLocale());
|
||||
|
||||
if (!browser_context_->IsOffTheRecord()) {
|
||||
auto base_path = browser_context_->GetPath();
|
||||
network_context_params->http_cache_path =
|
||||
base_path.Append(chrome::kCacheDirname);
|
||||
network_context_params->http_cache_max_size =
|
||||
browser_context_->GetMaxCacheSize();
|
||||
network_context_params->http_server_properties_path =
|
||||
base_path.Append(chrome::kNetworkPersistentStateFilename);
|
||||
network_context_params->cookie_path =
|
||||
base_path.Append(chrome::kCookieFilename);
|
||||
network_context_params->restore_old_session_cookies = false;
|
||||
network_context_params->persist_session_cookies = false;
|
||||
// TODO(deepak1556): Matches the existing behavior https://git.io/fxHMl,
|
||||
// enable encryption as a followup.
|
||||
network_context_params->enable_encrypted_cookies = false;
|
||||
}
|
||||
|
||||
// TODO(deepak1556): Decide the stand on chrome ct policy and
|
||||
// enable it.
|
||||
// See //net/docs/certificate-transparency.md
|
||||
// network_context_params->enforce_chrome_ct_policy = true;
|
||||
return network_context_params;
|
||||
}
|
||||
|
||||
void URLRequestContextGetter::Handle::LazyInitialize() {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
if (initialized_)
|
||||
return;
|
||||
|
||||
initialized_ = true;
|
||||
main_network_context_params_ = CreateNetworkContextParams();
|
||||
|
||||
browser_context_->proxy_config_monitor()->AddToNetworkContextParams(
|
||||
main_network_context_params_.get());
|
||||
|
||||
BrowserProcessImpl::ApplyProxyModeFromCommandLine(
|
||||
browser_context_->in_memory_pref_store());
|
||||
|
||||
if (!main_network_context_request_.is_pending()) {
|
||||
main_network_context_request_ = mojo::MakeRequest(&main_network_context_);
|
||||
}
|
||||
content::BrowserContext::EnsureResourceContextInitialized(
|
||||
browser_context_.get());
|
||||
}
|
||||
|
||||
void URLRequestContextGetter::Handle::ShutdownOnUIThread() {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
|
||||
if (main_request_context_getter_) {
|
||||
if (BrowserThread::IsThreadInitialized(BrowserThread::IO)) {
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {BrowserThread::IO},
|
||||
base::BindOnce(&URLRequestContextGetter::NotifyContextShuttingDown,
|
||||
base::RetainedRef(main_request_context_getter_)));
|
||||
}
|
||||
}
|
||||
|
||||
if (!BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, this))
|
||||
delete this;
|
||||
}
|
||||
|
||||
URLRequestContextGetter::URLRequestContextGetter(
|
||||
URLRequestContextGetter::Handle* context_handle,
|
||||
content::ProtocolHandlerMap* protocol_handlers,
|
||||
content::URLRequestInterceptorScopedVector protocol_interceptors)
|
||||
: context_handle_(context_handle),
|
||||
url_request_context_(nullptr),
|
||||
protocol_interceptors_(std::move(protocol_interceptors)),
|
||||
context_shutting_down_(false) {
|
||||
// Must first be created on the UI thread.
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
|
||||
if (protocol_handlers)
|
||||
std::swap(protocol_handlers_, *protocol_handlers);
|
||||
}
|
||||
|
||||
URLRequestContextGetter::~URLRequestContextGetter() {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
// NotifyContextShuttingDown should have been called.
|
||||
DCHECK(context_shutting_down_);
|
||||
}
|
||||
|
||||
void URLRequestContextGetter::NotifyContextShuttingDown() {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
DCHECK(g_browser_process->io_thread());
|
||||
DCHECK(context_handle_);
|
||||
|
||||
if (context_shutting_down_)
|
||||
return;
|
||||
|
||||
g_browser_process->io_thread()->DeregisterURLRequestContextGetter(this);
|
||||
|
||||
context_shutting_down_ = true;
|
||||
context_handle_->resource_context_.reset();
|
||||
net::URLRequestContextGetter::NotifyContextShuttingDown();
|
||||
network_context_.reset();
|
||||
}
|
||||
|
||||
net::URLRequestContext* URLRequestContextGetter::GetURLRequestContext() {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
|
||||
if (context_shutting_down_)
|
||||
return nullptr;
|
||||
|
||||
if (!url_request_context_) {
|
||||
std::unique_ptr<network::URLRequestContextBuilderMojo> builder =
|
||||
std::make_unique<network::URLRequestContextBuilderMojo>();
|
||||
|
||||
// Enable file:// support.
|
||||
builder->set_file_enabled(true);
|
||||
|
||||
#if BUILDFLAG(ENABLE_REPORTING)
|
||||
if (base::FeatureList::IsEnabled(network::features::kReporting)) {
|
||||
auto reporting_policy = net::ReportingPolicy::Create();
|
||||
builder->set_reporting_policy(std::move(reporting_policy));
|
||||
} else {
|
||||
builder->set_reporting_policy(nullptr);
|
||||
}
|
||||
|
||||
builder->set_network_error_logging_enabled(
|
||||
base::FeatureList::IsEnabled(network::features::kNetworkErrorLogging));
|
||||
#endif // BUILDFLAG(ENABLE_REPORTING)
|
||||
|
||||
auto network_delegate = std::make_unique<AtomNetworkDelegate>();
|
||||
network_delegate_ = network_delegate.get();
|
||||
builder->set_network_delegate(std::move(network_delegate));
|
||||
|
||||
ct_delegate_.reset(new RequireCTDelegate);
|
||||
auto cert_verifier = std::make_unique<AtomCertVerifier>(ct_delegate_.get());
|
||||
builder->SetCertVerifier(std::move(cert_verifier));
|
||||
|
||||
builder->SetCreateHttpTransactionFactoryCallback(
|
||||
base::BindOnce(&content::CreateDevToolsNetworkTransactionFactory));
|
||||
|
||||
builder->set_ct_verifier(std::make_unique<net::MultiLogCTVerifier>());
|
||||
|
||||
auto* network_service = content::GetNetworkServiceImpl();
|
||||
network_context_ = network_service->CreateNetworkContextWithBuilder(
|
||||
std::move(context_handle_->main_network_context_request_),
|
||||
std::move(context_handle_->main_network_context_params_),
|
||||
std::move(builder), &url_request_context_);
|
||||
|
||||
net::TransportSecurityState* transport_security_state =
|
||||
url_request_context_->transport_security_state();
|
||||
transport_security_state->SetRequireCTDelegate(ct_delegate_.get());
|
||||
|
||||
// Add custom standard schemes to cookie schemes.
|
||||
auto* cookie_monster =
|
||||
static_cast<net::CookieMonster*>(url_request_context_->cookie_store());
|
||||
std::vector<std::string> cookie_schemes(
|
||||
{url::kHttpScheme, url::kHttpsScheme, url::kWsScheme, url::kWssScheme});
|
||||
const auto& custom_standard_schemes = atom::api::GetStandardSchemes();
|
||||
cookie_schemes.insert(cookie_schemes.end(), custom_standard_schemes.begin(),
|
||||
custom_standard_schemes.end());
|
||||
cookie_monster->SetCookieableSchemes(cookie_schemes, base::NullCallback());
|
||||
|
||||
// Setup handlers for custom job factory.
|
||||
top_job_factory_.reset(new AtomURLRequestJobFactory);
|
||||
SetupAtomURLRequestJobFactory(&protocol_handlers_, url_request_context_,
|
||||
top_job_factory_.get());
|
||||
std::unique_ptr<net::URLRequestJobFactory> inner_job_factory(
|
||||
new net::URLRequestJobFactoryImpl);
|
||||
if (!protocol_interceptors_.empty()) {
|
||||
// Set up interceptors in the reverse order.
|
||||
for (auto it = protocol_interceptors_.rbegin();
|
||||
it != protocol_interceptors_.rend(); ++it) {
|
||||
inner_job_factory.reset(new net::URLRequestInterceptingJobFactory(
|
||||
std::move(inner_job_factory), std::move(*it)));
|
||||
}
|
||||
protocol_interceptors_.clear();
|
||||
}
|
||||
top_job_factory_->Chain(std::move(inner_job_factory));
|
||||
url_request_context_->set_job_factory(top_job_factory_.get());
|
||||
}
|
||||
|
||||
return url_request_context_;
|
||||
}
|
||||
|
||||
scoped_refptr<base::SingleThreadTaskRunner>
|
||||
URLRequestContextGetter::GetNetworkTaskRunner() const {
|
||||
return base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO});
|
||||
}
|
||||
|
||||
} // namespace atom
|
114
shell/browser/net/url_request_context_getter.h
Normal file
114
shell/browser/net/url_request_context_getter.h
Normal file
|
@ -0,0 +1,114 @@
|
|||
// Copyright (c) 2018 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_URL_REQUEST_CONTEXT_GETTER_H_
|
||||
#define ATOM_BROWSER_NET_URL_REQUEST_CONTEXT_GETTER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "content/public/browser/browser_context.h"
|
||||
#include "content/public/browser/resource_context.h"
|
||||
#include "net/url_request/url_request_context.h"
|
||||
#include "net/url_request/url_request_context_getter.h"
|
||||
#include "services/network/public/mojom/network_service.mojom.h"
|
||||
|
||||
#if DCHECK_IS_ON()
|
||||
#include "base/debug/leak_tracker.h"
|
||||
#endif
|
||||
|
||||
namespace atom {
|
||||
|
||||
class AtomBrowserContext;
|
||||
class AtomNetworkDelegate;
|
||||
class AtomURLRequestJobFactory;
|
||||
class RequireCTDelegate;
|
||||
class ResourceContext;
|
||||
|
||||
class URLRequestContextGetter : public net::URLRequestContextGetter {
|
||||
public:
|
||||
// net::URLRequestContextGetter:
|
||||
net::URLRequestContext* GetURLRequestContext() override;
|
||||
scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner()
|
||||
const override;
|
||||
|
||||
// Discard reference to URLRequestContext and inform observers to
|
||||
// shutdown. Must be called only on IO thread.
|
||||
void NotifyContextShuttingDown();
|
||||
|
||||
AtomURLRequestJobFactory* job_factory() const {
|
||||
return top_job_factory_.get();
|
||||
}
|
||||
|
||||
AtomNetworkDelegate* network_delegate() const { return network_delegate_; }
|
||||
|
||||
private:
|
||||
friend class AtomBrowserContext;
|
||||
|
||||
// Responsible for destroying URLRequestContextGetter
|
||||
// on the IO thread.
|
||||
class Handle {
|
||||
public:
|
||||
explicit Handle(base::WeakPtr<AtomBrowserContext> browser_context);
|
||||
~Handle();
|
||||
|
||||
scoped_refptr<URLRequestContextGetter> CreateMainRequestContextGetter(
|
||||
content::ProtocolHandlerMap* protocol_handlers,
|
||||
content::URLRequestInterceptorScopedVector protocol_interceptors);
|
||||
content::ResourceContext* GetResourceContext();
|
||||
scoped_refptr<URLRequestContextGetter> GetMainRequestContextGetter();
|
||||
network::mojom::NetworkContextPtr GetNetworkContext();
|
||||
network::mojom::NetworkContextParamsPtr CreateNetworkContextParams();
|
||||
|
||||
void ShutdownOnUIThread();
|
||||
|
||||
private:
|
||||
friend class URLRequestContextGetter;
|
||||
void LazyInitialize();
|
||||
|
||||
scoped_refptr<URLRequestContextGetter> main_request_context_getter_;
|
||||
std::unique_ptr<content::ResourceContext> resource_context_;
|
||||
base::WeakPtr<AtomBrowserContext> browser_context_;
|
||||
// This is a NetworkContext interface that uses URLRequestContextGetter
|
||||
// NetworkContext, ownership is passed to StoragePartition when
|
||||
// CreateMainNetworkContext is called.
|
||||
network::mojom::NetworkContextPtr main_network_context_;
|
||||
// Request corresponding to |main_network_context_|. Ownership
|
||||
// is passed to network service.
|
||||
network::mojom::NetworkContextRequest main_network_context_request_;
|
||||
network::mojom::NetworkContextParamsPtr main_network_context_params_;
|
||||
bool initialized_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Handle);
|
||||
};
|
||||
|
||||
URLRequestContextGetter(
|
||||
URLRequestContextGetter::Handle* context_handle,
|
||||
content::ProtocolHandlerMap* protocol_handlers,
|
||||
content::URLRequestInterceptorScopedVector protocol_interceptors);
|
||||
~URLRequestContextGetter() override;
|
||||
|
||||
#if DCHECK_IS_ON()
|
||||
base::debug::LeakTracker<URLRequestContextGetter> leak_tracker_;
|
||||
#endif
|
||||
|
||||
std::unique_ptr<RequireCTDelegate> ct_delegate_;
|
||||
std::unique_ptr<AtomURLRequestJobFactory> top_job_factory_;
|
||||
std::unique_ptr<network::mojom::NetworkContext> network_context_;
|
||||
|
||||
URLRequestContextGetter::Handle* context_handle_;
|
||||
net::URLRequestContext* url_request_context_;
|
||||
AtomNetworkDelegate* network_delegate_;
|
||||
content::ProtocolHandlerMap protocol_handlers_;
|
||||
content::URLRequestInterceptorScopedVector protocol_interceptors_;
|
||||
bool context_shutting_down_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(URLRequestContextGetter);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_URL_REQUEST_CONTEXT_GETTER_H_
|
343
shell/browser/net/url_request_fetch_job.cc
Normal file
343
shell/browser/net/url_request_fetch_job.cc
Normal file
|
@ -0,0 +1,343 @@
|
|||
// Copyright (c) 2015 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/url_request_fetch_job.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "atom/browser/api/atom_api_session.h"
|
||||
#include "atom/browser/atom_browser_context.h"
|
||||
#include "atom/common/native_mate_converters/net_converter.h"
|
||||
#include "atom/common/native_mate_converters/v8_value_converter.h"
|
||||
#include "base/guid.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/task/post_task.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "net/base/io_buffer.h"
|
||||
#include "net/base/net_errors.h"
|
||||
#include "net/http/http_response_headers.h"
|
||||
#include "net/url_request/url_fetcher.h"
|
||||
#include "net/url_request/url_fetcher_response_writer.h"
|
||||
|
||||
using content::BrowserThread;
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
// Convert string to RequestType.
|
||||
net::URLFetcher::RequestType GetRequestType(const std::string& raw) {
|
||||
std::string method = base::ToUpperASCII(raw);
|
||||
if (method.empty() || method == "GET")
|
||||
return net::URLFetcher::GET;
|
||||
else if (method == "POST")
|
||||
return net::URLFetcher::POST;
|
||||
else if (method == "HEAD")
|
||||
return net::URLFetcher::HEAD;
|
||||
else if (method == "DELETE")
|
||||
return net::URLFetcher::DELETE_REQUEST;
|
||||
else if (method == "PUT")
|
||||
return net::URLFetcher::PUT;
|
||||
else if (method == "PATCH")
|
||||
return net::URLFetcher::PATCH;
|
||||
else // Use "GET" as fallback.
|
||||
return net::URLFetcher::GET;
|
||||
}
|
||||
|
||||
// Pipe the response writer back to URLRequestFetchJob.
|
||||
class ResponsePiper : public net::URLFetcherResponseWriter {
|
||||
public:
|
||||
explicit ResponsePiper(URLRequestFetchJob* job) : job_(job) {}
|
||||
|
||||
// net::URLFetcherResponseWriter:
|
||||
int Initialize(net::CompletionOnceCallback callback) override {
|
||||
return net::OK;
|
||||
}
|
||||
int Write(net::IOBuffer* buffer,
|
||||
int num_bytes,
|
||||
net::CompletionOnceCallback callback) override {
|
||||
if (first_write_) {
|
||||
// The URLFetcherResponseWriter doesn't have an event when headers have
|
||||
// been read, so we have to emulate by hooking to first write event.
|
||||
job_->HeadersCompleted();
|
||||
first_write_ = false;
|
||||
}
|
||||
return job_->DataAvailable(buffer, num_bytes, std::move(callback));
|
||||
}
|
||||
int Finish(int net_error, net::CompletionOnceCallback callback) override {
|
||||
return net::OK;
|
||||
}
|
||||
|
||||
private:
|
||||
bool first_write_ = true;
|
||||
URLRequestFetchJob* job_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ResponsePiper);
|
||||
};
|
||||
|
||||
void BeforeStartInUI(base::WeakPtr<URLRequestFetchJob> job,
|
||||
mate::Arguments* args) {
|
||||
// Pass whatever user passed to the actaul request job.
|
||||
v8::Local<v8::Value> value;
|
||||
mate::Dictionary options;
|
||||
if (!args->GetNext(&value) ||
|
||||
!mate::ConvertFromV8(args->isolate(), value, &options)) {
|
||||
base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO},
|
||||
base::BindOnce(&URLRequestFetchJob::OnError, job,
|
||||
net::ERR_NOT_IMPLEMENTED));
|
||||
return;
|
||||
}
|
||||
|
||||
scoped_refptr<net::URLRequestContextGetter> url_request_context_getter;
|
||||
scoped_refptr<AtomBrowserContext> custom_browser_context;
|
||||
// When |session| is set to |null| we use a new request context for fetch
|
||||
// job.
|
||||
if (options.Get("session", &value)) {
|
||||
if (value->IsNull()) {
|
||||
// We have to create the URLRequestContextGetter on UI thread.
|
||||
custom_browser_context =
|
||||
AtomBrowserContext::From(base::GenerateGUID(), true);
|
||||
url_request_context_getter = custom_browser_context->GetRequestContext();
|
||||
} else {
|
||||
mate::Handle<api::Session> session;
|
||||
if (mate::ConvertFromV8(args->isolate(), value, &session) &&
|
||||
!session.IsEmpty()) {
|
||||
AtomBrowserContext* browser_context = session->browser_context();
|
||||
url_request_context_getter = browser_context->GetRequestContext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
V8ValueConverter converter;
|
||||
v8::Local<v8::Context> context = args->isolate()->GetCurrentContext();
|
||||
std::unique_ptr<base::Value> request_options(
|
||||
converter.FromV8Value(value, context));
|
||||
|
||||
int error = net::OK;
|
||||
if (!request_options || !request_options->is_dict())
|
||||
error = net::ERR_NOT_IMPLEMENTED;
|
||||
|
||||
JsAsker::IsErrorOptions(request_options.get(), &error);
|
||||
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::IO},
|
||||
base::BindOnce(&URLRequestFetchJob::StartAsync, job,
|
||||
base::RetainedRef(url_request_context_getter),
|
||||
base::RetainedRef(custom_browser_context),
|
||||
std::move(request_options), error));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
URLRequestFetchJob::URLRequestFetchJob(net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate)
|
||||
: net::URLRequestJob(request, network_delegate), weak_factory_(this) {}
|
||||
|
||||
URLRequestFetchJob::~URLRequestFetchJob() = default;
|
||||
|
||||
void URLRequestFetchJob::Start() {
|
||||
auto request_details = std::make_unique<base::DictionaryValue>();
|
||||
FillRequestDetails(request_details.get(), request());
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::UI},
|
||||
base::BindOnce(
|
||||
&JsAsker::AskForOptions, base::Unretained(isolate()), handler(),
|
||||
std::move(request_details),
|
||||
base::BindOnce(&BeforeStartInUI, weak_factory_.GetWeakPtr())));
|
||||
}
|
||||
|
||||
void URLRequestFetchJob::StartAsync(
|
||||
scoped_refptr<net::URLRequestContextGetter> url_request_context_getter,
|
||||
scoped_refptr<AtomBrowserContext> browser_context,
|
||||
std::unique_ptr<base::Value> options,
|
||||
int error) {
|
||||
if (error != net::OK) {
|
||||
NotifyStartError(
|
||||
net::URLRequestStatus(net::URLRequestStatus::FAILED, error));
|
||||
return;
|
||||
}
|
||||
|
||||
std::string url, method, referrer;
|
||||
base::DictionaryValue* upload_data = nullptr;
|
||||
base::DictionaryValue* dict =
|
||||
static_cast<base::DictionaryValue*>(options.get());
|
||||
dict->GetString("url", &url);
|
||||
dict->GetString("method", &method);
|
||||
dict->GetString("referrer", &referrer);
|
||||
dict->GetDictionary("uploadData", &upload_data);
|
||||
|
||||
// Check if URL is valid.
|
||||
GURL formated_url(url);
|
||||
if (!formated_url.is_valid()) {
|
||||
NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED,
|
||||
net::ERR_INVALID_URL));
|
||||
return;
|
||||
}
|
||||
|
||||
// Use |request|'s method if |method| is not specified.
|
||||
net::URLFetcher::RequestType request_type;
|
||||
if (method.empty())
|
||||
request_type = GetRequestType(request()->method());
|
||||
else
|
||||
request_type = GetRequestType(method);
|
||||
|
||||
fetcher_ = net::URLFetcher::Create(formated_url, request_type, this);
|
||||
fetcher_->SaveResponseWithWriter(base::WrapUnique(new ResponsePiper(this)));
|
||||
|
||||
// A request context getter is passed by the user.
|
||||
if (url_request_context_getter)
|
||||
fetcher_->SetRequestContext(url_request_context_getter.get());
|
||||
else
|
||||
fetcher_->SetRequestContext(request_context_getter());
|
||||
|
||||
// Use |request|'s referrer if |referrer| is not specified.
|
||||
if (referrer.empty())
|
||||
fetcher_->SetReferrer(request()->referrer());
|
||||
else
|
||||
fetcher_->SetReferrer(referrer);
|
||||
|
||||
// Set the data needed for POSTs.
|
||||
if (upload_data && request_type == net::URLFetcher::POST) {
|
||||
std::string content_type, data;
|
||||
upload_data->GetString("contentType", &content_type);
|
||||
upload_data->GetString("data", &data);
|
||||
fetcher_->SetUploadData(content_type, data);
|
||||
}
|
||||
|
||||
// Use |request|'s headers.
|
||||
fetcher_->SetExtraRequestHeaders(
|
||||
request()->extra_request_headers().ToString());
|
||||
|
||||
fetcher_->Start();
|
||||
|
||||
if (browser_context)
|
||||
custom_browser_context_ = browser_context;
|
||||
}
|
||||
|
||||
void URLRequestFetchJob::OnError(int error) {
|
||||
NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED, error));
|
||||
}
|
||||
|
||||
void URLRequestFetchJob::HeadersCompleted() {
|
||||
response_info_.reset(new net::HttpResponseInfo);
|
||||
response_info_->headers = fetcher_->GetResponseHeaders();
|
||||
NotifyHeadersComplete();
|
||||
}
|
||||
|
||||
int URLRequestFetchJob::DataAvailable(net::IOBuffer* buffer,
|
||||
int num_bytes,
|
||||
net::CompletionOnceCallback callback) {
|
||||
// When pending_buffer_ is empty, there's no ReadRawData() operation waiting
|
||||
// for IO completion, we have to save the parameters until the request is
|
||||
// ready to read data.
|
||||
if (!pending_buffer_.get()) {
|
||||
write_buffer_ = buffer;
|
||||
write_num_bytes_ = num_bytes;
|
||||
write_callback_ = std::move(callback);
|
||||
return net::ERR_IO_PENDING;
|
||||
}
|
||||
|
||||
// Write data to the pending buffer and clear them after the writing.
|
||||
int bytes_read = BufferCopy(buffer, num_bytes, pending_buffer_.get(),
|
||||
pending_buffer_size_);
|
||||
ClearPendingBuffer();
|
||||
ReadRawDataComplete(bytes_read);
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
void URLRequestFetchJob::Kill() {
|
||||
weak_factory_.InvalidateWeakPtrs();
|
||||
net::URLRequestJob::Kill();
|
||||
fetcher_.reset();
|
||||
custom_browser_context_ = nullptr;
|
||||
}
|
||||
|
||||
int URLRequestFetchJob::ReadRawData(net::IOBuffer* dest, int dest_size) {
|
||||
if (GetResponseCode() == 204) {
|
||||
request()->set_received_response_content_length(prefilter_bytes_read());
|
||||
return net::OK;
|
||||
}
|
||||
|
||||
// When write_buffer_ is empty, there is no data valable yet, we have to save
|
||||
// the dest buffer util DataAvailable.
|
||||
if (!write_buffer_.get()) {
|
||||
pending_buffer_ = dest;
|
||||
pending_buffer_size_ = dest_size;
|
||||
return net::ERR_IO_PENDING;
|
||||
}
|
||||
|
||||
// Read from the write buffer and clear them after reading.
|
||||
int bytes_read =
|
||||
BufferCopy(write_buffer_.get(), write_num_bytes_, dest, dest_size);
|
||||
ClearWriteBuffer();
|
||||
if (!write_callback_.is_null())
|
||||
std::move(write_callback_).Run(bytes_read);
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
bool URLRequestFetchJob::GetMimeType(std::string* mime_type) const {
|
||||
if (!response_info_ || !response_info_->headers)
|
||||
return false;
|
||||
|
||||
return response_info_->headers->GetMimeType(mime_type);
|
||||
}
|
||||
|
||||
void URLRequestFetchJob::GetResponseInfo(net::HttpResponseInfo* info) {
|
||||
if (response_info_)
|
||||
*info = *response_info_;
|
||||
}
|
||||
|
||||
int URLRequestFetchJob::GetResponseCode() const {
|
||||
if (!response_info_ || !response_info_->headers)
|
||||
return -1;
|
||||
|
||||
return response_info_->headers->response_code();
|
||||
}
|
||||
|
||||
void URLRequestFetchJob::OnURLFetchComplete(const net::URLFetcher* source) {
|
||||
ClearPendingBuffer();
|
||||
ClearWriteBuffer();
|
||||
|
||||
if (fetcher_->GetStatus().is_success()) {
|
||||
if (!response_info_) {
|
||||
// Since we notify header completion only after first write there will be
|
||||
// no response object constructed for http respones with no content 204.
|
||||
// We notify header completion here.
|
||||
HeadersCompleted();
|
||||
return;
|
||||
}
|
||||
if (request_->status().is_io_pending()) {
|
||||
ReadRawDataComplete(0);
|
||||
}
|
||||
} else {
|
||||
NotifyStartError(fetcher_->GetStatus());
|
||||
}
|
||||
}
|
||||
|
||||
int URLRequestFetchJob::BufferCopy(net::IOBuffer* source,
|
||||
int num_bytes,
|
||||
net::IOBuffer* target,
|
||||
int target_size) {
|
||||
int bytes_written = std::min(num_bytes, target_size);
|
||||
memcpy(target->data(), source->data(), bytes_written);
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
void URLRequestFetchJob::ClearPendingBuffer() {
|
||||
pending_buffer_ = nullptr;
|
||||
pending_buffer_size_ = 0;
|
||||
}
|
||||
|
||||
void URLRequestFetchJob::ClearWriteBuffer() {
|
||||
write_buffer_ = nullptr;
|
||||
write_num_bytes_ = 0;
|
||||
}
|
||||
|
||||
} // namespace atom
|
81
shell/browser/net/url_request_fetch_job.h
Normal file
81
shell/browser/net/url_request_fetch_job.h
Normal file
|
@ -0,0 +1,81 @@
|
|||
// Copyright (c) 2015 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_URL_REQUEST_FETCH_JOB_H_
|
||||
#define ATOM_BROWSER_NET_URL_REQUEST_FETCH_JOB_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "atom/browser/net/js_asker.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "net/url_request/url_fetcher_delegate.h"
|
||||
#include "net/url_request/url_request_context_getter.h"
|
||||
#include "net/url_request/url_request_job.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class AtomBrowserContext;
|
||||
|
||||
class URLRequestFetchJob : public JsAsker,
|
||||
public net::URLRequestJob,
|
||||
public net::URLFetcherDelegate {
|
||||
public:
|
||||
URLRequestFetchJob(net::URLRequest*, net::NetworkDelegate*);
|
||||
~URLRequestFetchJob() override;
|
||||
|
||||
void StartAsync(
|
||||
scoped_refptr<net::URLRequestContextGetter> request_context_getter,
|
||||
scoped_refptr<AtomBrowserContext> browser_context,
|
||||
std::unique_ptr<base::Value> options,
|
||||
int error);
|
||||
void OnError(int error);
|
||||
|
||||
// Called by response writer.
|
||||
void HeadersCompleted();
|
||||
int DataAvailable(net::IOBuffer* buffer,
|
||||
int num_bytes,
|
||||
net::CompletionOnceCallback callback);
|
||||
|
||||
protected:
|
||||
// net::URLRequestJob:
|
||||
void Start() override;
|
||||
void Kill() override;
|
||||
int ReadRawData(net::IOBuffer* buf, int buf_size) override;
|
||||
bool GetMimeType(std::string* mime_type) const override;
|
||||
void GetResponseInfo(net::HttpResponseInfo* info) override;
|
||||
int GetResponseCode() const override;
|
||||
|
||||
// net::URLFetcherDelegate:
|
||||
void OnURLFetchComplete(const net::URLFetcher* source) override;
|
||||
|
||||
private:
|
||||
int BufferCopy(net::IOBuffer* source,
|
||||
int num_bytes,
|
||||
net::IOBuffer* target,
|
||||
int target_size);
|
||||
void ClearPendingBuffer();
|
||||
void ClearWriteBuffer();
|
||||
|
||||
scoped_refptr<AtomBrowserContext> custom_browser_context_;
|
||||
std::unique_ptr<net::URLFetcher> fetcher_;
|
||||
std::unique_ptr<net::HttpResponseInfo> response_info_;
|
||||
|
||||
// Saved arguments passed to ReadRawData.
|
||||
scoped_refptr<net::IOBuffer> pending_buffer_;
|
||||
int pending_buffer_size_ = 0;
|
||||
|
||||
// Saved arguments passed to DataAvailable.
|
||||
scoped_refptr<net::IOBuffer> write_buffer_;
|
||||
int write_num_bytes_ = 0;
|
||||
net::CompletionOnceCallback write_callback_;
|
||||
|
||||
base::WeakPtrFactory<URLRequestFetchJob> weak_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(URLRequestFetchJob);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_URL_REQUEST_FETCH_JOB_H_
|
258
shell/browser/net/url_request_stream_job.cc
Normal file
258
shell/browser/net/url_request_stream_job.cc
Normal file
|
@ -0,0 +1,258 @@
|
|||
// Copyright (c) 2017 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/url_request_stream_job.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "atom/common/api/event_emitter_caller.h"
|
||||
#include "atom/common/atom_constants.h"
|
||||
#include "atom/common/native_mate_converters/net_converter.h"
|
||||
#include "atom/common/node_includes.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/task/post_task.h"
|
||||
#include "base/threading/thread_task_runner_handle.h"
|
||||
#include "base/time/time.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "net/base/net_errors.h"
|
||||
#include "net/filter/gzip_source_stream.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
void BeforeStartInUI(base::WeakPtr<URLRequestStreamJob> job,
|
||||
mate::Arguments* args) {
|
||||
v8::Local<v8::Value> value;
|
||||
int error = net::OK;
|
||||
bool ended = false;
|
||||
if (!args->GetNext(&value) || !value->IsObject()) {
|
||||
// Invalid opts.
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::IO},
|
||||
base::BindOnce(&URLRequestStreamJob::OnError, job, net::ERR_FAILED));
|
||||
return;
|
||||
}
|
||||
|
||||
mate::Dictionary opts(args->isolate(), v8::Local<v8::Object>::Cast(value));
|
||||
int status_code;
|
||||
if (!opts.Get("statusCode", &status_code)) {
|
||||
// assume HTTP OK if statusCode is not passed.
|
||||
status_code = 200;
|
||||
}
|
||||
std::string status("HTTP/1.1 ");
|
||||
status.append(base::NumberToString(status_code));
|
||||
status.append(" ");
|
||||
status.append(
|
||||
net::GetHttpReasonPhrase(static_cast<net::HttpStatusCode>(status_code)));
|
||||
status.append("\0\0", 2);
|
||||
scoped_refptr<net::HttpResponseHeaders> response_headers(
|
||||
new net::HttpResponseHeaders(status));
|
||||
|
||||
if (opts.Get("headers", &value)) {
|
||||
mate::Converter<net::HttpResponseHeaders*>::FromV8(args->isolate(), value,
|
||||
response_headers.get());
|
||||
}
|
||||
|
||||
if (!opts.Get("data", &value)) {
|
||||
// Assume the opts is already a stream
|
||||
value = opts.GetHandle();
|
||||
} else if (value->IsNullOrUndefined()) {
|
||||
// "data" was explicitly passed as null or undefined, assume the user wants
|
||||
// to send an empty body.
|
||||
ended = true;
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::IO},
|
||||
base::BindOnce(&URLRequestStreamJob::StartAsync, job, nullptr,
|
||||
base::RetainedRef(response_headers), ended, error));
|
||||
return;
|
||||
}
|
||||
|
||||
mate::Dictionary data(args->isolate(), v8::Local<v8::Object>::Cast(value));
|
||||
if (!data.Get("on", &value) || !value->IsFunction() ||
|
||||
!data.Get("removeListener", &value) || !value->IsFunction()) {
|
||||
// If data is passed but it is not a stream, signal an error.
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::IO},
|
||||
base::BindOnce(&URLRequestStreamJob::OnError, job, net::ERR_FAILED));
|
||||
return;
|
||||
}
|
||||
|
||||
auto subscriber = base::MakeRefCounted<mate::StreamSubscriber>(
|
||||
args->isolate(), data.GetHandle(), job,
|
||||
base::ThreadTaskRunnerHandle::Get());
|
||||
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::IO},
|
||||
base::BindOnce(&URLRequestStreamJob::StartAsync, job, subscriber,
|
||||
base::RetainedRef(response_headers), ended, error));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
URLRequestStreamJob::URLRequestStreamJob(net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate)
|
||||
: net::URLRequestJob(request, network_delegate),
|
||||
pending_buf_(nullptr),
|
||||
pending_buf_size_(0),
|
||||
ended_(false),
|
||||
response_headers_(nullptr),
|
||||
weak_factory_(this) {}
|
||||
|
||||
URLRequestStreamJob::~URLRequestStreamJob() {
|
||||
DCHECK(!subscriber_ || subscriber_->HasOneRef());
|
||||
}
|
||||
|
||||
void URLRequestStreamJob::Start() {
|
||||
auto request_details = std::make_unique<base::DictionaryValue>();
|
||||
FillRequestDetails(request_details.get(), request());
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::UI},
|
||||
base::BindOnce(
|
||||
&JsAsker::AskForOptions, base::Unretained(isolate()), handler(),
|
||||
std::move(request_details),
|
||||
base::BindOnce(&BeforeStartInUI, weak_factory_.GetWeakPtr())));
|
||||
}
|
||||
|
||||
void URLRequestStreamJob::StartAsync(
|
||||
scoped_refptr<mate::StreamSubscriber> subscriber,
|
||||
scoped_refptr<net::HttpResponseHeaders> response_headers,
|
||||
bool ended,
|
||||
int error) {
|
||||
if (error != net::OK) {
|
||||
NotifyStartError(
|
||||
net::URLRequestStatus(net::URLRequestStatus::FAILED, error));
|
||||
return;
|
||||
}
|
||||
|
||||
ended_ = ended;
|
||||
response_headers_ = response_headers;
|
||||
subscriber_ = subscriber;
|
||||
request_start_time_ = base::TimeTicks::Now();
|
||||
NotifyHeadersComplete();
|
||||
}
|
||||
|
||||
void URLRequestStreamJob::OnData(std::vector<char>&& buffer) { // NOLINT
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
if (write_buffer_.empty()) {
|
||||
// Quick branch without copying.
|
||||
write_buffer_ = std::move(buffer);
|
||||
} else {
|
||||
// write_buffer_ += buffer
|
||||
size_t len = write_buffer_.size();
|
||||
write_buffer_.resize(len + buffer.size());
|
||||
std::copy(buffer.begin(), buffer.end(), write_buffer_.begin() + len);
|
||||
}
|
||||
|
||||
// Copy to output.
|
||||
if (pending_buf_) {
|
||||
int len = BufferCopy(&write_buffer_, pending_buf_.get(), pending_buf_size_);
|
||||
write_buffer_.erase(write_buffer_.begin(), write_buffer_.begin() + len);
|
||||
pending_buf_ = nullptr;
|
||||
pending_buf_size_ = 0;
|
||||
ReadRawDataComplete(len);
|
||||
}
|
||||
}
|
||||
|
||||
void URLRequestStreamJob::OnEnd() {
|
||||
ended_ = true;
|
||||
if (pending_buf_) {
|
||||
ReadRawDataComplete(0);
|
||||
}
|
||||
}
|
||||
|
||||
void URLRequestStreamJob::OnError(int error) {
|
||||
NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED, error));
|
||||
}
|
||||
|
||||
int URLRequestStreamJob::ReadRawData(net::IOBuffer* dest, int dest_size) {
|
||||
response_start_time_ = base::TimeTicks::Now();
|
||||
|
||||
if (ended_ && write_buffer_.empty())
|
||||
return 0;
|
||||
|
||||
// When write_buffer_ is empty, there is no data valable yet, we have to save
|
||||
// the dest buffer util DataAvailable.
|
||||
if (write_buffer_.empty()) {
|
||||
pending_buf_ = dest;
|
||||
pending_buf_size_ = dest_size;
|
||||
return net::ERR_IO_PENDING;
|
||||
}
|
||||
|
||||
// Read from the write buffer and clear them after reading.
|
||||
int len = BufferCopy(&write_buffer_, dest, dest_size);
|
||||
write_buffer_.erase(write_buffer_.begin(), write_buffer_.begin() + len);
|
||||
return len;
|
||||
}
|
||||
|
||||
void URLRequestStreamJob::DoneReading() {
|
||||
write_buffer_.clear();
|
||||
}
|
||||
|
||||
void URLRequestStreamJob::DoneReadingRedirectResponse() {
|
||||
if (subscriber_) {
|
||||
DCHECK(subscriber_->HasAtLeastOneRef());
|
||||
subscriber_ = nullptr;
|
||||
}
|
||||
DoneReading();
|
||||
}
|
||||
|
||||
std::unique_ptr<net::SourceStream> URLRequestStreamJob::SetUpSourceStream() {
|
||||
std::unique_ptr<net::SourceStream> source =
|
||||
net::URLRequestJob::SetUpSourceStream();
|
||||
size_t i = 0;
|
||||
std::string type;
|
||||
while (response_headers_->EnumerateHeader(&i, "Content-Encoding", &type)) {
|
||||
if (base::LowerCaseEqualsASCII(type, "gzip") ||
|
||||
base::LowerCaseEqualsASCII(type, "x-gzip")) {
|
||||
return net::GzipSourceStream::Create(std::move(source),
|
||||
net::SourceStream::TYPE_GZIP);
|
||||
} else if (base::LowerCaseEqualsASCII(type, "deflate")) {
|
||||
return net::GzipSourceStream::Create(std::move(source),
|
||||
net::SourceStream::TYPE_DEFLATE);
|
||||
}
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
bool URLRequestStreamJob::GetMimeType(std::string* mime_type) const {
|
||||
return response_headers_->GetMimeType(mime_type);
|
||||
}
|
||||
|
||||
int URLRequestStreamJob::GetResponseCode() const {
|
||||
return response_headers_->response_code();
|
||||
}
|
||||
|
||||
void URLRequestStreamJob::GetResponseInfo(net::HttpResponseInfo* info) {
|
||||
info->headers = response_headers_;
|
||||
}
|
||||
|
||||
void URLRequestStreamJob::GetLoadTimingInfo(
|
||||
net::LoadTimingInfo* load_timing_info) const {
|
||||
load_timing_info->send_start = request_start_time_;
|
||||
load_timing_info->send_end = request_start_time_;
|
||||
load_timing_info->request_start = request_start_time_;
|
||||
load_timing_info->receive_headers_end = response_start_time_;
|
||||
}
|
||||
|
||||
void URLRequestStreamJob::Kill() {
|
||||
weak_factory_.InvalidateWeakPtrs();
|
||||
net::URLRequestJob::Kill();
|
||||
}
|
||||
|
||||
int URLRequestStreamJob::BufferCopy(std::vector<char>* source,
|
||||
net::IOBuffer* target,
|
||||
int target_size) {
|
||||
int bytes_written = std::min(static_cast<int>(source->size()), target_size);
|
||||
memcpy(target->data(), source->data(), bytes_written);
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
} // namespace atom
|
73
shell/browser/net/url_request_stream_job.h
Normal file
73
shell/browser/net/url_request_stream_job.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
// Copyright (c) 2017 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_URL_REQUEST_STREAM_JOB_H_
|
||||
#define ATOM_BROWSER_NET_URL_REQUEST_STREAM_JOB_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "atom/browser/api/stream_subscriber.h"
|
||||
#include "atom/browser/net/js_asker.h"
|
||||
#include "base/memory/scoped_refptr.h"
|
||||
#include "net/base/io_buffer.h"
|
||||
#include "net/http/http_status_code.h"
|
||||
#include "net/url_request/url_request_job.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class URLRequestStreamJob : public JsAsker, public net::URLRequestJob {
|
||||
public:
|
||||
URLRequestStreamJob(net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate);
|
||||
~URLRequestStreamJob() override;
|
||||
|
||||
void StartAsync(scoped_refptr<mate::StreamSubscriber> subscriber,
|
||||
scoped_refptr<net::HttpResponseHeaders> response_headers,
|
||||
bool ended,
|
||||
int error);
|
||||
|
||||
void OnData(std::vector<char>&& buffer); // NOLINT
|
||||
void OnEnd();
|
||||
void OnError(int error);
|
||||
|
||||
protected:
|
||||
// URLRequestJob
|
||||
void Start() override;
|
||||
int ReadRawData(net::IOBuffer* buf, int buf_size) override;
|
||||
void DoneReading() override;
|
||||
void DoneReadingRedirectResponse() override;
|
||||
std::unique_ptr<net::SourceStream> SetUpSourceStream() override;
|
||||
bool GetMimeType(std::string* mime_type) const override;
|
||||
int GetResponseCode() const override;
|
||||
void GetResponseInfo(net::HttpResponseInfo* info) override;
|
||||
void GetLoadTimingInfo(net::LoadTimingInfo* load_timing_info) const override;
|
||||
void Kill() override;
|
||||
|
||||
private:
|
||||
int BufferCopy(std::vector<char>* source,
|
||||
net::IOBuffer* target,
|
||||
int target_size);
|
||||
|
||||
// Saved arguments passed to ReadRawData.
|
||||
scoped_refptr<net::IOBuffer> pending_buf_;
|
||||
int pending_buf_size_;
|
||||
|
||||
// Saved arguments passed to OnData.
|
||||
std::vector<char> write_buffer_;
|
||||
|
||||
bool ended_;
|
||||
base::TimeTicks request_start_time_;
|
||||
base::TimeTicks response_start_time_;
|
||||
scoped_refptr<net::HttpResponseHeaders> response_headers_;
|
||||
scoped_refptr<mate::StreamSubscriber> subscriber_;
|
||||
|
||||
base::WeakPtrFactory<URLRequestStreamJob> weak_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(URLRequestStreamJob);
|
||||
};
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_URL_REQUEST_STREAM_JOB_H_
|
117
shell/browser/net/url_request_string_job.cc
Normal file
117
shell/browser/net/url_request_string_job.cc
Normal file
|
@ -0,0 +1,117 @@
|
|||
// Copyright (c) 2013 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/url_request_string_job.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "atom/common/atom_constants.h"
|
||||
#include "atom/common/native_mate_converters/net_converter.h"
|
||||
#include "atom/common/native_mate_converters/v8_value_converter.h"
|
||||
#include "base/task/post_task.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "net/base/net_errors.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
void BeforeStartInUI(base::WeakPtr<URLRequestStringJob> job,
|
||||
mate::Arguments* args) {
|
||||
v8::Local<v8::Value> value;
|
||||
int error = net::OK;
|
||||
std::unique_ptr<base::Value> request_options = nullptr;
|
||||
|
||||
if (args->GetNext(&value)) {
|
||||
V8ValueConverter converter;
|
||||
v8::Local<v8::Context> context = args->isolate()->GetCurrentContext();
|
||||
request_options = converter.FromV8Value(value, context);
|
||||
}
|
||||
|
||||
if (request_options) {
|
||||
JsAsker::IsErrorOptions(request_options.get(), &error);
|
||||
} else {
|
||||
error = net::ERR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO},
|
||||
base::BindOnce(&URLRequestStringJob::StartAsync, job,
|
||||
std::move(request_options), error));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
URLRequestStringJob::URLRequestStringJob(net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate)
|
||||
: net::URLRequestSimpleJob(request, network_delegate),
|
||||
weak_factory_(this) {}
|
||||
|
||||
URLRequestStringJob::~URLRequestStringJob() = default;
|
||||
|
||||
void URLRequestStringJob::Start() {
|
||||
auto request_details = std::make_unique<base::DictionaryValue>();
|
||||
FillRequestDetails(request_details.get(), request());
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {content::BrowserThread::UI},
|
||||
base::BindOnce(
|
||||
&JsAsker::AskForOptions, base::Unretained(isolate()), handler(),
|
||||
std::move(request_details),
|
||||
base::BindOnce(&BeforeStartInUI, weak_factory_.GetWeakPtr())));
|
||||
}
|
||||
|
||||
void URLRequestStringJob::StartAsync(std::unique_ptr<base::Value> options,
|
||||
int error) {
|
||||
if (error != net::OK) {
|
||||
NotifyStartError(
|
||||
net::URLRequestStatus(net::URLRequestStatus::FAILED, error));
|
||||
return;
|
||||
}
|
||||
|
||||
if (options->is_dict()) {
|
||||
base::DictionaryValue* dict =
|
||||
static_cast<base::DictionaryValue*>(options.get());
|
||||
dict->GetString("mimeType", &mime_type_);
|
||||
dict->GetString("charset", &charset_);
|
||||
dict->GetString("data", &data_);
|
||||
} else if (options->is_string()) {
|
||||
data_ = options->GetString();
|
||||
}
|
||||
net::URLRequestSimpleJob::Start();
|
||||
}
|
||||
|
||||
void URLRequestStringJob::Kill() {
|
||||
weak_factory_.InvalidateWeakPtrs();
|
||||
net::URLRequestSimpleJob::Kill();
|
||||
}
|
||||
|
||||
void URLRequestStringJob::GetResponseInfo(net::HttpResponseInfo* info) {
|
||||
std::string status("HTTP/1.1 200 OK");
|
||||
auto* headers = new net::HttpResponseHeaders(status);
|
||||
|
||||
headers->AddHeader(kCORSHeader);
|
||||
|
||||
if (!mime_type_.empty()) {
|
||||
std::string content_type_header(net::HttpRequestHeaders::kContentType);
|
||||
content_type_header.append(": ");
|
||||
content_type_header.append(mime_type_);
|
||||
headers->AddHeader(content_type_header);
|
||||
}
|
||||
|
||||
info->headers = headers;
|
||||
}
|
||||
|
||||
int URLRequestStringJob::GetData(std::string* mime_type,
|
||||
std::string* charset,
|
||||
std::string* data,
|
||||
net::CompletionOnceCallback callback) const {
|
||||
*mime_type = mime_type_;
|
||||
*charset = charset_;
|
||||
*data = data_;
|
||||
return net::OK;
|
||||
}
|
||||
|
||||
} // namespace atom
|
46
shell/browser/net/url_request_string_job.h
Normal file
46
shell/browser/net/url_request_string_job.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) 2013 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_URL_REQUEST_STRING_JOB_H_
|
||||
#define ATOM_BROWSER_NET_URL_REQUEST_STRING_JOB_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "atom/browser/net/js_asker.h"
|
||||
#include "net/url_request/url_request_simple_job.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class URLRequestStringJob : public JsAsker, public net::URLRequestSimpleJob {
|
||||
public:
|
||||
URLRequestStringJob(net::URLRequest*, net::NetworkDelegate*);
|
||||
~URLRequestStringJob() override;
|
||||
|
||||
void StartAsync(std::unique_ptr<base::Value> options, int error);
|
||||
|
||||
// URLRequestJob:
|
||||
void Start() override;
|
||||
void GetResponseInfo(net::HttpResponseInfo* info) override;
|
||||
void Kill() override;
|
||||
|
||||
// URLRequestSimpleJob:
|
||||
int GetData(std::string* mime_type,
|
||||
std::string* charset,
|
||||
std::string* data,
|
||||
net::CompletionOnceCallback callback) const override;
|
||||
|
||||
private:
|
||||
std::string mime_type_;
|
||||
std::string charset_;
|
||||
std::string data_;
|
||||
|
||||
base::WeakPtrFactory<URLRequestStringJob> weak_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(URLRequestStringJob);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_URL_REQUEST_STRING_JOB_H_
|
Loading…
Add table
Add a link
Reference in a new issue