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

#include "services/network/public/cpp/simple_host_resolver.h"

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/base/network_anonymization_key.h"
#include "services/network/public/cpp/resolve_host_client_base.h"
#include "services/network/public/mojom/host_resolver.mojom.h"
#include "services/network/public/mojom/network_context.mojom.h"

namespace network {

class SimpleHostResolverImpl : public SimpleHostResolver,
                               public ResolveHostClientBase {
 public:
  SimpleHostResolverImpl(mojom::NetworkContext* network_context,
                         NetworkContextFactory network_context_factory)
      : network_context_(network_context),
        network_context_factory_(std::move(network_context_factory)) {
    receivers_.set_disconnect_handler(
        base::BindRepeating(&SimpleHostResolverImpl::OnReceiverDisconnected,
                            base::Unretained(this)));
  }

  void ResolveHost(
      mojom::HostResolverHostPtr host,
      const net::NetworkAnonymizationKey& network_anonymization_key,
      mojom::ResolveHostParametersPtr optional_parameters,
      ResolveHostCallback callback) override {
    mojo::PendingReceiver<mojom::ResolveHostClient> receiver;
    GetNetworkContext()->ResolveHost(std::move(host), network_anonymization_key,
                                     std::move(optional_parameters),
                                     receiver.InitWithNewPipeAndPassRemote());
    receivers_.Add(this, std::move(receiver), std::move(callback));
  }

  uint32_t GetNumOutstandingRequestsForTesting() const override {
    return receivers_.size();
  }

 private:
  // network::ResolveHostClientBase:
  void OnComplete(
      int result,
      const net::ResolveErrorInfo& resolve_error_info,
      const net::AddressList& resolved_addresses,
      const net::HostResolverEndpointResults& alternative_endpoints) override {
    auto callback = std::move(receivers_.current_context());
    receivers_.Remove(receivers_.current_receiver());
    std::move(callback).Run(result, resolve_error_info, resolved_addresses,
                            alternative_endpoints);
  }

  void OnReceiverDisconnected() {
    std::move(receivers_.current_context())
        .Run(net::ERR_FAILED, net::ResolveErrorInfo(net::ERR_FAILED),
             /*resolved_addresses=*/{},
             /*alternative_endpoints=*/{});
  }

  mojom::NetworkContext* GetNetworkContext() const {
    if (network_context_factory_) {
      return network_context_factory_.Run();
    }
    return network_context_;
  }

  // This is kept as `raw_ptr` to help track potential UAFs.
  const raw_ptr<mojom::NetworkContext> network_context_;

  NetworkContextFactory network_context_factory_;

  mojo::ReceiverSet<mojom::ResolveHostClient,
                    SimpleHostResolver::ResolveHostCallback>
      receivers_;
};

// static
std::unique_ptr<SimpleHostResolver> SimpleHostResolver::Create(
    SimpleHostResolver::NetworkContextFactory network_context_factory) {
  return std::make_unique<SimpleHostResolverImpl>(
      /*network_context=*/nullptr,
      /*network_context_factory=*/std::move(network_context_factory));
}

// static
std::unique_ptr<SimpleHostResolver> SimpleHostResolver::Create(
    network::mojom::NetworkContext* network_context) {
  return std::make_unique<SimpleHostResolverImpl>(
      /*network_context=*/network_context,
      /*network_context_factory=*/base::NullCallback());
}

}  // namespace network
