xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/tools/connect_server_backend.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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