1 // Copyright 2022 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/connect_server_backend.h"
6
7 #include <memory>
8 #include <string>
9 #include <utility>
10
11 #include "absl/container/flat_hash_set.h"
12 #include "absl/strings/string_view.h"
13 #include "quiche/quic/core/quic_server_id.h"
14 #include "quiche/quic/core/socket_factory.h"
15 #include "quiche/quic/tools/connect_tunnel.h"
16 #include "quiche/quic/tools/connect_udp_tunnel.h"
17 #include "quiche/quic/tools/quic_simple_server_backend.h"
18 #include "quiche/common/platform/api/quiche_bug_tracker.h"
19 #include "quiche/common/platform/api/quiche_logging.h"
20 #include "quiche/spdy/core/http2_header_block.h"
21
22 namespace quic {
23
24 namespace {
25
SendErrorResponse(QuicSimpleServerBackend::RequestHandler * request_handler,absl::string_view error_code)26 void SendErrorResponse(QuicSimpleServerBackend::RequestHandler* request_handler,
27 absl::string_view error_code) {
28 spdy::Http2HeaderBlock headers;
29 headers[":status"] = error_code;
30 QuicBackendResponse response;
31 response.set_headers(std::move(headers));
32 request_handler->OnResponseBackendComplete(&response);
33 }
34
35 } // namespace
36
ConnectServerBackend(std::unique_ptr<QuicSimpleServerBackend> non_connect_backend,absl::flat_hash_set<QuicServerId> acceptable_connect_destinations,absl::flat_hash_set<QuicServerId> acceptable_connect_udp_targets,std::string server_label)37 ConnectServerBackend::ConnectServerBackend(
38 std::unique_ptr<QuicSimpleServerBackend> non_connect_backend,
39 absl::flat_hash_set<QuicServerId> acceptable_connect_destinations,
40 absl::flat_hash_set<QuicServerId> acceptable_connect_udp_targets,
41 std::string server_label)
42 : non_connect_backend_(std::move(non_connect_backend)),
43 acceptable_connect_destinations_(
44 std::move(acceptable_connect_destinations)),
45 acceptable_connect_udp_targets_(
46 std::move(acceptable_connect_udp_targets)),
47 server_label_(std::move(server_label)) {
48 QUICHE_DCHECK(non_connect_backend_);
49 QUICHE_DCHECK(!server_label_.empty());
50 }
51
~ConnectServerBackend()52 ConnectServerBackend::~ConnectServerBackend() {
53 // Expect all streams to be closed before destroying backend.
54 QUICHE_DCHECK(connect_tunnels_.empty());
55 QUICHE_DCHECK(connect_udp_tunnels_.empty());
56 }
57
InitializeBackend(const std::string &)58 bool ConnectServerBackend::InitializeBackend(const std::string&) {
59 return true;
60 }
61
IsBackendInitialized() const62 bool ConnectServerBackend::IsBackendInitialized() const { return true; }
63
SetSocketFactory(SocketFactory * socket_factory)64 void ConnectServerBackend::SetSocketFactory(SocketFactory* socket_factory) {
65 QUICHE_DCHECK(socket_factory);
66 QUICHE_DCHECK(connect_tunnels_.empty());
67 QUICHE_DCHECK(connect_udp_tunnels_.empty());
68 socket_factory_ = socket_factory;
69 }
70
FetchResponseFromBackend(const spdy::Http2HeaderBlock & request_headers,const std::string & request_body,RequestHandler * request_handler)71 void ConnectServerBackend::FetchResponseFromBackend(
72 const spdy::Http2HeaderBlock& request_headers,
73 const std::string& request_body, RequestHandler* request_handler) {
74 // Not a CONNECT request, so send to `non_connect_backend_`.
75 non_connect_backend_->FetchResponseFromBackend(request_headers, request_body,
76 request_handler);
77 }
78
HandleConnectHeaders(const spdy::Http2HeaderBlock & request_headers,RequestHandler * request_handler)79 void ConnectServerBackend::HandleConnectHeaders(
80 const spdy::Http2HeaderBlock& request_headers,
81 RequestHandler* request_handler) {
82 QUICHE_DCHECK(request_headers.contains(":method") &&
83 request_headers.find(":method")->second == "CONNECT");
84
85 if (!socket_factory_) {
86 QUICHE_BUG(connect_server_backend_no_socket_factory)
87 << "Must set socket factory before ConnectServerBackend receives "
88 "requests.";
89 SendErrorResponse(request_handler, "500");
90 return;
91 }
92
93 if (!request_headers.contains(":protocol")) {
94 // normal CONNECT
95 auto [tunnel_it, inserted] = connect_tunnels_.emplace(
96 std::make_pair(request_handler->connection_id(),
97 request_handler->stream_id()),
98 std::make_unique<ConnectTunnel>(request_handler, socket_factory_,
99 acceptable_connect_destinations_));
100 QUICHE_DCHECK(inserted);
101
102 tunnel_it->second->OpenTunnel(request_headers);
103 } else if (request_headers.find(":protocol")->second == "connect-udp") {
104 // CONNECT-UDP
105 auto [tunnel_it, inserted] = connect_udp_tunnels_.emplace(
106 std::make_pair(request_handler->connection_id(),
107 request_handler->stream_id()),
108 std::make_unique<ConnectUdpTunnel>(request_handler, socket_factory_,
109 server_label_,
110 acceptable_connect_udp_targets_));
111 QUICHE_DCHECK(inserted);
112
113 tunnel_it->second->OpenTunnel(request_headers);
114 } else {
115 // Not a supported request.
116 non_connect_backend_->HandleConnectHeaders(request_headers,
117 request_handler);
118 }
119 }
120
HandleConnectData(absl::string_view data,bool data_complete,RequestHandler * request_handler)121 void ConnectServerBackend::HandleConnectData(absl::string_view data,
122 bool data_complete,
123 RequestHandler* request_handler) {
124 // Expect ConnectUdpTunnels to register a datagram visitor, causing the
125 // stream to process data as capsules. HandleConnectData() should therefore
126 // never be called for streams with a ConnectUdpTunnel.
127 QUICHE_DCHECK(!connect_udp_tunnels_.contains(std::make_pair(
128 request_handler->connection_id(), request_handler->stream_id())));
129
130 auto tunnel_it = connect_tunnels_.find(std::make_pair(
131 request_handler->connection_id(), request_handler->stream_id()));
132 if (tunnel_it == connect_tunnels_.end()) {
133 // If tunnel not found, perhaps it's something being handled for
134 // non-CONNECT. Possible because this method could be called for anything
135 // with a ":method":"CONNECT" header, but this class does not handle such
136 // requests if they have a ":protocol" header.
137 non_connect_backend_->HandleConnectData(data, data_complete,
138 request_handler);
139 return;
140 }
141
142 if (!data.empty()) {
143 tunnel_it->second->SendDataToDestination(data);
144 }
145 if (data_complete) {
146 tunnel_it->second->OnClientStreamClose();
147 connect_tunnels_.erase(tunnel_it);
148 }
149 }
150
CloseBackendResponseStream(QuicSimpleServerBackend::RequestHandler * request_handler)151 void ConnectServerBackend::CloseBackendResponseStream(
152 QuicSimpleServerBackend::RequestHandler* request_handler) {
153 auto tunnel_it = connect_tunnels_.find(std::make_pair(
154 request_handler->connection_id(), request_handler->stream_id()));
155 if (tunnel_it != connect_tunnels_.end()) {
156 tunnel_it->second->OnClientStreamClose();
157 connect_tunnels_.erase(tunnel_it);
158 }
159
160 auto udp_tunnel_it = connect_udp_tunnels_.find(std::pair(
161 request_handler->connection_id(), request_handler->stream_id()));
162 if (udp_tunnel_it != connect_udp_tunnels_.end()) {
163 udp_tunnel_it->second->OnClientStreamClose();
164 connect_udp_tunnels_.erase(udp_tunnel_it);
165 }
166
167 non_connect_backend_->CloseBackendResponseStream(request_handler);
168 }
169
170 } // namespace quic
171