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(¶m_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