xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/test_tools/quic_test_backend.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright (c) 2021 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/test_tools/quic_test_backend.h"
6 
7 #include <cstring>
8 #include <memory>
9 
10 #include "absl/strings/str_cat.h"
11 #include "absl/strings/str_split.h"
12 #include "absl/strings/string_view.h"
13 #include "quiche/quic/core/web_transport_interface.h"
14 #include "quiche/quic/test_tools/web_transport_resets_backend.h"
15 #include "quiche/quic/tools/web_transport_test_visitors.h"
16 #include "quiche/common/platform/api/quiche_googleurl.h"
17 
18 namespace quic {
19 namespace test {
20 
21 namespace {
22 
23 // SessionCloseVisitor implements the "/session-close" endpoint.  If the client
24 // sends a unidirectional stream of format "code message" to this endpoint, it
25 // will close the session with the corresponding error code and error message.
26 // For instance, sending "42 test error" will cause it to be closed with code 42
27 // and message "test error".  As a special case, sending "DRAIN" would result in
28 // a DRAIN_WEBTRANSPORT_SESSION capsule being sent.
29 class SessionCloseVisitor : public WebTransportVisitor {
30  public:
SessionCloseVisitor(WebTransportSession * session)31   SessionCloseVisitor(WebTransportSession* session) : session_(session) {}
32 
OnSessionReady()33   void OnSessionReady() override {}
OnSessionClosed(WebTransportSessionError,const std::string &)34   void OnSessionClosed(WebTransportSessionError /*error_code*/,
35                        const std::string& /*error_message*/) override {}
36 
OnIncomingBidirectionalStreamAvailable()37   void OnIncomingBidirectionalStreamAvailable() override {}
OnIncomingUnidirectionalStreamAvailable()38   void OnIncomingUnidirectionalStreamAvailable() override {
39     WebTransportStream* stream = session_->AcceptIncomingUnidirectionalStream();
40     if (stream == nullptr) {
41       return;
42     }
43     stream->SetVisitor(
44         std::make_unique<WebTransportUnidirectionalEchoReadVisitor>(
45             stream, [this](const std::string& data) {
46               if (data == "DRAIN") {
47                 session_->NotifySessionDraining();
48                 return;
49               }
50               std::pair<absl::string_view, absl::string_view> parsed =
51                   absl::StrSplit(data, absl::MaxSplits(' ', 1));
52               WebTransportSessionError error_code = 0;
53               bool success = absl::SimpleAtoi(parsed.first, &error_code);
54               QUICHE_DCHECK(success) << data;
55               session_->CloseSession(error_code, parsed.second);
56             }));
57     stream->visitor()->OnCanRead();
58   }
59 
OnDatagramReceived(absl::string_view)60   void OnDatagramReceived(absl::string_view /*datagram*/) override {}
61 
OnCanCreateNewOutgoingBidirectionalStream()62   void OnCanCreateNewOutgoingBidirectionalStream() override {}
OnCanCreateNewOutgoingUnidirectionalStream()63   void OnCanCreateNewOutgoingUnidirectionalStream() override {}
64 
65  private:
66   WebTransportSession* session_;  // Not owned.
67 };
68 
69 }  // namespace
70 
71 QuicSimpleServerBackend::WebTransportResponse
ProcessWebTransportRequest(const spdy::Http2HeaderBlock & request_headers,WebTransportSession * session)72 QuicTestBackend::ProcessWebTransportRequest(
73     const spdy::Http2HeaderBlock& request_headers,
74     WebTransportSession* session) {
75   if (!SupportsWebTransport()) {
76     return QuicSimpleServerBackend::ProcessWebTransportRequest(request_headers,
77                                                                session);
78   }
79 
80   auto path_it = request_headers.find(":path");
81   if (path_it == request_headers.end()) {
82     WebTransportResponse response;
83     response.response_headers[":status"] = "400";
84     return response;
85   }
86   absl::string_view path = path_it->second;
87   // Match any "/echo.*" pass, e.g. "/echo_foobar"
88   if (absl::StartsWith(path, "/echo")) {
89     WebTransportResponse response;
90     response.response_headers[":status"] = "200";
91     // Add response headers if the paramer has "set-header=XXX:YYY" query.
92     GURL url = GURL(absl::StrCat("https://localhost", path));
93     const std::vector<std::string>& params = absl::StrSplit(url.query(), '&');
94     for (const auto& param : params) {
95       absl::string_view param_view = param;
96       if (absl::ConsumePrefix(&param_view, "set-header=")) {
97         const std::vector<absl::string_view> header_value =
98             absl::StrSplit(param_view, ':');
99         if (header_value.size() == 2 &&
100             !absl::StartsWith(header_value[0], ":")) {
101           response.response_headers[header_value[0]] = header_value[1];
102         }
103       }
104     }
105 
106     response.visitor =
107         std::make_unique<EchoWebTransportSessionVisitor>(session);
108     return response;
109   }
110   if (path == "/resets") {
111     return WebTransportResetsBackend(request_headers, session);
112   }
113   if (path == "/session-close") {
114     WebTransportResponse response;
115     response.response_headers[":status"] = "200";
116     response.visitor = std::make_unique<SessionCloseVisitor>(session);
117     return response;
118   }
119 
120   WebTransportResponse response;
121   response.response_headers[":status"] = "404";
122   return response;
123 }
124 
125 }  // namespace test
126 }  // namespace quic
127