// Copyright 2019 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 "quiche/quic/masque/masque_client.h"

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

#include "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
#include "quiche/quic/core/crypto/proof_verifier.h"
#include "quiche/quic/core/io/quic_event_loop.h"
#include "quiche/quic/core/quic_connection.h"
#include "quiche/quic/core/quic_connection_id.h"
#include "quiche/quic/core/quic_error_codes.h"
#include "quiche/quic/core/quic_session.h"
#include "quiche/quic/core/quic_versions.h"
#include "quiche/quic/masque/masque_client_session.h"
#include "quiche/quic/masque/masque_utils.h"
#include "quiche/quic/platform/api/quic_logging.h"
#include "quiche/quic/platform/api/quic_socket_address.h"
#include "quiche/quic/tools/quic_default_client.h"
#include "quiche/quic/tools/quic_name_lookup.h"
#include "quiche/quic/tools/quic_url.h"

namespace quic {

MasqueClient::MasqueClient(QuicSocketAddress server_address,
                           const QuicServerId& server_id,
                           MasqueMode masque_mode, QuicEventLoop* event_loop,
                           std::unique_ptr<ProofVerifier> proof_verifier,
                           const std::string& uri_template)
    : QuicDefaultClient(server_address, server_id, MasqueSupportedVersions(),
                        event_loop, std::move(proof_verifier)),
      masque_mode_(masque_mode),
      uri_template_(uri_template) {}

std::unique_ptr<QuicSession> MasqueClient::CreateQuicClientSession(
    const ParsedQuicVersionVector& supported_versions,
    QuicConnection* connection) {
  QUIC_DLOG(INFO) << "Creating MASQUE session for "
                  << connection->connection_id();
  return std::make_unique<MasqueClientSession>(
      masque_mode_, uri_template_, *config(), supported_versions, connection,
      server_id(), crypto_config(), this);
}

MasqueClientSession* MasqueClient::masque_client_session() {
  return static_cast<MasqueClientSession*>(QuicDefaultClient::session());
}

QuicConnectionId MasqueClient::connection_id() {
  return masque_client_session()->connection_id();
}

std::string MasqueClient::authority() const {
  QuicUrl url(uri_template_);
  return absl::StrCat(url.host(), ":", url.port());
}

// static
std::unique_ptr<MasqueClient> MasqueClient::Create(
    const std::string& uri_template, MasqueMode masque_mode,
    QuicEventLoop* event_loop, std::unique_ptr<ProofVerifier> proof_verifier) {
  QuicUrl url(uri_template);
  std::string host = url.host();
  uint16_t port = url.port();
  // Build the masque_client, and try to connect.
  QuicSocketAddress addr = tools::LookupAddress(host, absl::StrCat(port));
  if (!addr.IsInitialized()) {
    QUIC_LOG(ERROR) << "Unable to resolve address: " << host;
    return nullptr;
  }
  QuicServerId server_id(host, port);
  // Use absl::WrapUnique(new MasqueClient(...)) instead of
  // std::make_unique<MasqueClient>(...) because the constructor for
  // MasqueClient is private and therefore not accessible from make_unique.
  auto masque_client = absl::WrapUnique(
      new MasqueClient(addr, server_id, masque_mode, event_loop,
                       std::move(proof_verifier), uri_template));

  if (masque_client == nullptr) {
    QUIC_LOG(ERROR) << "Failed to create masque_client";
    return nullptr;
  }

  masque_client->set_initial_max_packet_length(kMasqueMaxOuterPacketSize);
  masque_client->set_drop_response_body(false);
  if (!masque_client->Initialize()) {
    QUIC_LOG(ERROR) << "Failed to initialize masque_client";
    return nullptr;
  }
  if (!masque_client->Connect()) {
    QuicErrorCode error = masque_client->session()->error();
    QUIC_LOG(ERROR) << "Failed to connect to " << host << ":" << port
                    << ". Error: " << QuicErrorCodeToString(error);
    return nullptr;
  }

  if (!masque_client->WaitUntilSettingsReceived()) {
    QUIC_LOG(ERROR) << "Failed to receive settings";
    return nullptr;
  }

  return masque_client;
}

void MasqueClient::OnSettingsReceived() { settings_received_ = true; }

bool MasqueClient::WaitUntilSettingsReceived() {
  while (connected() && !settings_received_) {
    network_helper()->RunEventLoop();
  }
  return connected() && settings_received_;
}

}  // namespace quic
