blob: 8ee372b5394e3ec043ed0be7d7539b814f76532f [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/base/protobuf_http_client.h"
#include "base/functional/bind.h"
#include "base/strings/stringprintf.h"
#include "google_apis/common/api_key_request_util.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/http/http_request_headers.h"
#include "remoting/base/http_status.h"
#include "remoting/base/oauth_token_getter.h"
#include "remoting/base/protobuf_http_request_base.h"
#include "remoting/base/protobuf_http_request_config.h"
#include "remoting/base/url_loader_network_service_observer.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "third_party/protobuf/src/google/protobuf/message_lite.h"
#include "url/gurl.h"
namespace remoting {
ProtobufHttpClient::ProtobufHttpClient(
const std::string& server_endpoint,
OAuthTokenGetter* token_getter,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
std::unique_ptr<net::ClientCertStore> client_cert_store)
: server_endpoint_(server_endpoint),
token_getter_(token_getter),
url_loader_factory_(url_loader_factory),
client_cert_store_(std::move(client_cert_store)) {}
ProtobufHttpClient::~ProtobufHttpClient() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void ProtobufHttpClient::ExecuteRequest(
std::unique_ptr<ProtobufHttpRequestBase> request) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!request->config().authenticated) {
DoExecuteRequest(std::move(request), OAuthTokenGetter::Status::SUCCESS,
OAuthTokenInfo());
return;
}
DCHECK(token_getter_);
token_getter_->CallWithToken(
base::BindOnce(&ProtobufHttpClient::DoExecuteRequest,
weak_factory_.GetWeakPtr(), std::move(request)));
}
void ProtobufHttpClient::CancelPendingRequests() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
weak_factory_.InvalidateWeakPtrs();
pending_requests_.clear();
}
bool ProtobufHttpClient::HasPendingRequests() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return !pending_requests_.empty();
}
void ProtobufHttpClient::DoExecuteRequest(
std::unique_ptr<ProtobufHttpRequestBase> request,
OAuthTokenGetter::Status status,
const OAuthTokenInfo& token_info) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (status != OAuthTokenGetter::Status::SUCCESS) {
std::string error_message =
base::StringPrintf("Failed to fetch access token. Status: %d", status);
LOG(ERROR) << error_message;
HttpStatus::Code code;
switch (status) {
case OAuthTokenGetter::Status::AUTH_ERROR:
code = HttpStatus::Code::UNAUTHENTICATED;
break;
case OAuthTokenGetter::Status::NETWORK_ERROR:
code = HttpStatus::Code::NETWORK_ERROR;
break;
default:
NOTREACHED() << "Unknown OAuthTokenGetter Status: " << status;
}
request->OnAuthFailed(HttpStatus(code, error_message));
return;
}
// SimpleURLLoader can only be used once, so we just bind and pass a creator
// callback.
// Can't bind a WeakPtr here since the callback returns a value. Using
// Unretained is safe since `request` is owned by `this`.
auto create_simple_url_loader = base::BindRepeating(
&ProtobufHttpClient::CreateSimpleUrlLoader, base::Unretained(this),
status == OAuthTokenGetter::Status::SUCCESS ? token_info.access_token()
: std::string(),
request->GetRequestTimeoutDuration());
auto* unowned_request = request.get();
base::OnceClosure invalidator = base::BindOnce(
&ProtobufHttpClient::CancelRequest, weak_factory_.GetWeakPtr(),
pending_requests_.insert(pending_requests_.end(), std::move(request)));
unowned_request->StartRequest(url_loader_factory_.get(),
std::move(create_simple_url_loader),
std::move(invalidator));
}
std::unique_ptr<network::SimpleURLLoader>
ProtobufHttpClient::CreateSimpleUrlLoader(
const std::string& access_token,
base::TimeDelta timeout_duration,
const ProtobufHttpRequestConfig& config) {
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->url = GURL("https://" + server_endpoint_ + config.path);
resource_request->load_flags =
net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE;
resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
resource_request->method = config.method;
if (!access_token.empty()) {
resource_request->headers.SetHeader(net::HttpRequestHeaders::kAuthorization,
"Bearer " + access_token);
} else {
VLOG(1) << "Attempting to execute request without access token";
}
if (!config.api_key.empty()) {
google_apis::AddAPIKeyToRequest(*resource_request, config.api_key);
}
if (config.provide_certificate) {
if (!resource_request->trusted_params.has_value()) {
resource_request->trusted_params.emplace();
}
if (!service_observer_.has_value()) {
CHECK(client_cert_store_);
service_observer_.emplace(std::move(client_cert_store_));
}
resource_request->trusted_params->url_loader_network_observer =
service_observer_->Bind();
}
std::unique_ptr<network::SimpleURLLoader> send_url_loader =
network::SimpleURLLoader::Create(std::move(resource_request),
config.traffic_annotation);
if (!timeout_duration.is_zero()) {
send_url_loader->SetTimeoutDuration(timeout_duration);
}
send_url_loader->AttachStringForUpload(
config.request_message->SerializeAsString(), "application/x-protobuf");
send_url_loader->SetAllowHttpErrorResults(true);
return send_url_loader;
}
void ProtobufHttpClient::CancelRequest(
const PendingRequestListIterator& request_iterator) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pending_requests_.erase(request_iterator);
}
} // namespace remoting