[go: nahoru, domu]

blob: eabeb73a0d1e8a8d5dc245220ab3c7114eb91272 [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 "components/autofill_assistant/browser/service/service_request_sender_impl.h"
#include "base/strings/strcat.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/storage_partition.h"
#include "net/base/load_flags.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/simple_url_loader.h"
namespace {
constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
net::DefineNetworkTrafficAnnotation("autofill_service", R"(
semantics {
sender: "Autofill Assistant"
description:
"Chromium posts requests to autofill assistant server to get
scripts for a URL."
trigger:
"Matching URL."
data: "None."
destination: GOOGLE_OWNED_SERVICE
}
policy {
cookies_allowed: NO
setting:
"This feature can be disabled in settings."
policy_exception_justification: "Not implemented."
})");
void OnURLLoaderComplete(
autofill_assistant::ServiceRequestSender::ResponseCallback callback,
std::unique_ptr<::network::SimpleURLLoader> loader,
std::unique_ptr<std::string> response_body) {
std::string response_str;
if (response_body != nullptr) {
response_str = std::move(*response_body);
}
int response_code = 0;
if (loader->ResponseInfo() && loader->ResponseInfo()->headers) {
response_code = loader->ResponseInfo()->headers->response_code();
}
VLOG(3) << "Received response: status=" << response_code << ", "
<< response_str.length() << " bytes";
std::move(callback).Run(response_code, response_str);
}
std::unique_ptr<::network::ResourceRequest> CreateResourceRequest(GURL url) {
auto resource_request = std::make_unique<::network::ResourceRequest>();
resource_request->url = url;
resource_request->method = "POST";
resource_request->redirect_mode = ::network::mojom::RedirectMode::kError;
resource_request->credentials_mode = ::network::mojom::CredentialsMode::kOmit;
return resource_request;
}
void SendRequestImpl(
std::unique_ptr<::network::ResourceRequest> request,
const std::string& request_body,
content::BrowserContext* context,
autofill_assistant::SimpleURLLoaderFactory* loader_factory,
autofill_assistant::ServiceRequestSender::ResponseCallback callback) {
auto loader =
loader_factory->CreateLoader(std::move(request), kTrafficAnnotation);
loader->AttachStringForUpload(request_body, "application/x-protobuffer");
#ifdef DEBUG
loader->SetAllowHttpErrorResults(true);
#endif
auto* const loader_ptr = loader.get();
loader_ptr->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
content::BrowserContext::GetDefaultStoragePartition(context)
->GetURLLoaderFactoryForBrowserProcess()
.get(),
base::BindOnce(&OnURLLoaderComplete, std::move(callback),
std::move(loader)));
}
void SendRequestNoAuth(
const GURL& url,
const std::string& request_body,
content::BrowserContext* context,
autofill_assistant::SimpleURLLoaderFactory* loader_factory,
const std::string& api_key,
autofill_assistant::ServiceRequestSender::ResponseCallback callback) {
if (callback.IsCancelled()) {
return;
}
if (api_key.empty()) {
LOG(ERROR) << "no api key provided";
std::move(callback).Run(net::HTTP_UNAUTHORIZED, std::string());
return;
}
std::string query_str = base::StrCat({"key=", api_key});
// query_str must remain valid until ReplaceComponents() has returned.
url::StringPieceReplacements<std::string> add_key;
add_key.SetQueryStr(query_str);
GURL modified_url = url.ReplaceComponents(add_key);
VLOG(2) << "Sending request with api key to backend";
SendRequestImpl(CreateResourceRequest(modified_url), request_body, context,
loader_factory, std::move(callback));
}
} // namespace
namespace autofill_assistant {
ServiceRequestSenderImpl::ServiceRequestSenderImpl(
content::BrowserContext* context,
AccessTokenFetcher* access_token_fetcher,
std::unique_ptr<SimpleURLLoaderFactory> loader_factory,
const std::string& api_key,
bool auth_enabled,
bool disable_auth_if_no_access_token)
: context_(context),
access_token_fetcher_(access_token_fetcher),
loader_factory_(std::move(loader_factory)),
api_key_(api_key),
auth_enabled_(auth_enabled),
disable_auth_if_no_access_token_(disable_auth_if_no_access_token) {
DCHECK(!auth_enabled || access_token_fetcher != nullptr);
DCHECK(auth_enabled || !api_key.empty());
}
ServiceRequestSenderImpl::~ServiceRequestSenderImpl() = default;
void ServiceRequestSenderImpl::SendRequest(const GURL& url,
const std::string& request_body,
ResponseCallback callback) {
if (auth_enabled_ && access_token_fetcher_ == nullptr) {
LOG(ERROR) << "auth requested, but no access token fetcher provided";
std::move(callback).Run(net::HTTP_UNAUTHORIZED, std::string());
return;
}
if (auth_enabled_) {
access_token_fetcher_->FetchAccessToken(
base::BindOnce(&ServiceRequestSenderImpl::OnFetchAccessToken,
weak_ptr_factory_.GetWeakPtr(), url, request_body,
std::move(callback)));
return;
}
SendRequestNoAuth(url, request_body, context_, loader_factory_.get(),
api_key_, std::move(callback));
}
void ServiceRequestSenderImpl::OnFetchAccessToken(
GURL url,
std::string request_body,
ResponseCallback callback,
bool access_token_fetched,
const std::string& access_token) {
if (!access_token_fetched || access_token.empty()) {
if (disable_auth_if_no_access_token_) {
// Give up on authentication for this run. Without access token, requests
// might be successful or rejected, depending on the server configuration.
auth_enabled_ = false;
VLOG(1) << "No access token, falling back to api key";
SendRequestNoAuth(url, request_body, context_, loader_factory_.get(),
api_key_, std::move(callback));
return;
}
VLOG(1) << "No access token, but disable_auth_if_no_access_token not set";
std::move(callback).Run(net::HTTP_UNAUTHORIZED, std::string());
return;
}
SendRequestAuth(url, request_body, access_token, std::move(callback));
}
void ServiceRequestSenderImpl::SendRequestAuth(const GURL& url,
const std::string& request_body,
const std::string& access_token,
ResponseCallback callback) {
if (callback.IsCancelled()) {
return;
}
auto resource_request = CreateResourceRequest(url);
resource_request->headers.SetHeader("Authorization",
base::StrCat({"Bearer ", access_token}));
if (!retried_with_fresh_access_token_) {
callback = base::BindOnce(&ServiceRequestSenderImpl::RetryIfUnauthorized,
weak_ptr_factory_.GetWeakPtr(), url, access_token,
request_body, std::move(callback));
}
VLOG(2) << "Sending request with access token to backend";
SendRequestImpl(std::move(resource_request), request_body, context_,
loader_factory_.get(), std::move(callback));
}
void ServiceRequestSenderImpl::RetryIfUnauthorized(
const GURL& url,
const std::string& access_token,
const std::string& request_body,
ResponseCallback callback,
int http_status,
const std::string& response) {
// On first UNAUTHORIZED error, invalidate access token and try again.
if (auth_enabled_ && http_status == net::HTTP_UNAUTHORIZED) {
VLOG(1) << "Request with access token returned with 401 UNAUTHORIZED, "
"fetching a fresh access token and trying again";
DCHECK(!retried_with_fresh_access_token_);
retried_with_fresh_access_token_ = true;
access_token_fetcher_->InvalidateAccessToken(access_token);
SendRequest(url, request_body, std::move(callback));
return;
}
std::move(callback).Run(http_status, response);
}
} // namespace autofill_assistant