|  | // Copyright (c) 2015 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. | 
|  |  | 
|  | // A base class for the toy client, which connects to a specified port and sends | 
|  | // QUIC request to that endpoint. | 
|  |  | 
|  | #ifndef QUICHE_QUIC_TOOLS_QUIC_CLIENT_BASE_H_ | 
|  | #define QUICHE_QUIC_TOOLS_QUIC_CLIENT_BASE_H_ | 
|  |  | 
|  | #include <memory> | 
|  | #include <string> | 
|  |  | 
|  | #include "absl/base/attributes.h" | 
|  | #include "absl/strings/string_view.h" | 
|  | #include "quic/core/crypto/crypto_handshake.h" | 
|  | #include "quic/core/http/quic_client_push_promise_index.h" | 
|  | #include "quic/core/http/quic_spdy_client_session.h" | 
|  | #include "quic/core/http/quic_spdy_client_stream.h" | 
|  | #include "quic/core/quic_config.h" | 
|  | #include "quic/platform/api/quic_socket_address.h" | 
|  |  | 
|  | namespace quic { | 
|  |  | 
|  | class ProofVerifier; | 
|  | class QuicServerId; | 
|  | class SessionCache; | 
|  |  | 
|  | // QuicClientBase handles establishing a connection to the passed in | 
|  | // server id, including ensuring that it supports the passed in versions | 
|  | // and config. | 
|  | // Subclasses derived from this class are responsible for creating the | 
|  | // actual QuicSession instance, as well as defining functions that | 
|  | // create and run the underlying network transport. | 
|  | class QuicClientBase { | 
|  | public: | 
|  | // An interface to various network events that the QuicClient will need to | 
|  | // interact with. | 
|  | class NetworkHelper { | 
|  | public: | 
|  | virtual ~NetworkHelper(); | 
|  |  | 
|  | // Runs one iteration of the event loop. | 
|  | virtual void RunEventLoop() = 0; | 
|  |  | 
|  | // Used during initialization: creates the UDP socket FD, sets socket | 
|  | // options, and binds the socket to our address. | 
|  | virtual bool CreateUDPSocketAndBind(QuicSocketAddress server_address, | 
|  | QuicIpAddress bind_to_address, | 
|  | int bind_to_port) = 0; | 
|  |  | 
|  | // Unregister and close all open UDP sockets. | 
|  | virtual void CleanUpAllUDPSockets() = 0; | 
|  |  | 
|  | // If the client has at least one UDP socket, return address of the latest | 
|  | // created one. Otherwise, return an empty socket address. | 
|  | virtual QuicSocketAddress GetLatestClientAddress() const = 0; | 
|  |  | 
|  | // Creates a packet writer to be used for the next connection. | 
|  | virtual QuicPacketWriter* CreateQuicPacketWriter() = 0; | 
|  | }; | 
|  |  | 
|  | QuicClientBase(const QuicServerId& server_id, | 
|  | const ParsedQuicVersionVector& supported_versions, | 
|  | const QuicConfig& config, | 
|  | QuicConnectionHelperInterface* helper, | 
|  | QuicAlarmFactory* alarm_factory, | 
|  | std::unique_ptr<NetworkHelper> network_helper, | 
|  | std::unique_ptr<ProofVerifier> proof_verifier, | 
|  | std::unique_ptr<SessionCache> session_cache); | 
|  | QuicClientBase(const QuicClientBase&) = delete; | 
|  | QuicClientBase& operator=(const QuicClientBase&) = delete; | 
|  |  | 
|  | virtual ~QuicClientBase(); | 
|  |  | 
|  | // Initializes the client to create a connection. Should be called exactly | 
|  | // once before calling StartConnect or Connect. Returns true if the | 
|  | // initialization succeeds, false otherwise. | 
|  | virtual bool Initialize(); | 
|  |  | 
|  | // "Connect" to the QUIC server, including performing synchronous crypto | 
|  | // handshake. | 
|  | bool Connect(); | 
|  |  | 
|  | // Start the crypto handshake.  This can be done in place of the synchronous | 
|  | // Connect(), but callers are responsible for making sure the crypto handshake | 
|  | // completes. | 
|  | void StartConnect(); | 
|  |  | 
|  | // Calls session()->Initialize(). Subclasses may override this if any extra | 
|  | // initialization needs to be done. Subclasses should expect that session() | 
|  | // is non-null and valid. | 
|  | virtual void InitializeSession(); | 
|  |  | 
|  | // Disconnects from the QUIC server. | 
|  | void Disconnect(); | 
|  |  | 
|  | // Returns true if the crypto handshake has yet to establish encryption. | 
|  | // Returns false if encryption is active (even if the server hasn't confirmed | 
|  | // the handshake) or if the connection has been closed. | 
|  | bool EncryptionBeingEstablished(); | 
|  |  | 
|  | // Wait for events until the stream with the given ID is closed. | 
|  | void WaitForStreamToClose(QuicStreamId id); | 
|  |  | 
|  | // Wait for 1-RTT keys become available. | 
|  | // Returns true once 1-RTT keys are available, false otherwise. | 
|  | ABSL_MUST_USE_RESULT bool WaitForOneRttKeysAvailable(); | 
|  |  | 
|  | // Wait for handshake state proceeds to HANDSHAKE_CONFIRMED. | 
|  | // In QUIC crypto, this does the same as WaitForOneRttKeysAvailable, while in | 
|  | // TLS, this waits for HANDSHAKE_DONE frame is received. | 
|  | ABSL_MUST_USE_RESULT bool WaitForHandshakeConfirmed(); | 
|  |  | 
|  | // Wait up to 50ms, and handle any events which occur. | 
|  | // Returns true if there are any outstanding requests. | 
|  | bool WaitForEvents(); | 
|  |  | 
|  | // Migrate to a new socket (new_host) during an active connection. | 
|  | bool MigrateSocket(const QuicIpAddress& new_host); | 
|  |  | 
|  | // Migrate to a new socket (new_host, port) during an active connection. | 
|  | bool MigrateSocketWithSpecifiedPort(const QuicIpAddress& new_host, int port); | 
|  |  | 
|  | // Validate the new socket and migrate to it if the validation succeeds. | 
|  | // Otherwise stay on the current socket. Return true if the validation has | 
|  | // started. | 
|  | bool ValidateAndMigrateSocket(const QuicIpAddress& new_host); | 
|  |  | 
|  | // Open a new socket to change to a new ephemeral port. | 
|  | bool ChangeEphemeralPort(); | 
|  |  | 
|  | QuicSession* session(); | 
|  | const QuicSession* session() const; | 
|  |  | 
|  | bool connected() const; | 
|  | virtual bool goaway_received() const; | 
|  |  | 
|  | const QuicServerId& server_id() const { return server_id_; } | 
|  |  | 
|  | // This should only be set before the initial Connect() | 
|  | void set_server_id(const QuicServerId& server_id) { server_id_ = server_id; } | 
|  |  | 
|  | void SetUserAgentID(const std::string& user_agent_id) { | 
|  | crypto_config_.set_user_agent_id(user_agent_id); | 
|  | } | 
|  |  | 
|  | void SetTlsSignatureAlgorithms(std::string signature_algorithms) { | 
|  | crypto_config_.set_tls_signature_algorithms( | 
|  | std::move(signature_algorithms)); | 
|  | } | 
|  |  | 
|  | const ParsedQuicVersionVector& supported_versions() const { | 
|  | return supported_versions_; | 
|  | } | 
|  |  | 
|  | void SetSupportedVersions(const ParsedQuicVersionVector& versions) { | 
|  | supported_versions_ = versions; | 
|  | } | 
|  |  | 
|  | QuicConfig* config() { return &config_; } | 
|  |  | 
|  | QuicCryptoClientConfig* crypto_config() { return &crypto_config_; } | 
|  |  | 
|  | // Change the initial maximum packet size of the connection.  Has to be called | 
|  | // before Connect()/StartConnect() in order to have any effect. | 
|  | void set_initial_max_packet_length(QuicByteCount initial_max_packet_length) { | 
|  | initial_max_packet_length_ = initial_max_packet_length; | 
|  | } | 
|  |  | 
|  | // The number of client hellos sent. | 
|  | int GetNumSentClientHellos(); | 
|  |  | 
|  | // Returns true if early data (0-RTT data) was sent and the server accepted | 
|  | // it. | 
|  | virtual bool EarlyDataAccepted() = 0; | 
|  |  | 
|  | // Returns true if the handshake was delayed one round trip by the server | 
|  | // because the server wanted proof the client controls its source address | 
|  | // before progressing further. In Google QUIC, this would be due to an | 
|  | // inchoate REJ in the QUIC Crypto handshake; in IETF QUIC this would be due | 
|  | // to a Retry packet. | 
|  | // TODO(nharper): Consider a better name for this method. | 
|  | virtual bool ReceivedInchoateReject() = 0; | 
|  |  | 
|  | // Gather the stats for the last session and update the stats for the overall | 
|  | // connection. | 
|  | void UpdateStats(); | 
|  |  | 
|  | // The number of server config updates received. | 
|  | int GetNumReceivedServerConfigUpdates(); | 
|  |  | 
|  | // Returns any errors that occurred at the connection-level. | 
|  | QuicErrorCode connection_error() const; | 
|  | void set_connection_error(QuicErrorCode connection_error) { | 
|  | connection_error_ = connection_error; | 
|  | } | 
|  |  | 
|  | bool connected_or_attempting_connect() const { | 
|  | return connected_or_attempting_connect_; | 
|  | } | 
|  | void set_connected_or_attempting_connect( | 
|  | bool connected_or_attempting_connect) { | 
|  | connected_or_attempting_connect_ = connected_or_attempting_connect; | 
|  | } | 
|  |  | 
|  | QuicPacketWriter* writer() { return writer_.get(); } | 
|  | void set_writer(QuicPacketWriter* writer) { | 
|  | if (writer_.get() != writer) { | 
|  | writer_.reset(writer); | 
|  | } | 
|  | } | 
|  |  | 
|  | void reset_writer() { writer_.reset(); } | 
|  |  | 
|  | ProofVerifier* proof_verifier() const; | 
|  |  | 
|  | void set_bind_to_address(QuicIpAddress address) { | 
|  | bind_to_address_ = address; | 
|  | } | 
|  |  | 
|  | QuicIpAddress bind_to_address() const { return bind_to_address_; } | 
|  |  | 
|  | void set_local_port(int local_port) { local_port_ = local_port; } | 
|  |  | 
|  | int local_port() const { return local_port_; } | 
|  |  | 
|  | const QuicSocketAddress& server_address() const { return server_address_; } | 
|  |  | 
|  | void set_server_address(const QuicSocketAddress& server_address) { | 
|  | server_address_ = server_address; | 
|  | } | 
|  |  | 
|  | QuicConnectionHelperInterface* helper() { return helper_.get(); } | 
|  |  | 
|  | NetworkHelper* network_helper(); | 
|  | const NetworkHelper* network_helper() const; | 
|  |  | 
|  | bool initialized() const { return initialized_; } | 
|  |  | 
|  | void SetPreSharedKey(absl::string_view key) { | 
|  | crypto_config_.set_pre_shared_key(key); | 
|  | } | 
|  |  | 
|  | void set_connection_debug_visitor( | 
|  | QuicConnectionDebugVisitor* connection_debug_visitor) { | 
|  | connection_debug_visitor_ = connection_debug_visitor; | 
|  | } | 
|  |  | 
|  | void set_server_connection_id_length(uint8_t server_connection_id_length) { | 
|  | server_connection_id_length_ = server_connection_id_length; | 
|  | } | 
|  |  | 
|  | void set_client_connection_id_length(uint8_t client_connection_id_length) { | 
|  | client_connection_id_length_ = client_connection_id_length; | 
|  | } | 
|  |  | 
|  | bool HasPendingPathValidation(); | 
|  |  | 
|  | void ValidateNewNetwork(const QuicIpAddress& host); | 
|  |  | 
|  | void AddValidatedPath(std::unique_ptr<QuicPathValidationContext> context) { | 
|  | validated_paths_.push_back(std::move(context)); | 
|  | } | 
|  |  | 
|  | const std::vector<std::unique_ptr<QuicPathValidationContext>>& | 
|  | validated_paths() const { | 
|  | return validated_paths_; | 
|  | } | 
|  |  | 
|  | protected: | 
|  | // TODO(rch): Move GetNumSentClientHellosFromSession and | 
|  | // GetNumReceivedServerConfigUpdatesFromSession into a new/better | 
|  | // QuicSpdyClientSession class. The current inherits dependencies from | 
|  | // Spdy. When that happens this class and all its subclasses should | 
|  | // work with QuicSpdyClientSession instead of QuicSession. | 
|  | // That will obviate the need for the pure virtual functions below. | 
|  |  | 
|  | // Extract the number of sent client hellos from the session. | 
|  | virtual int GetNumSentClientHellosFromSession() = 0; | 
|  |  | 
|  | // The number of server config updates received. | 
|  | virtual int GetNumReceivedServerConfigUpdatesFromSession() = 0; | 
|  |  | 
|  | // If this client supports buffering data, resend it. | 
|  | virtual void ResendSavedData() = 0; | 
|  |  | 
|  | // If this client supports buffering data, clear it. | 
|  | virtual void ClearDataToResend() = 0; | 
|  |  | 
|  | // Takes ownership of |connection|. If you override this function, | 
|  | // you probably want to call ResetSession() in your destructor. | 
|  | // TODO(rch): Change the connection parameter to take in a | 
|  | // std::unique_ptr<QuicConnection> instead. | 
|  | virtual std::unique_ptr<QuicSession> CreateQuicClientSession( | 
|  | const ParsedQuicVersionVector& supported_versions, | 
|  | QuicConnection* connection) = 0; | 
|  |  | 
|  | // Generates the next ConnectionId for |server_id_|.  By default, if the | 
|  | // cached server config contains a server-designated ID, that ID will be | 
|  | // returned.  Otherwise, the next random ID will be returned. | 
|  | QuicConnectionId GetNextConnectionId(); | 
|  |  | 
|  | // Generates a new, random connection ID (as opposed to a server-designated | 
|  | // connection ID). | 
|  | virtual QuicConnectionId GenerateNewConnectionId(); | 
|  |  | 
|  | // Returns the client connection ID to use. | 
|  | virtual QuicConnectionId GetClientConnectionId(); | 
|  |  | 
|  | QuicAlarmFactory* alarm_factory() { return alarm_factory_.get(); } | 
|  |  | 
|  | // Subclasses may need to explicitly clear the session on destruction | 
|  | // if they create it with objects that will be destroyed before this is. | 
|  | // You probably want to call this if you override CreateQuicSpdyClientSession. | 
|  | void ResetSession() { session_.reset(); } | 
|  |  | 
|  | // Returns true if the corresponding of this client has active requests. | 
|  | virtual bool HasActiveRequests() = 0; | 
|  |  | 
|  | private: | 
|  | // Returns true and set |version| if client can reconnect with a different | 
|  | // version. | 
|  | bool CanReconnectWithDifferentVersion(ParsedQuicVersion* version) const; | 
|  |  | 
|  | std::unique_ptr<QuicPacketWriter> CreateWriterForNewNetwork( | 
|  | const QuicIpAddress& new_host, | 
|  | int port); | 
|  |  | 
|  | // |server_id_| is a tuple (hostname, port, is_https) of the server. | 
|  | QuicServerId server_id_; | 
|  |  | 
|  | // Tracks if the client is initialized to connect. | 
|  | bool initialized_; | 
|  |  | 
|  | // Address of the server. | 
|  | QuicSocketAddress server_address_; | 
|  |  | 
|  | // If initialized, the address to bind to. | 
|  | QuicIpAddress bind_to_address_; | 
|  |  | 
|  | // Local port to bind to. Initialize to 0. | 
|  | int local_port_; | 
|  |  | 
|  | // config_ and crypto_config_ contain configuration and cached state about | 
|  | // servers. | 
|  | QuicConfig config_; | 
|  | QuicCryptoClientConfig crypto_config_; | 
|  |  | 
|  | // Helper to be used by created connections. Must outlive |session_|. | 
|  | std::unique_ptr<QuicConnectionHelperInterface> helper_; | 
|  |  | 
|  | // Alarm factory to be used by created connections. Must outlive |session_|. | 
|  | std::unique_ptr<QuicAlarmFactory> alarm_factory_; | 
|  |  | 
|  | // Writer used to actually send packets to the wire. Must outlive |session_|. | 
|  | std::unique_ptr<QuicPacketWriter> writer_; | 
|  |  | 
|  | // Session which manages streams. | 
|  | std::unique_ptr<QuicSession> session_; | 
|  |  | 
|  | // This vector contains QUIC versions which we currently support. | 
|  | // This should be ordered such that the highest supported version is the first | 
|  | // element, with subsequent elements in descending order (versions can be | 
|  | // skipped as necessary). We will always pick supported_versions_[0] as the | 
|  | // initial version to use. | 
|  | ParsedQuicVersionVector supported_versions_; | 
|  |  | 
|  | // The initial value of maximum packet size of the connection.  If set to | 
|  | // zero, the default is used. | 
|  | QuicByteCount initial_max_packet_length_; | 
|  |  | 
|  | // The number of hellos sent during the current/latest connection. | 
|  | int num_sent_client_hellos_; | 
|  |  | 
|  | // Used to store any errors that occurred with the overall connection (as | 
|  | // opposed to that associated with the last session object). | 
|  | QuicErrorCode connection_error_; | 
|  |  | 
|  | // True when the client is attempting to connect.  Set to false between a call | 
|  | // to Disconnect() and the subsequent call to StartConnect().  When | 
|  | // connected_or_attempting_connect_ is false, the session object corresponds | 
|  | // to the previous client-level connection. | 
|  | bool connected_or_attempting_connect_; | 
|  |  | 
|  | // The network helper used to create sockets and manage the event loop. | 
|  | // Not owned by this class. | 
|  | std::unique_ptr<NetworkHelper> network_helper_; | 
|  |  | 
|  | // The debug visitor set on the connection right after it is constructed. | 
|  | // Not owned, must be valid for the lifetime of the QuicClientBase instance. | 
|  | QuicConnectionDebugVisitor* connection_debug_visitor_; | 
|  |  | 
|  | // GenerateNewConnectionId creates a random connection ID of this length. | 
|  | // Defaults to 8. | 
|  | uint8_t server_connection_id_length_; | 
|  |  | 
|  | // GetClientConnectionId creates a random connection ID of this length. | 
|  | // Defaults to 0. | 
|  | uint8_t client_connection_id_length_; | 
|  |  | 
|  | // Stores validated paths. | 
|  | std::vector<std::unique_ptr<QuicPathValidationContext>> validated_paths_; | 
|  | }; | 
|  |  | 
|  | }  // namespace quic | 
|  |  | 
|  | #endif  // QUICHE_QUIC_TOOLS_QUIC_CLIENT_BASE_H_ |