blob: 2b65854a4acd61d5e52e4f12a45f4ebae391ebac [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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/strings/stringprintf.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.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/protobuf_http_status.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 {
constexpr char kAuthorizationHeaderFormat[] = "Authorization: Bearer %s";
constexpr char kApiKeyHeaderFormat[] = "x-goog-api-key: %s";
} // namespace
namespace remoting {
ProtobufHttpClient::ProtobufHttpClient(
const std::string& server_endpoint,
OAuthTokenGetter* token_getter,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
: server_endpoint_(server_endpoint),
token_getter_(token_getter),
url_loader_factory_(url_loader_factory) {}
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, {},
{});
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 std::string& user_email,
const std::string& access_token) {
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;
ProtobufHttpStatus::Code code;
switch (status) {
case OAuthTokenGetter::Status::AUTH_ERROR:
code = ProtobufHttpStatus::Code::UNAUTHENTICATED;
break;
case OAuthTokenGetter::Status::NETWORK_ERROR:
code = ProtobufHttpStatus::Code::UNAVAILABLE;
break;
default:
NOTREACHED() << "Unknown OAuthTokenGetter Status: " << status;
code = ProtobufHttpStatus::Code::UNKNOWN;
}
request->OnAuthFailed(ProtobufHttpStatus(code, error_message));
return;
}
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->url =
GURL("https://" + server_endpoint_ + request->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 = net::HttpRequestHeaders::kPostMethod;
if (status == OAuthTokenGetter::Status::SUCCESS && !access_token.empty()) {
resource_request->headers.AddHeaderFromString(
base::StringPrintf(kAuthorizationHeaderFormat, access_token.c_str()));
} else {
VLOG(1) << "Attempting to execute request without access token";
}
if (!request->config().api_key.empty()) {
resource_request->headers.AddHeaderFromString(base::StringPrintf(
kApiKeyHeaderFormat, request->config().api_key.c_str()));
}
std::unique_ptr<network::SimpleURLLoader> send_url_loader =
network::SimpleURLLoader::Create(std::move(resource_request),
request->config().traffic_annotation);
base::TimeDelta timeout_duration = request->GetRequestTimeoutDuration();
if (!timeout_duration.is_zero()) {
send_url_loader->SetTimeoutDuration(request->GetRequestTimeoutDuration());
}
send_url_loader->AttachStringForUpload(
request->config().request_message->SerializeAsString(),
"application/x-protobuf");
send_url_loader->SetAllowHttpErrorResults(true);
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(send_url_loader),
std::move(invalidator));
}
void ProtobufHttpClient::CancelRequest(
const PendingRequestListIterator& request_iterator) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pending_requests_.erase(request_iterator);
}
} // namespace remoting