chore: remove _ns suffixes (#20691)
* chore: remove _ns suffixes * lint
This commit is contained in:
parent
cc278cea00
commit
77414813b4
11 changed files with 237 additions and 240 deletions
551
shell/browser/api/atom_api_url_request.cc
Normal file
551
shell/browser/api/atom_api_url_request.cc
Normal file
|
@ -0,0 +1,551 @@
|
|||
// 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 "shell/browser/api/atom_api_url_request.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "gin/handle.h"
|
||||
#include "mojo/public/cpp/bindings/receiver_set.h"
|
||||
#include "mojo/public/cpp/system/string_data_source.h"
|
||||
#include "net/http/http_util.h"
|
||||
#include "services/network/public/mojom/chunked_data_pipe_getter.mojom.h"
|
||||
#include "shell/browser/api/atom_api_session.h"
|
||||
#include "shell/browser/atom_browser_context.h"
|
||||
#include "shell/common/gin_converters/gurl_converter.h"
|
||||
#include "shell/common/gin_converters/net_converter.h"
|
||||
#include "shell/common/gin_helper/dictionary.h"
|
||||
#include "shell/common/gin_helper/event_emitter_caller.h"
|
||||
#include "shell/common/gin_helper/object_template_builder.h"
|
||||
|
||||
#include "shell/common/node_includes.h"
|
||||
|
||||
namespace gin {
|
||||
|
||||
template <>
|
||||
struct Converter<network::mojom::RedirectMode> {
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
network::mojom::RedirectMode* out) {
|
||||
std::string mode;
|
||||
if (!ConvertFromV8(isolate, val, &mode))
|
||||
return false;
|
||||
if (mode == "follow")
|
||||
*out = network::mojom::RedirectMode::kFollow;
|
||||
else if (mode == "error")
|
||||
*out = network::mojom::RedirectMode::kError;
|
||||
else if (mode == "manual")
|
||||
*out = network::mojom::RedirectMode::kManual;
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gin
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace api {
|
||||
|
||||
namespace {
|
||||
|
||||
// Network state for request and response.
|
||||
enum State {
|
||||
STATE_STARTED = 1 << 0,
|
||||
STATE_FINISHED = 1 << 1,
|
||||
STATE_CANCELED = 1 << 2,
|
||||
STATE_FAILED = 1 << 3,
|
||||
STATE_CLOSED = 1 << 4,
|
||||
STATE_ERROR = STATE_CANCELED | STATE_FAILED | STATE_CLOSED,
|
||||
};
|
||||
|
||||
// Annotation tag passed to NetworkService.
|
||||
const net::NetworkTrafficAnnotationTag kTrafficAnnotation =
|
||||
net::DefineNetworkTrafficAnnotation("electron_net_module", R"(
|
||||
semantics {
|
||||
sender: "Electron Net module"
|
||||
description:
|
||||
"Issue HTTP/HTTPS requests using Chromium's native networking "
|
||||
"library."
|
||||
trigger: "Using the Net module"
|
||||
data: "Anything the user wants to send."
|
||||
destination: OTHER
|
||||
}
|
||||
policy {
|
||||
cookies_allowed: YES
|
||||
cookies_store: "user"
|
||||
setting: "This feature cannot be disabled."
|
||||
})");
|
||||
|
||||
} // namespace
|
||||
|
||||
// Common class for streaming data.
|
||||
class UploadDataPipeGetter {
|
||||
public:
|
||||
explicit UploadDataPipeGetter(URLRequest* request) : request_(request) {}
|
||||
virtual ~UploadDataPipeGetter() = default;
|
||||
|
||||
virtual void AttachToRequestBody(network::ResourceRequestBody* body) = 0;
|
||||
|
||||
protected:
|
||||
void SetCallback(network::mojom::DataPipeGetter::ReadCallback callback) {
|
||||
request_->size_callback_ = std::move(callback);
|
||||
}
|
||||
|
||||
void SetPipe(mojo::ScopedDataPipeProducerHandle pipe) {
|
||||
request_->producer_ =
|
||||
std::make_unique<mojo::DataPipeProducer>(std::move(pipe));
|
||||
request_->StartWriting();
|
||||
}
|
||||
|
||||
private:
|
||||
URLRequest* request_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(UploadDataPipeGetter);
|
||||
};
|
||||
|
||||
// Streaming multipart data to NetworkService.
|
||||
class MultipartDataPipeGetter : public UploadDataPipeGetter,
|
||||
public network::mojom::DataPipeGetter {
|
||||
public:
|
||||
explicit MultipartDataPipeGetter(URLRequest* request)
|
||||
: UploadDataPipeGetter(request) {}
|
||||
~MultipartDataPipeGetter() override = default;
|
||||
|
||||
void AttachToRequestBody(network::ResourceRequestBody* body) override {
|
||||
mojo::PendingRemote<network::mojom::DataPipeGetter> data_pipe_getter_remote;
|
||||
receivers_.Add(this,
|
||||
data_pipe_getter_remote.InitWithNewPipeAndPassReceiver());
|
||||
body->AppendDataPipe(std::move(data_pipe_getter_remote));
|
||||
}
|
||||
|
||||
private:
|
||||
// network::mojom::DataPipeGetter:
|
||||
void Read(mojo::ScopedDataPipeProducerHandle pipe,
|
||||
ReadCallback callback) override {
|
||||
SetCallback(std::move(callback));
|
||||
SetPipe(std::move(pipe));
|
||||
}
|
||||
|
||||
void Clone(
|
||||
mojo::PendingReceiver<network::mojom::DataPipeGetter> receiver) override {
|
||||
receivers_.Add(this, std::move(receiver));
|
||||
}
|
||||
|
||||
mojo::ReceiverSet<network::mojom::DataPipeGetter> receivers_;
|
||||
};
|
||||
|
||||
// Streaming chunked data to NetworkService.
|
||||
class ChunkedDataPipeGetter : public UploadDataPipeGetter,
|
||||
public network::mojom::ChunkedDataPipeGetter {
|
||||
public:
|
||||
explicit ChunkedDataPipeGetter(URLRequest* request)
|
||||
: UploadDataPipeGetter(request) {}
|
||||
~ChunkedDataPipeGetter() override = default;
|
||||
|
||||
void AttachToRequestBody(network::ResourceRequestBody* body) override {
|
||||
mojo::PendingRemote<network::mojom::ChunkedDataPipeGetter>
|
||||
data_pipe_getter_remote;
|
||||
receiver_set_.Add(this,
|
||||
data_pipe_getter_remote.InitWithNewPipeAndPassReceiver());
|
||||
body->SetToChunkedDataPipe(std::move(data_pipe_getter_remote));
|
||||
}
|
||||
|
||||
private:
|
||||
// network::mojom::ChunkedDataPipeGetter:
|
||||
void GetSize(GetSizeCallback callback) override {
|
||||
SetCallback(std::move(callback));
|
||||
}
|
||||
|
||||
void StartReading(mojo::ScopedDataPipeProducerHandle pipe) override {
|
||||
SetPipe(std::move(pipe));
|
||||
}
|
||||
|
||||
mojo::ReceiverSet<network::mojom::ChunkedDataPipeGetter> receiver_set_;
|
||||
};
|
||||
|
||||
URLRequest::URLRequest(gin::Arguments* args) : weak_factory_(this) {
|
||||
request_ = std::make_unique<network::ResourceRequest>();
|
||||
gin_helper::Dictionary dict;
|
||||
if (args->GetNext(&dict)) {
|
||||
dict.Get("method", &request_->method);
|
||||
dict.Get("url", &request_->url);
|
||||
dict.Get("redirect", &redirect_mode_);
|
||||
request_->redirect_mode = redirect_mode_;
|
||||
}
|
||||
|
||||
std::string partition;
|
||||
mate::Handle<api::Session> session;
|
||||
if (!dict.Get("session", &session)) {
|
||||
if (dict.Get("partition", &partition))
|
||||
session = Session::FromPartition(args->isolate(), partition);
|
||||
else // default session
|
||||
session = Session::FromPartition(args->isolate(), "");
|
||||
}
|
||||
|
||||
url_loader_factory_ = session->browser_context()->GetURLLoaderFactory();
|
||||
|
||||
InitWithArgs(args);
|
||||
}
|
||||
|
||||
URLRequest::~URLRequest() = default;
|
||||
|
||||
bool URLRequest::NotStarted() const {
|
||||
return request_state_ == 0;
|
||||
}
|
||||
|
||||
bool URLRequest::Finished() const {
|
||||
return request_state_ & STATE_FINISHED;
|
||||
}
|
||||
|
||||
void URLRequest::Cancel() {
|
||||
// Cancel only once.
|
||||
if (request_state_ & (STATE_CANCELED | STATE_CLOSED))
|
||||
return;
|
||||
|
||||
// Mark as canceled.
|
||||
request_state_ |= STATE_CANCELED;
|
||||
EmitEvent(EventType::kRequest, true, "abort");
|
||||
|
||||
if ((response_state_ & STATE_STARTED) && !(response_state_ & STATE_FINISHED))
|
||||
EmitEvent(EventType::kResponse, true, "aborted");
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
void URLRequest::Close() {
|
||||
if (!(request_state_ & STATE_CLOSED)) {
|
||||
request_state_ |= STATE_CLOSED;
|
||||
if (response_state_ & STATE_STARTED) {
|
||||
// Emit a close event if we really have a response object.
|
||||
EmitEvent(EventType::kResponse, true, "close");
|
||||
}
|
||||
EmitEvent(EventType::kRequest, true, "close");
|
||||
}
|
||||
Unpin();
|
||||
loader_.reset();
|
||||
}
|
||||
|
||||
bool URLRequest::Write(v8::Local<v8::Value> data, bool is_last) {
|
||||
if (request_state_ & (STATE_FINISHED | STATE_ERROR))
|
||||
return false;
|
||||
|
||||
size_t length = node::Buffer::Length(data);
|
||||
|
||||
if (!loader_) {
|
||||
// Pin on first write.
|
||||
request_state_ = STATE_STARTED;
|
||||
Pin();
|
||||
|
||||
// Create the loader.
|
||||
network::ResourceRequest* request_ref = request_.get();
|
||||
loader_ = network::SimpleURLLoader::Create(std::move(request_),
|
||||
kTrafficAnnotation);
|
||||
loader_->SetOnResponseStartedCallback(
|
||||
base::Bind(&URLRequest::OnResponseStarted, weak_factory_.GetWeakPtr()));
|
||||
loader_->SetOnRedirectCallback(
|
||||
base::Bind(&URLRequest::OnRedirect, weak_factory_.GetWeakPtr()));
|
||||
loader_->SetOnUploadProgressCallback(
|
||||
base::Bind(&URLRequest::OnUploadProgress, weak_factory_.GetWeakPtr()));
|
||||
|
||||
// Create upload data pipe if we have data to write.
|
||||
if (length > 0) {
|
||||
request_ref->request_body = new network::ResourceRequestBody();
|
||||
if (is_chunked_upload_)
|
||||
data_pipe_getter_ = std::make_unique<ChunkedDataPipeGetter>(this);
|
||||
else
|
||||
data_pipe_getter_ = std::make_unique<MultipartDataPipeGetter>(this);
|
||||
data_pipe_getter_->AttachToRequestBody(request_ref->request_body.get());
|
||||
}
|
||||
|
||||
// Start downloading.
|
||||
loader_->DownloadAsStream(url_loader_factory_.get(), this);
|
||||
}
|
||||
|
||||
if (length > 0)
|
||||
pending_writes_.emplace_back(node::Buffer::Data(data), length);
|
||||
|
||||
if (is_last) {
|
||||
// The ElementsUploadDataStream requires the knowledge of content length
|
||||
// before doing upload, while Node's stream does not give us any size
|
||||
// information. So the only option left for us is to keep all the write
|
||||
// data in memory and flush them after the write is done.
|
||||
//
|
||||
// While this looks frustrating, it is actually the behavior of the non-
|
||||
// NetworkService implementation, and we are not breaking anything.
|
||||
if (!pending_writes_.empty()) {
|
||||
last_chunk_written_ = true;
|
||||
StartWriting();
|
||||
}
|
||||
|
||||
request_state_ |= STATE_FINISHED;
|
||||
EmitEvent(EventType::kRequest, true, "finish");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void URLRequest::FollowRedirect() {
|
||||
if (request_state_ & (STATE_CANCELED | STATE_CLOSED))
|
||||
return;
|
||||
follow_redirect_ = true;
|
||||
}
|
||||
|
||||
bool URLRequest::SetExtraHeader(const std::string& name,
|
||||
const std::string& value) {
|
||||
if (!request_)
|
||||
return false;
|
||||
if (!net::HttpUtil::IsValidHeaderName(name))
|
||||
return false;
|
||||
if (!net::HttpUtil::IsValidHeaderValue(value))
|
||||
return false;
|
||||
request_->headers.SetHeader(name, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
void URLRequest::RemoveExtraHeader(const std::string& name) {
|
||||
if (request_)
|
||||
request_->headers.RemoveHeader(name);
|
||||
}
|
||||
|
||||
void URLRequest::SetChunkedUpload(bool is_chunked_upload) {
|
||||
if (request_)
|
||||
is_chunked_upload_ = is_chunked_upload;
|
||||
}
|
||||
|
||||
gin::Dictionary URLRequest::GetUploadProgress() {
|
||||
gin::Dictionary progress = gin::Dictionary::CreateEmpty(isolate());
|
||||
if (loader_) {
|
||||
if (request_)
|
||||
progress.Set("started", false);
|
||||
else
|
||||
progress.Set("started", true);
|
||||
progress.Set("current", upload_position_);
|
||||
progress.Set("total", upload_total_);
|
||||
progress.Set("active", true);
|
||||
} else {
|
||||
progress.Set("active", false);
|
||||
}
|
||||
return progress;
|
||||
}
|
||||
|
||||
int URLRequest::StatusCode() const {
|
||||
if (response_headers_)
|
||||
return response_headers_->response_code();
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string URLRequest::StatusMessage() const {
|
||||
if (response_headers_)
|
||||
return response_headers_->GetStatusText();
|
||||
return "";
|
||||
}
|
||||
|
||||
net::HttpResponseHeaders* URLRequest::RawResponseHeaders() const {
|
||||
return response_headers_.get();
|
||||
}
|
||||
|
||||
uint32_t URLRequest::ResponseHttpVersionMajor() const {
|
||||
if (response_headers_)
|
||||
return response_headers_->GetHttpVersion().major_value();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t URLRequest::ResponseHttpVersionMinor() const {
|
||||
if (response_headers_)
|
||||
return response_headers_->GetHttpVersion().minor_value();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void URLRequest::OnDataReceived(base::StringPiece data,
|
||||
base::OnceClosure resume) {
|
||||
// In case we received an unexpected event from Chromium net, don't emit any
|
||||
// data event after request cancel/error/close.
|
||||
if (!(request_state_ & STATE_ERROR) && !(response_state_ & STATE_ERROR)) {
|
||||
v8::HandleScope handle_scope(isolate());
|
||||
v8::Local<v8::Value> buffer;
|
||||
auto maybe = node::Buffer::Copy(isolate(), data.data(), data.size());
|
||||
if (maybe.ToLocal(&buffer))
|
||||
Emit("data", buffer);
|
||||
}
|
||||
std::move(resume).Run();
|
||||
}
|
||||
|
||||
void URLRequest::OnRetry(base::OnceClosure start_retry) {}
|
||||
|
||||
void URLRequest::OnComplete(bool success) {
|
||||
if (success) {
|
||||
// In case we received an unexpected event from Chromium net, don't emit any
|
||||
// data event after request cancel/error/close.
|
||||
if (!(request_state_ & STATE_ERROR) && !(response_state_ & STATE_ERROR)) {
|
||||
response_state_ |= STATE_FINISHED;
|
||||
Emit("end");
|
||||
}
|
||||
} else { // failed
|
||||
// If response is started then emit response event, else emit request error.
|
||||
//
|
||||
// Error is only emitted when there is no previous failure. This is to align
|
||||
// with the behavior of non-NetworkService implementation.
|
||||
std::string error = net::ErrorToString(loader_->NetError());
|
||||
if (response_state_ & STATE_STARTED) {
|
||||
if (!(response_state_ & STATE_FAILED))
|
||||
EmitError(EventType::kResponse, error);
|
||||
} else {
|
||||
if (!(request_state_ & STATE_FAILED))
|
||||
EmitError(EventType::kRequest, error);
|
||||
}
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
void URLRequest::OnResponseStarted(
|
||||
const GURL& final_url,
|
||||
const network::mojom::URLResponseHead& response_head) {
|
||||
// Don't emit any event after request cancel.
|
||||
if (request_state_ & STATE_ERROR)
|
||||
return;
|
||||
|
||||
response_headers_ = response_head.headers;
|
||||
response_state_ |= STATE_STARTED;
|
||||
Emit("response");
|
||||
}
|
||||
|
||||
void URLRequest::OnRedirect(
|
||||
const net::RedirectInfo& redirect_info,
|
||||
const network::mojom::URLResponseHead& response_head,
|
||||
std::vector<std::string>* to_be_removed_headers) {
|
||||
if (!loader_)
|
||||
return;
|
||||
|
||||
if (request_state_ & (STATE_CLOSED | STATE_CANCELED)) {
|
||||
NOTREACHED();
|
||||
Cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (redirect_mode_) {
|
||||
case network::mojom::RedirectMode::kError:
|
||||
Cancel();
|
||||
EmitError(
|
||||
EventType::kRequest,
|
||||
"Request cannot follow redirect with the current redirect mode");
|
||||
break;
|
||||
case network::mojom::RedirectMode::kManual:
|
||||
// When redirect mode is "manual", the user has to explicitly call the
|
||||
// FollowRedirect method to continue redirecting, otherwise the request
|
||||
// would be cancelled.
|
||||
//
|
||||
// Note that the SimpleURLLoader always calls FollowRedirect and does not
|
||||
// provide a formal way for us to cancel redirection, we have to cancel
|
||||
// the request to prevent the redirection.
|
||||
follow_redirect_ = false;
|
||||
EmitEvent(EventType::kRequest, false, "redirect",
|
||||
redirect_info.status_code, redirect_info.new_method,
|
||||
redirect_info.new_url, response_head.headers.get());
|
||||
if (!follow_redirect_)
|
||||
Cancel();
|
||||
break;
|
||||
case network::mojom::RedirectMode::kFollow:
|
||||
EmitEvent(EventType::kRequest, false, "redirect",
|
||||
redirect_info.status_code, redirect_info.new_method,
|
||||
redirect_info.new_url, response_head.headers.get());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void URLRequest::OnUploadProgress(uint64_t position, uint64_t total) {
|
||||
upload_position_ = position;
|
||||
upload_total_ = total;
|
||||
}
|
||||
|
||||
void URLRequest::OnWrite(MojoResult result) {
|
||||
if (result != MOJO_RESULT_OK)
|
||||
return;
|
||||
|
||||
// Continue the pending writes.
|
||||
pending_writes_.pop_front();
|
||||
if (!pending_writes_.empty())
|
||||
DoWrite();
|
||||
}
|
||||
|
||||
void URLRequest::DoWrite() {
|
||||
DCHECK(producer_);
|
||||
DCHECK(!pending_writes_.empty());
|
||||
producer_->Write(
|
||||
std::make_unique<mojo::StringDataSource>(
|
||||
pending_writes_.front(), mojo::StringDataSource::AsyncWritingMode::
|
||||
STRING_STAYS_VALID_UNTIL_COMPLETION),
|
||||
base::BindOnce(&URLRequest::OnWrite, weak_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
void URLRequest::StartWriting() {
|
||||
if (!last_chunk_written_ || size_callback_.is_null())
|
||||
return;
|
||||
|
||||
size_t size = 0;
|
||||
for (const auto& data : pending_writes_)
|
||||
size += data.size();
|
||||
std::move(size_callback_).Run(net::OK, size);
|
||||
DoWrite();
|
||||
}
|
||||
|
||||
void URLRequest::Pin() {
|
||||
if (wrapper_.IsEmpty()) {
|
||||
wrapper_.Reset(isolate(), GetWrapper());
|
||||
}
|
||||
}
|
||||
|
||||
void URLRequest::Unpin() {
|
||||
wrapper_.Reset();
|
||||
}
|
||||
|
||||
void URLRequest::EmitError(EventType type, base::StringPiece message) {
|
||||
if (type == EventType::kRequest)
|
||||
request_state_ |= STATE_FAILED;
|
||||
else
|
||||
response_state_ |= STATE_FAILED;
|
||||
v8::HandleScope handle_scope(isolate());
|
||||
auto error = v8::Exception::Error(gin::StringToV8(isolate(), message));
|
||||
EmitEvent(type, false, "error", error);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void URLRequest::EmitEvent(EventType type, Args... args) {
|
||||
const char* method =
|
||||
type == EventType::kRequest ? "_emitRequestEvent" : "_emitResponseEvent";
|
||||
v8::HandleScope handle_scope(isolate());
|
||||
gin_helper::CustomEmit(isolate(), GetWrapper(), method, args...);
|
||||
}
|
||||
|
||||
// static
|
||||
mate::WrappableBase* URLRequest::New(gin::Arguments* args) {
|
||||
return new URLRequest(args);
|
||||
}
|
||||
|
||||
// static
|
||||
void URLRequest::BuildPrototype(v8::Isolate* isolate,
|
||||
v8::Local<v8::FunctionTemplate> prototype) {
|
||||
prototype->SetClassName(gin::StringToV8(isolate, "URLRequest"));
|
||||
gin_helper::Destroyable::MakeDestroyable(isolate, prototype);
|
||||
gin_helper::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
|
||||
.SetMethod("write", &URLRequest::Write)
|
||||
.SetMethod("cancel", &URLRequest::Cancel)
|
||||
.SetMethod("setExtraHeader", &URLRequest::SetExtraHeader)
|
||||
.SetMethod("removeExtraHeader", &URLRequest::RemoveExtraHeader)
|
||||
.SetMethod("setChunkedUpload", &URLRequest::SetChunkedUpload)
|
||||
.SetMethod("followRedirect", &URLRequest::FollowRedirect)
|
||||
.SetMethod("getUploadProgress", &URLRequest::GetUploadProgress)
|
||||
.SetProperty("notStarted", &URLRequest::NotStarted)
|
||||
.SetProperty("finished", &URLRequest::Finished)
|
||||
.SetProperty("statusCode", &URLRequest::StatusCode)
|
||||
.SetProperty("statusMessage", &URLRequest::StatusMessage)
|
||||
.SetProperty("rawResponseHeaders", &URLRequest::RawResponseHeaders)
|
||||
.SetProperty("httpVersionMajor", &URLRequest::ResponseHttpVersionMajor)
|
||||
.SetProperty("httpVersionMinor", &URLRequest::ResponseHttpVersionMinor);
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
|
||||
} // namespace electron
|
Loading…
Add table
Add a link
Reference in a new issue