Allow supporting multiple OHTTP gateways from a single relay PiperOrigin-RevId: 846764070
diff --git a/quiche/quic/masque/masque_tcp_server_bin.cc b/quiche/quic/masque/masque_tcp_server_bin.cc index bffeff8..a21e1e2 100644 --- a/quiche/quic/masque/masque_tcp_server_bin.cc +++ b/quiche/quic/masque/masque_tcp_server_bin.cc
@@ -26,6 +26,7 @@ #include "absl/status/statusor.h" #include "absl/strings/escaping.h" #include "absl/strings/str_cat.h" +#include "absl/strings/str_split.h" #include "absl/strings/string_view.h" #include "openssl/base.h" #include "openssl/bio.h" @@ -68,12 +69,11 @@ "Hex-encoded bytes of the OHTTP HPKE private key."); DEFINE_QUICHE_COMMAND_LINE_FLAG( - std::string, relay_path, "", - "Path of the relay server to accept requests on."); - -DEFINE_QUICHE_COMMAND_LINE_FLAG( - std::string, relay_gateway_url, "", - "URL of the gateway that this relay will forward requests to."); + std::string, relay, "", + "Enables and configures an OHTTP relay. The format is a list of (path, " + "URL) pairs where each local path is relayed to the corresponding URL, " + "formatted as \"path1>url1|path2>url2\". For example: " + "\"/foo>https://foo.example:8443/|/bar>https://example.com/bar\"."); DEFINE_QUICHE_COMMAND_LINE_FLAG( bool, disable_certificate_verification, false, @@ -508,14 +508,15 @@ encapsulated_request); } - absl::Status HandleOhttpRelayRequest( - MasqueH2Connection* connection, int32_t stream_id, - const std::string& encapsulated_request) { + absl::Status HandleOhttpRelayRequest(MasqueH2Connection* connection, + int32_t stream_id, + const std::string& encapsulated_request, + const QuicUrl& relay_gateway_url) { Message request; request.headers[":method"] = "POST"; - request.headers[":scheme"] = relay_gateway_url_.scheme(); - request.headers[":authority"] = relay_gateway_url_.HostPort(); - request.headers[":path"] = relay_gateway_url_.path(); + request.headers[":scheme"] = relay_gateway_url.scheme(); + request.headers[":authority"] = relay_gateway_url.HostPort(); + request.headers[":path"] = relay_gateway_url.path(); request.headers["content-type"] = "message/ohttp-req"; request.body = encapsulated_request; absl::StatusOr<RequestId> request_id = @@ -552,11 +553,14 @@ response_headers[":status"] = "200"; response_headers["content-type"] = "application/ohttp-keys"; response_body = masque_ohttp_gateway_->concatenated_keys(); - } else if (!relay_path_.empty() && path_pair->second == relay_path_ && + } else if (auto relay_pair = relay_gateway_urls_.find(path_pair->second); + relay_pair != relay_gateway_urls_.end() && method_pair->second == "POST" && content_type_pair != headers.end() && content_type_pair->second == "message/ohttp-req") { - if (HandleOhttpRelayRequest(connection, stream_id, body).ok()) { + if (HandleOhttpRelayRequest(connection, stream_id, body, + relay_pair->second) + .ok()) { return; } else { response_headers[":status"] = "500"; @@ -616,13 +620,28 @@ pending_request.connection->AttemptToSend(); } - bool SetupRelay(const std::string& relay_path, - const std::string& relay_gateway_url) { - if (relay_path.empty() != relay_gateway_url.empty()) { - return false; + bool SetupRelay(const std::string& relay) { + if (relay.empty()) { + return true; } - relay_path_ = relay_path; - relay_gateway_url_ = QuicUrl(relay_gateway_url, "https"); + std::vector<absl::string_view> relay_split = absl::StrSplit(relay, '|'); + for (absl::string_view relay_param : relay_split) { + std::vector<absl::string_view> relay_param_split = + absl::StrSplit(relay_param, '>'); + if (relay_param_split.size() != 2) { + QUICHE_LOG(ERROR) << "Invalid relay parameter: \"" << relay_param + << "\". It should be in the format of \"path>url\""; + return false; + } + absl::string_view path = relay_param_split[0]; + absl::string_view gateway_url = relay_param_split[1]; + auto [it, inserted] = relay_gateway_urls_.insert( + {std::string(path), QuicUrl(gateway_url, "https")}); + if (!inserted) { + QUICHE_LOG(ERROR) << "Duplicate relay path: \"" << path << "\""; + return false; + } + } return true; } @@ -662,8 +681,8 @@ MasqueOhttpGateway* masque_ohttp_gateway_; // Unowned. SocketFd server_socket_ = kInvalidSocketFd; std::vector<std::unique_ptr<MasqueH2SocketConnection>> connections_; - std::string relay_path_; - QuicUrl relay_gateway_url_; + // Maps from local paths to remote gateway URLs. + absl::flat_hash_map<std::string, QuicUrl> relay_gateway_urls_; MasqueConnectionPool connection_pool_; absl::flat_hash_map<RequestId, PendingRequest> pending_requests_; }; @@ -733,11 +752,8 @@ disable_certificate_verification, address_family_for_lookup); - if (!server.SetupRelay( - quiche::GetQuicheCommandLineFlag(FLAGS_relay_path), - quiche::GetQuicheCommandLineFlag(FLAGS_relay_gateway_url))) { - QUICHE_LOG(ERROR) << "Both --relay_path and --relay_gateway_url must be " - "set, or neither can be set"; + if (!server.SetupRelay(quiche::GetQuicheCommandLineFlag(FLAGS_relay))) { + QUICHE_LOG(ERROR) << "Invalid --relay input"; return 1; } if (!server.SetupSslCtx(server_certificate_file, server_key_file,