|  | // 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 "quic/masque/masque_epoll_client.h" | 
|  |  | 
|  | #include <string> | 
|  |  | 
|  | #include "absl/memory/memory.h" | 
|  | #include "quic/masque/masque_client_session.h" | 
|  | #include "quic/masque/masque_utils.h" | 
|  | #include "quic/tools/quic_url.h" | 
|  |  | 
|  | namespace quic { | 
|  |  | 
|  | MasqueEpollClient::MasqueEpollClient( | 
|  | QuicSocketAddress server_address, const QuicServerId& server_id, | 
|  | MasqueMode masque_mode, QuicEpollServer* epoll_server, | 
|  | std::unique_ptr<ProofVerifier> proof_verifier, | 
|  | const std::string& uri_template) | 
|  | : QuicClient(server_address, server_id, MasqueSupportedVersions(), | 
|  | epoll_server, std::move(proof_verifier)), | 
|  | masque_mode_(masque_mode), | 
|  | uri_template_(uri_template) {} | 
|  |  | 
|  | std::unique_ptr<QuicSession> MasqueEpollClient::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(), push_promise_index(), this); | 
|  | } | 
|  |  | 
|  | MasqueClientSession* MasqueEpollClient::masque_client_session() { | 
|  | return static_cast<MasqueClientSession*>(QuicClient::session()); | 
|  | } | 
|  |  | 
|  | QuicConnectionId MasqueEpollClient::connection_id() { | 
|  | return masque_client_session()->connection_id(); | 
|  | } | 
|  |  | 
|  | std::string MasqueEpollClient::authority() const { | 
|  | QuicUrl url(uri_template_); | 
|  | return absl::StrCat(url.host(), ":", url.port()); | 
|  | } | 
|  |  | 
|  | // static | 
|  | std::unique_ptr<MasqueEpollClient> MasqueEpollClient::Create( | 
|  | const std::string& uri_template, MasqueMode masque_mode, | 
|  | QuicEpollServer* epoll_server, | 
|  | 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 MasqueEpollClient(...)) instead of | 
|  | // std::make_unique<MasqueEpollClient>(...) because the constructor for | 
|  | // MasqueEpollClient is private and therefore not accessible from make_unique. | 
|  | auto masque_client = absl::WrapUnique( | 
|  | new MasqueEpollClient(addr, server_id, masque_mode, epoll_server, | 
|  | 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; | 
|  | } | 
|  |  | 
|  | if (masque_client->masque_mode() == MasqueMode::kLegacy) { | 
|  | // Construct the legacy mode init request. | 
|  | spdy::Http2HeaderBlock header_block; | 
|  | header_block[":method"] = "POST"; | 
|  | header_block[":scheme"] = "https"; | 
|  | header_block[":authority"] = masque_client->authority(); | 
|  | header_block[":path"] = "/.well-known/masque/init"; | 
|  | std::string body = "foo"; | 
|  |  | 
|  | // Make sure to store the response, for later output. | 
|  | masque_client->set_store_response(true); | 
|  |  | 
|  | // Send the MASQUE init command. | 
|  | masque_client->SendRequestAndWaitForResponse(header_block, body, | 
|  | /*fin=*/true); | 
|  |  | 
|  | if (!masque_client->connected()) { | 
|  | QUIC_LOG(ERROR) | 
|  | << "MASQUE init request caused connection failure. Error: " | 
|  | << QuicErrorCodeToString(masque_client->session()->error()); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | const int response_code = masque_client->latest_response_code(); | 
|  | if (response_code != 200) { | 
|  | QUIC_LOG(ERROR) << "MASQUE init request failed with HTTP response code " | 
|  | << response_code; | 
|  | return nullptr; | 
|  | } | 
|  | } | 
|  | return masque_client; | 
|  | } | 
|  |  | 
|  | void MasqueEpollClient::OnSettingsReceived() { | 
|  | settings_received_ = true; | 
|  | } | 
|  |  | 
|  | bool MasqueEpollClient::WaitUntilSettingsReceived() { | 
|  | while (connected() && !settings_received_) { | 
|  | network_helper()->RunEventLoop(); | 
|  | } | 
|  | return connected() && settings_received_; | 
|  | } | 
|  |  | 
|  | void MasqueEpollClient::UnregisterClientConnectionId( | 
|  | QuicConnectionId client_connection_id) { | 
|  | std::string body(client_connection_id.data(), client_connection_id.length()); | 
|  |  | 
|  | // Construct a GET or POST request for supplied URL. | 
|  | spdy::Http2HeaderBlock header_block; | 
|  | header_block[":method"] = "POST"; | 
|  | header_block[":scheme"] = "https"; | 
|  | header_block[":authority"] = authority(); | 
|  | header_block[":path"] = "/.well-known/masque/unregister"; | 
|  |  | 
|  | // Make sure to store the response, for later output. | 
|  | set_store_response(true); | 
|  |  | 
|  | // Send the MASQUE unregister command. | 
|  | SendRequest(header_block, body, /*fin=*/true); | 
|  | } | 
|  |  | 
|  | }  // namespace quic |