1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "quiche/quic/tools/quic_toy_server.h"
6
7 #include <string>
8 #include <utility>
9 #include <vector>
10
11 #include "absl/container/flat_hash_set.h"
12 #include "absl/strings/str_cat.h"
13 #include "absl/strings/str_split.h"
14 #include "absl/strings/string_view.h"
15 #include "quiche/quic/core/quic_server_id.h"
16 #include "quiche/quic/core/quic_versions.h"
17 #include "quiche/quic/platform/api/quic_default_proof_providers.h"
18 #include "quiche/quic/platform/api/quic_socket_address.h"
19 #include "quiche/quic/tools/connect_server_backend.h"
20 #include "quiche/quic/tools/quic_memory_cache_backend.h"
21 #include "quiche/common/platform/api/quiche_command_line_flags.h"
22 #include "quiche/common/platform/api/quiche_logging.h"
23 #include "quiche/common/quiche_random.h"
24
25 DEFINE_QUICHE_COMMAND_LINE_FLAG(int32_t, port, 6121,
26 "The port the quic server will listen on.");
27
28 DEFINE_QUICHE_COMMAND_LINE_FLAG(
29 std::string, quic_response_cache_dir, "",
30 "Specifies the directory used during QuicHttpResponseCache "
31 "construction to seed the cache. Cache directory can be "
32 "generated using `wget -p --save-headers <url>`");
33
34 DEFINE_QUICHE_COMMAND_LINE_FLAG(
35 bool, generate_dynamic_responses, false,
36 "If true, then URLs which have a numeric path will send a dynamically "
37 "generated response of that many bytes.");
38
39 DEFINE_QUICHE_COMMAND_LINE_FLAG(
40 std::string, quic_versions, "",
41 "QUIC versions to enable, e.g. \"h3-25,h3-27\". If not set, then all "
42 "available versions are enabled.");
43
44 DEFINE_QUICHE_COMMAND_LINE_FLAG(bool, enable_webtransport, false,
45 "If true, WebTransport support is enabled.");
46
47 DEFINE_QUICHE_COMMAND_LINE_FLAG(
48 std::string, connect_proxy_destinations, "",
49 "Specifies a comma-separated list of destinations (\"hostname:port\") to "
50 "which the QUIC server will allow tunneling via CONNECT.");
51
52 DEFINE_QUICHE_COMMAND_LINE_FLAG(
53 std::string, connect_udp_proxy_targets, "",
54 "Specifies a comma-separated list of target servers (\"hostname:port\") to "
55 "which the QUIC server will allow tunneling via CONNECT-UDP.");
56
57 DEFINE_QUICHE_COMMAND_LINE_FLAG(
58 std::string, proxy_server_label, "",
59 "Specifies an identifier to identify the server in proxy error headers, "
60 "per the requirements of RFC 9209, Section 2. It should uniquely identify "
61 "the running service between separate running instances of the QUIC toy "
62 "server binary. If not specified, one will be randomly generated as "
63 "\"QuicToyServerN\" where N is a random uint64_t.");
64
65 namespace quic {
66
67 std::unique_ptr<quic::QuicSimpleServerBackend>
CreateBackend()68 QuicToyServer::MemoryCacheBackendFactory::CreateBackend() {
69 auto memory_cache_backend = std::make_unique<QuicMemoryCacheBackend>();
70 if (quiche::GetQuicheCommandLineFlag(FLAGS_generate_dynamic_responses)) {
71 memory_cache_backend->GenerateDynamicResponses();
72 }
73 if (!quiche::GetQuicheCommandLineFlag(FLAGS_quic_response_cache_dir)
74 .empty()) {
75 memory_cache_backend->InitializeBackend(
76 quiche::GetQuicheCommandLineFlag(FLAGS_quic_response_cache_dir));
77 }
78 if (quiche::GetQuicheCommandLineFlag(FLAGS_enable_webtransport)) {
79 memory_cache_backend->EnableWebTransport();
80 }
81
82 if (!quiche::GetQuicheCommandLineFlag(FLAGS_connect_proxy_destinations)
83 .empty() ||
84 !quiche::GetQuicheCommandLineFlag(FLAGS_connect_udp_proxy_targets)
85 .empty()) {
86 absl::flat_hash_set<QuicServerId> connect_proxy_destinations;
87 for (absl::string_view destination : absl::StrSplit(
88 quiche::GetQuicheCommandLineFlag(FLAGS_connect_proxy_destinations),
89 ',', absl::SkipEmpty())) {
90 std::optional<QuicServerId> destination_server_id =
91 QuicServerId::ParseFromHostPortString(destination);
92 QUICHE_CHECK(destination_server_id.has_value());
93 connect_proxy_destinations.insert(
94 std::move(destination_server_id).value());
95 }
96
97 absl::flat_hash_set<QuicServerId> connect_udp_proxy_targets;
98 for (absl::string_view target : absl::StrSplit(
99 quiche::GetQuicheCommandLineFlag(FLAGS_connect_udp_proxy_targets),
100 ',', absl::SkipEmpty())) {
101 std::optional<QuicServerId> target_server_id =
102 QuicServerId::ParseFromHostPortString(target);
103 QUICHE_CHECK(target_server_id.has_value());
104 connect_udp_proxy_targets.insert(std::move(target_server_id).value());
105 }
106
107 QUICHE_CHECK(!connect_proxy_destinations.empty() ||
108 !connect_udp_proxy_targets.empty());
109
110 std::string proxy_server_label =
111 quiche::GetQuicheCommandLineFlag(FLAGS_proxy_server_label);
112 if (proxy_server_label.empty()) {
113 proxy_server_label = absl::StrCat(
114 "QuicToyServer",
115 quiche::QuicheRandom::GetInstance()->InsecureRandUint64());
116 }
117
118 return std::make_unique<ConnectServerBackend>(
119 std::move(memory_cache_backend), std::move(connect_proxy_destinations),
120 std::move(connect_udp_proxy_targets), std::move(proxy_server_label));
121 }
122
123 return memory_cache_backend;
124 }
125
QuicToyServer(BackendFactory * backend_factory,ServerFactory * server_factory)126 QuicToyServer::QuicToyServer(BackendFactory* backend_factory,
127 ServerFactory* server_factory)
128 : backend_factory_(backend_factory), server_factory_(server_factory) {}
129
Start()130 int QuicToyServer::Start() {
131 ParsedQuicVersionVector supported_versions = AllSupportedVersions();
132 std::string versions_string =
133 quiche::GetQuicheCommandLineFlag(FLAGS_quic_versions);
134 if (!versions_string.empty()) {
135 supported_versions = ParseQuicVersionVectorString(versions_string);
136 }
137 if (supported_versions.empty()) {
138 return 1;
139 }
140 for (const auto& version : supported_versions) {
141 QuicEnableVersion(version);
142 }
143 auto proof_source = quic::CreateDefaultProofSource();
144 auto backend = backend_factory_->CreateBackend();
145 auto server = server_factory_->CreateServer(
146 backend.get(), std::move(proof_source), supported_versions);
147
148 if (!server->CreateUDPSocketAndListen(quic::QuicSocketAddress(
149 quic::QuicIpAddress::Any6(),
150 quiche::GetQuicheCommandLineFlag(FLAGS_port)))) {
151 return 1;
152 }
153
154 server->HandleEventsForever();
155 return 0;
156 }
157
158 } // namespace quic
159