|  | // Copyright (c) 2021 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "quic/test_tools/quic_test_backend.h" | 
|  |  | 
|  | #include <cstring> | 
|  | #include <memory> | 
|  |  | 
|  | #include "absl/strings/str_cat.h" | 
|  | #include "absl/strings/str_split.h" | 
|  | #include "absl/strings/string_view.h" | 
|  | #include "quic/core/quic_buffer_allocator.h" | 
|  | #include "quic/core/quic_simple_buffer_allocator.h" | 
|  | #include "quic/core/web_transport_interface.h" | 
|  | #include "quic/platform/api/quic_mem_slice.h" | 
|  | #include "quic/test_tools/web_transport_resets_backend.h" | 
|  | #include "quic/tools/web_transport_test_visitors.h" | 
|  |  | 
|  | namespace quic { | 
|  | namespace test { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // SessionCloseVisitor implements the "/session-close" endpoint.  If the client | 
|  | // sends a unidirectional stream of format "code message" to this endpoint, it | 
|  | // will close the session with the corresponding error code and error message. | 
|  | // For instance, sending "42 test error" will cause it to be closed with code 42 | 
|  | // and message "test error". | 
|  | class SessionCloseVisitor : public WebTransportVisitor { | 
|  | public: | 
|  | SessionCloseVisitor(WebTransportSession* session) : session_(session) {} | 
|  |  | 
|  | void OnSessionReady(const spdy::SpdyHeaderBlock& /*headers*/) override {} | 
|  | void OnSessionClosed(WebTransportSessionError /*error_code*/, | 
|  | const std::string& /*error_message*/) override {} | 
|  |  | 
|  | void OnIncomingBidirectionalStreamAvailable() override {} | 
|  | void OnIncomingUnidirectionalStreamAvailable() override { | 
|  | WebTransportStream* stream = session_->AcceptIncomingUnidirectionalStream(); | 
|  | if (stream == nullptr) { | 
|  | return; | 
|  | } | 
|  | stream->SetVisitor( | 
|  | std::make_unique<WebTransportUnidirectionalEchoReadVisitor>( | 
|  | stream, [this](const std::string& data) { | 
|  | std::pair<absl::string_view, absl::string_view> parsed = | 
|  | absl::StrSplit(data, absl::MaxSplits(' ', 1)); | 
|  | WebTransportSessionError error_code = 0; | 
|  | bool success = absl::SimpleAtoi(parsed.first, &error_code); | 
|  | QUICHE_DCHECK(success) << data; | 
|  | session_->CloseSession(error_code, parsed.second); | 
|  | })); | 
|  | stream->visitor()->OnCanRead(); | 
|  | } | 
|  |  | 
|  | void OnDatagramReceived(absl::string_view /*datagram*/) override {} | 
|  |  | 
|  | void OnCanCreateNewOutgoingBidirectionalStream() override {} | 
|  | void OnCanCreateNewOutgoingUnidirectionalStream() override {} | 
|  |  | 
|  | private: | 
|  | WebTransportSession* session_;  // Not owned. | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | QuicSimpleServerBackend::WebTransportResponse | 
|  | QuicTestBackend::ProcessWebTransportRequest( | 
|  | const spdy::Http2HeaderBlock& request_headers, | 
|  | WebTransportSession* session) { | 
|  | if (!SupportsWebTransport()) { | 
|  | return QuicSimpleServerBackend::ProcessWebTransportRequest(request_headers, | 
|  | session); | 
|  | } | 
|  |  | 
|  | auto path_it = request_headers.find(":path"); | 
|  | if (path_it == request_headers.end()) { | 
|  | WebTransportResponse response; | 
|  | response.response_headers[":status"] = "400"; | 
|  | return response; | 
|  | } | 
|  | absl::string_view path = path_it->second; | 
|  | // Match any "/echo.*" pass, e.g. "/echo_foobar" | 
|  | if (absl::StartsWith(path, "/echo")) { | 
|  | WebTransportResponse response; | 
|  | response.response_headers[":status"] = "200"; | 
|  | // Add response headers if the paramer has "set-header=XXX:YYY" query. | 
|  | GURL url = GURL(absl::StrCat("https://localhost", path)); | 
|  | const std::vector<std::string>& params = absl::StrSplit(url.query(), '&'); | 
|  | for (const auto& param : params) { | 
|  | absl::string_view param_view = param; | 
|  | if (absl::ConsumePrefix(¶m_view, "set-header=")) { | 
|  | const std::vector<absl::string_view> header_value = | 
|  | absl::StrSplit(param_view, ':'); | 
|  | if (header_value.size() == 2 && | 
|  | !absl::StartsWith(header_value[0], ":")) { | 
|  | response.response_headers[header_value[0]] = header_value[1]; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | response.visitor = | 
|  | std::make_unique<EchoWebTransportSessionVisitor>(session); | 
|  | return response; | 
|  | } | 
|  | if (path == "/resets") { | 
|  | return WebTransportResetsBackend(request_headers, session); | 
|  | } | 
|  | if (path == "/session-close") { | 
|  | WebTransportResponse response; | 
|  | response.response_headers[":status"] = "200"; | 
|  | response.visitor = std::make_unique<SessionCloseVisitor>(session); | 
|  | return response; | 
|  | } | 
|  |  | 
|  | WebTransportResponse response; | 
|  | response.response_headers[":status"] = "404"; | 
|  | return response; | 
|  | } | 
|  |  | 
|  | }  // namespace test | 
|  | }  // namespace quic |