#include "chrome/updater/app/app_net_worker.h"
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "base/command_line.h"
#include "base/containers/flat_map.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/task/bind_post_task.h"
#include "base/threading/sequence_bound.h"
#include "base/threading/thread.h"
#include "chrome/updater/app/app.h"
#include "chrome/updater/constants.h"
#include "chrome/updater/net/mac/mojom/updater_fetcher.mojom.h"
#include "chrome/updater/net/network.h"
#include "chrome/updater/net/network_file_fetcher.h"
#include "chrome/updater/policy/service.h"
#include "components/update_client/network.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/system/invitation.h"
#include "mojo/public/cpp/system/message_pipe.h"
namespace updater {
namespace {
class PostRequestObserverWrapper
: public base::RefCountedThreadSafe<PostRequestObserverWrapper> {
public:
explicit PostRequestObserverWrapper(
mojom::FetchService::PostRequestCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::move(callback).Run(observer_.BindNewPipeAndPassReceiver());
}
void OnResponseStarted(int32_t http_status_code, int64_t content_length) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observer_->OnResponseStarted(http_status_code, content_length);
}
void OnProgress(int64_t current) { observer_->OnProgress(current); }
void OnRequestComplete(std::optional<std::string> response_body,
int32_t net_error,
const std::string& header_etag,
const std::string& header_x_cup_server_proof,
const std::string& header_set_cookie,
int64_t xheader_retry_after_sec) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observer_->OnRequestComplete(*response_body, net_error, header_etag,
header_x_cup_server_proof, header_set_cookie,
xheader_retry_after_sec);
}
private:
friend class base::RefCountedThreadSafe<PostRequestObserverWrapper>;
virtual ~PostRequestObserverWrapper() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
SEQUENCE_CHECKER(sequence_checker_);
mojo::Remote<mojom::PostRequestObserver> observer_;
};
class FileDownloadObserverWrapper
: public base::RefCountedThreadSafe<FileDownloadObserverWrapper> {
public:
explicit FileDownloadObserverWrapper(
mojom::FetchService::DownloadToFileCallback callback) {
std::move(callback).Run(observer_.BindNewPipeAndPassReceiver());
}
void OnResponseStarted(int32_t http_status_code, int64_t content_length) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observer_->OnResponseStarted(http_status_code, content_length);
}
void OnProgress(int64_t current) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observer_->OnProgress(current);
}
void OnDownloadComplete(int32_t net_error, int64_t content_length) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observer_->OnDownloadComplete(net_error, content_length);
}
private:
friend class base::RefCountedThreadSafe<FileDownloadObserverWrapper>;
virtual ~FileDownloadObserverWrapper() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
SEQUENCE_CHECKER(sequence_checker_);
mojo::Remote<mojom::FileDownloadObserver> observer_;
};
class FetchServiceImpl : public mojom::FetchService {
public:
FetchServiceImpl(mojo::PendingReceiver<mojom::FetchService> pending_receiver,
base::OnceCallback<void(int)> on_complete_callback);
~FetchServiceImpl() override;
void PostRequest(const ::GURL& url,
const std::string& post_data,
const std::string& content_type,
std::vector<mojom::HttpHeaderPtr> additional_headers,
mojom::FetchService::PostRequestCallback callback) override;
void DownloadToFile(
const ::GURL& url,
::base::File output_file,
mojom::FetchService::DownloadToFileCallback callback) override;
private:
SEQUENCE_CHECKER(sequence_checker_);
mojo::Receiver<mojom::FetchService> receiver_;
};
FetchServiceImpl::FetchServiceImpl(
mojo::PendingReceiver<mojom::FetchService> pending_receiver,
base::OnceCallback<void(int)> shutdown_callback)
: receiver_(this, std::move(pending_receiver)) {
receiver_.set_disconnect_handler(
base::BindOnce(std::move(shutdown_callback), kErrorOk));
}
FetchServiceImpl::~FetchServiceImpl() = default;
void FetchServiceImpl::PostRequest(
const ::GURL& url,
const std::string& post_data,
const std::string& content_type,
std::vector<mojom::HttpHeaderPtr> additional_headers,
mojom::FetchService::PostRequestCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto wrapper =
base::MakeRefCounted<PostRequestObserverWrapper>(std::move(callback));
base::flat_map<std::string, std::string> headers;
for (const auto& header : additional_headers) {
headers.emplace(header->name, header->value);
}
std::unique_ptr<update_client::NetworkFetcher> fetcher =
base::MakeRefCounted<NetworkFetcherFactory>(
std::nullopt,
nullptr)
->Create();
update_client::NetworkFetcher* fetcher_ptr = fetcher.get();
fetcher_ptr->PostRequest(
url, post_data, content_type, headers,
base::BindRepeating(&PostRequestObserverWrapper::OnResponseStarted,
wrapper),
base::BindRepeating(&PostRequestObserverWrapper::OnProgress, wrapper),
base::BindOnce(
[](std::unique_ptr<update_client::NetworkFetcher> ,
scoped_refptr<PostRequestObserverWrapper> wrapper,
std::optional<std::string> response_body, int32_t net_error,
const std::string& header_etag,
const std::string& header_x_cup_server_proof,
const std::string& header_set_cookie,
int64_t xheader_retry_after_sec) {
wrapper->OnRequestComplete(std::move(response_body), net_error,
header_etag, header_x_cup_server_proof,
header_set_cookie,
xheader_retry_after_sec);
},
std::move(fetcher), wrapper));
}
void FetchServiceImpl::DownloadToFile(
const ::GURL& url,
::base::File output_file,
mojom::FetchService::DownloadToFileCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto wrapper =
base::MakeRefCounted<FileDownloadObserverWrapper>(std::move(callback));
std::unique_ptr<NetworkFileFetcher> file_fetcher =
std::make_unique<NetworkFileFetcher>();
NetworkFileFetcher* file_fetcher_ptr = file_fetcher.get();
file_fetcher_ptr->Download(
url, std::move(output_file),
base::BindRepeating(&FileDownloadObserverWrapper::OnResponseStarted,
wrapper),
base::BindRepeating(&FileDownloadObserverWrapper::OnProgress, wrapper),
base::BindOnce(
[](std::unique_ptr<NetworkFileFetcher> ,
scoped_refptr<FileDownloadObserverWrapper> wrapper,
int32_t net_error, int64_t content_length) {
wrapper->OnDownloadComplete(net_error, content_length);
},
std::move(file_fetcher), wrapper));
}
class AppNetWorker : public App {
public:
AppNetWorker() {
net_thread_.StartWithOptions({base::MessagePumpType::IO, 0});
}
private:
~AppNetWorker() override = default;
void FirstTaskRun() override {
mojo::PlatformChannelEndpoint endpoint =
mojo::PlatformChannel::RecoverPassedEndpointFromCommandLine(
*base::CommandLine::ForCurrentProcess());
if (!endpoint.is_valid()) {
Shutdown(kErrorMojoConnectionFailure);
return;
}
mojo::ScopedMessagePipeHandle pipe =
mojo::IncomingInvitation::AcceptIsolated(std::move(endpoint));
if (!pipe->is_valid()) {
Shutdown(kErrorMojoConnectionFailure);
return;
}
fetcher_stub_ = base::SequenceBound<FetchServiceImpl>(
net_thread_.task_runner(),
mojo::PendingReceiver<mojom::FetchService>(std::move(pipe)),
base::BindPostTaskToCurrentDefault(base::BindOnce(
&AppNetWorker::Shutdown, weak_ptr_factory_.GetWeakPtr())));
}
base::Thread net_thread_{"Network"};
base::SequenceBound<FetchServiceImpl> fetcher_stub_;
base::WeakPtrFactory<AppNetWorker> weak_ptr_factory_{this};
};
}
scoped_refptr<App> MakeAppNetWorker() {
return base::MakeRefCounted<AppNetWorker>();
}
}