chromium/chromeos/ash/components/boca/babelorca/tachyon_client_impl.cc

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromeos/ash/components/boca/babelorca/tachyon_client_impl.h"

#include <memory>
#include <string>
#include <utility>

#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/stringprintf.h"
#include "base/types/expected.h"
#include "chromeos/ash/components/boca/babelorca/request_data_wrapper.h"
#include "chromeos/ash/components/boca/babelorca/response_callback_wrapper.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_status_code.h"
#include "services/network/public/cpp/header_util.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 "services/network/public/mojom/fetch_api.mojom-shared.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "url/gurl.h"

namespace ash::babelorca {
namespace {

// TODO(b/353974384): Identify an accurate max size.
constexpr int kMaxResponseBodySize = 1024 * 1024;
constexpr char kOauthHeaderTemplate[] = "Authorization: Bearer %s";

}  // namespace

TachyonClientImpl::TachyonClientImpl(
    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
    : url_loader_factory_(url_loader_factory) {}

TachyonClientImpl::~TachyonClientImpl() = default;

void TachyonClientImpl::StartRequest(
    std::unique_ptr<RequestDataWrapper> request_data,
    std::string oauth_token,
    AuthFailureCallback auth_failure_cb) {
  auto resource_request = std::make_unique<network::ResourceRequest>();
  resource_request->url = GURL(request_data->url);
  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;
  resource_request->headers.AddHeaderFromString(
      base::StringPrintf(kOauthHeaderTemplate, oauth_token.c_str()));

  std::unique_ptr<network::SimpleURLLoader> url_loader =
      network::SimpleURLLoader::Create(std::move(resource_request),
                                       request_data->annotation_tag);
  auto* url_loader_ptr = url_loader.get();
  url_loader_ptr->AttachStringForUpload(request_data->content_data,
                                        "application/x-protobuf");
  if (request_data->max_retries > 0) {
    const int retry_mode = network::SimpleURLLoader::RETRY_ON_5XX |
                           network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE;
    url_loader_ptr->SetRetryOptions(request_data->max_retries, retry_mode);
  }
  url_loader_ptr->DownloadToString(
      url_loader_factory_.get(),
      base::BindOnce(&TachyonClientImpl::OnResponse,
                     weak_ptr_factory_.GetWeakPtr(), std::move(url_loader),
                     std::move(request_data), std::move(auth_failure_cb)),
      kMaxResponseBodySize);
}

void TachyonClientImpl::OnResponse(
    std::unique_ptr<network::SimpleURLLoader> url_loader,
    std::unique_ptr<RequestDataWrapper> request_data,
    AuthFailureCallback auth_failure_cb,
    std::unique_ptr<std::string> response_body) {
  if (url_loader->NetError() != net::OK &&
      url_loader->NetError() != net::ERR_HTTP_RESPONSE_CODE_FAILURE) {
    request_data->response_cb->Run(base::unexpected(
        ResponseCallbackWrapper::TachyonRequestError::kNetworkError));
    return;
  }
  if (!url_loader->ResponseInfo() || !url_loader->ResponseInfo()->headers) {
    request_data->response_cb->Run(base::unexpected(
        ResponseCallbackWrapper::TachyonRequestError::kInternalError));
    return;
  }
  const int response_code =
      url_loader->ResponseInfo()->headers->response_code();
  if (response_code == net::HttpStatusCode::HTTP_UNAUTHORIZED) {
    std::move(auth_failure_cb).Run(std::move(request_data));
    return;
  }
  if (!network::IsSuccessfulStatus(response_code)) {
    request_data->response_cb->Run(base::unexpected(
        ResponseCallbackWrapper::TachyonRequestError::kHttpError));
    return;
  }
  if (!response_body) {
    request_data->response_cb->Run(base::unexpected(
        ResponseCallbackWrapper::TachyonRequestError::kInternalError));
    return;
  }
  request_data->response_cb->Run(std::move(*response_body));
}

}  // namespace ash::babelorca