blob: 983db8e4481380cc73d15df55920e6a1dccd51d3 [file] [log] [blame]
// 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 "quiche/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 "quiche/quic/core/web_transport_interface.h"
#include "quiche/quic/test_tools/web_transport_resets_backend.h"
#include "quiche/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::Http2HeaderBlock& /*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(&param_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