// 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 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 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 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(); 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 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