|  | // Copyright (c) 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 <iostream> | 
|  | #include <memory> | 
|  | #include <string> | 
|  |  | 
|  | #include "net/third_party/quiche/src/quic/core/quic_versions.h" | 
|  | #include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h" | 
|  | #include "net/third_party/quiche/src/quic/platform/api/quic_system_event_loop.h" | 
|  | #include "net/third_party/quiche/src/quic/tools/fake_proof_verifier.h" | 
|  | #include "net/third_party/quiche/src/quic/tools/quic_client.h" | 
|  |  | 
|  | DEFINE_QUIC_COMMAND_LINE_FLAG(std::string, | 
|  | host, | 
|  | "", | 
|  | "The IP or hostname to connect to."); | 
|  |  | 
|  | DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t, port, 0, "The port to connect to."); | 
|  |  | 
|  | namespace quic { | 
|  |  | 
|  | enum class Feature { | 
|  | // A version negotiation response is elicited and acted on. | 
|  | kVersionNegotiation, | 
|  | // The handshake completes successfully. | 
|  | kHandshake, | 
|  | // Stream data is being exchanged and ACK'ed. | 
|  | kStreamData, | 
|  | // The connection close procedcure completes with a zero error code. | 
|  | kConnectionClose, | 
|  | // An H3 transaction succeeded. | 
|  | kHttp3, | 
|  | // TODO(nharper): Add Retry to list of tested features. | 
|  | }; | 
|  |  | 
|  | char MatrixLetter(Feature f) { | 
|  | switch (f) { | 
|  | case Feature::kVersionNegotiation: | 
|  | return 'V'; | 
|  | case Feature::kHandshake: | 
|  | return 'H'; | 
|  | case Feature::kStreamData: | 
|  | return 'D'; | 
|  | case Feature::kConnectionClose: | 
|  | return 'C'; | 
|  | case Feature::kHttp3: | 
|  | return '3'; | 
|  | } | 
|  | } | 
|  |  | 
|  | std::set<Feature> AttemptRequest(QuicSocketAddress addr, | 
|  | std::string authority, | 
|  | QuicServerId server_id, | 
|  | ParsedQuicVersionVector versions) { | 
|  | std::set<Feature> features; | 
|  | auto proof_verifier = std::make_unique<FakeProofVerifier>(); | 
|  | QuicEpollServer epoll_server; | 
|  | auto client = std::make_unique<QuicClient>( | 
|  | addr, server_id, versions, &epoll_server, std::move(proof_verifier)); | 
|  | if (!client->Initialize()) { | 
|  | return features; | 
|  | } | 
|  | if (!client->Connect()) { | 
|  | QuicErrorCode error = client->session()->error(); | 
|  | if (error == QUIC_INVALID_VERSION) { | 
|  | // QuicFramer::ProcessPacket returns RaiseError(QUIC_INVALID_VERSION) if | 
|  | // it receives a packet containing a version in the header that is not our | 
|  | // version. It might be possible that we didn't actually process a VN | 
|  | // packet here. | 
|  | features.insert(Feature::kVersionNegotiation); | 
|  | return features; | 
|  | } | 
|  | return features; | 
|  | } | 
|  | if (!client->session()->IsCryptoHandshakeConfirmed()) { | 
|  | return features; | 
|  | } | 
|  | features.insert(Feature::kHandshake); | 
|  |  | 
|  | // Construct and send a request. | 
|  | spdy::SpdyHeaderBlock header_block; | 
|  | header_block[":method"] = "GET"; | 
|  | header_block[":scheme"] = "https"; | 
|  | header_block[":authority"] = authority; | 
|  | header_block[":path"] = "/"; | 
|  | client->set_store_response(true); | 
|  | client->SendRequest(header_block, "", /*fin=*/true); | 
|  |  | 
|  | // TODO(nharper): After some period of time, time out and don't report | 
|  | // success. | 
|  | while (client->WaitForEvents()) { | 
|  | } | 
|  |  | 
|  | if (!client->connected()) { | 
|  | return features; | 
|  | } | 
|  |  | 
|  | if (client->latest_response_code() != -1) { | 
|  | features.insert(Feature::kHttp3); | 
|  | } | 
|  |  | 
|  | // TODO(nharper): Properly check that we actually sent stream data and | 
|  | // received ACKs for it. | 
|  | features.insert(Feature::kStreamData); | 
|  | // TODO(nharper): Check that we sent/received (which one?) a CONNECTION_CLOSE | 
|  | // with error code 0. | 
|  | features.insert(Feature::kConnectionClose); | 
|  | return features; | 
|  | } | 
|  |  | 
|  | std::set<Feature> ServerSupport(std::string host, int port) { | 
|  | // Configure version list. | 
|  | QuicVersionInitializeSupportForIetfDraft(); | 
|  | ParsedQuicVersion version = | 
|  | ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_99); | 
|  | ParsedQuicVersionVector versions = {version}; | 
|  | QuicEnableVersion(version); | 
|  |  | 
|  | // Build the client, and try to connect. | 
|  | QuicSocketAddress addr = tools::LookupAddress(host, QuicStrCat(port)); | 
|  | QuicServerId server_id(host, port, false); | 
|  | std::string authority = QuicStrCat(host, ":", port); | 
|  |  | 
|  | ParsedQuicVersionVector versions_with_negotiation = versions; | 
|  | versions_with_negotiation.insert(versions_with_negotiation.begin(), | 
|  | QuicVersionReservedForNegotiation()); | 
|  | auto supported_features = | 
|  | AttemptRequest(addr, authority, server_id, versions_with_negotiation); | 
|  | if (!supported_features.empty()) { | 
|  | supported_features.insert(Feature::kVersionNegotiation); | 
|  | } else { | 
|  | supported_features = AttemptRequest(addr, authority, server_id, versions); | 
|  | } | 
|  | return supported_features; | 
|  | } | 
|  |  | 
|  | }  // namespace quic | 
|  |  | 
|  | int main(int argc, char* argv[]) { | 
|  | QuicSystemEventLoop event_loop("quic_client"); | 
|  | const char* usage = "Usage: quic_client_interop_test [options]"; | 
|  |  | 
|  | std::vector<std::string> args = | 
|  | quic::QuicParseCommandLineFlags(usage, argc, argv); | 
|  | if (!args.empty()) { | 
|  | quic::QuicPrintCommandLineFlagHelp(usage); | 
|  | exit(1); | 
|  | } | 
|  | std::string host = GetQuicFlag(FLAGS_host); | 
|  | int port = GetQuicFlag(FLAGS_port); | 
|  | if (host.empty() || port == 0) { | 
|  | quic::QuicPrintCommandLineFlagHelp(usage); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | auto supported_features = quic::ServerSupport(host, port); | 
|  | std::cout << "Supported features: "; | 
|  | for (auto feature : supported_features) { | 
|  | std::cout << MatrixLetter(feature); | 
|  | } | 
|  | std::cout << std::endl; | 
|  | } |