Make gateway optional in masque_tcp_server PiperOrigin-RevId: 846788802
diff --git a/quiche/BUILD.bazel b/quiche/BUILD.bazel index 55f28cc..9358904 100644 --- a/quiche/BUILD.bazel +++ b/quiche/BUILD.bazel
@@ -624,6 +624,7 @@ "@boringssl//:ssl", "@com_google_absl//absl/cleanup", "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/memory", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings",
diff --git a/quiche/quic/masque/masque_tcp_server_bin.cc b/quiche/quic/masque/masque_tcp_server_bin.cc index a21e1e2..6023d41 100644 --- a/quiche/quic/masque/masque_tcp_server_bin.cc +++ b/quiche/quic/masque/masque_tcp_server_bin.cc
@@ -22,6 +22,7 @@ #include "absl/cleanup/cleanup.h" #include "absl/container/flat_hash_map.h" +#include "absl/memory/memory.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/escaping.h" @@ -69,6 +70,12 @@ "Hex-encoded bytes of the OHTTP HPKE private key."); DEFINE_QUICHE_COMMAND_LINE_FLAG( + std::string, gateway_path, "", + "Enables and configures an OHTTP gateway. Sets the path at which the " + "gateway will respond to both key requests and encapsulated requests. " + "Example: \"/.well-known/ohttp-gateway\"."); + +DEFINE_QUICHE_COMMAND_LINE_FLAG( 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, " @@ -104,7 +111,79 @@ class MasqueOhttpGateway { public: - MasqueOhttpGateway() {} + static std::unique_ptr<MasqueOhttpGateway> Create( + const std::string& ohttp_key) { + auto ohttp_gateway = absl::WrapUnique(new MasqueOhttpGateway()); + if (!ohttp_gateway->Setup(ohttp_key)) { + return nullptr; + } + return ohttp_gateway; + } + + bool HandleRequest(MasqueH2Connection* connection, int32_t stream_id, + const std::string& encapsulated_request) { + if (!ohttp_gateway_.has_value()) { + QUICHE_LOG(ERROR) << "Not ready to handle OHTTP request"; + return false; + } + absl::StatusOr<ObliviousHttpRequest> decrypted_request = + ohttp_gateway_->DecryptObliviousHttpRequest(encapsulated_request); + if (!decrypted_request.ok()) { + QUICHE_LOG(ERROR) << "Failed to decrypt OHTTP request: " + << decrypted_request.status(); + return false; + } + absl::StatusOr<BinaryHttpRequest> binary_request = + BinaryHttpRequest::Create(decrypted_request->GetPlaintextData()); + if (!binary_request.ok()) { + QUICHE_LOG(ERROR) << "Failed to parse binary request: " + << binary_request.status(); + return false; + } + const BinaryHttpRequest::ControlData& control_data = + binary_request->control_data(); + // TODO(dschinazi): Send the decapsulated request to the authority instead + // of replying with a fake local response. + absl::string_view request_body = binary_request->body(); + std::string response_body = absl::StrCat( + "OHTTP Response! Request method: ", control_data.method, + " scheme: ", control_data.scheme, " path: ", control_data.path, + " authority: ", control_data.authority, " body: \"", request_body, + "\""); + + BinaryHttpResponse binary_response(/*status_code=*/200); + binary_response.swap_body(response_body); + absl::StatusOr<std::string> encoded_response = binary_response.Serialize(); + if (!encoded_response.ok()) { + QUICHE_LOG(ERROR) << "Failed to encode response: " + << encoded_response.status(); + return false; + } + + auto context = std::move(*decrypted_request).ReleaseContext(); + absl::StatusOr<ObliviousHttpResponse> ohttp_response = + ohttp_gateway_->CreateObliviousHttpResponse(*encoded_response, context); + if (!ohttp_response.ok()) { + QUICHE_LOG(ERROR) << "Failed to create OHTTP response: " + << ohttp_response.status(); + return false; + } + std::string encapsulated_response = + ohttp_response->EncapsulateAndSerialize(); + QUICHE_LOG(INFO) << "Sending OHTTP response"; + + quiche::HttpHeaderBlock response_headers; + response_headers[":status"] = "200"; + response_headers["content-type"] = "message/ohttp-res"; + connection->SendResponse(stream_id, response_headers, + encapsulated_response); + return true; + } + + std::string concatenated_keys() const { return concatenated_keys_; } + + private: + MasqueOhttpGateway() = default; bool Setup(const std::string& ohttp_key) { hpke_key_.reset(EVP_HPKE_KEY_new()); @@ -192,69 +271,6 @@ return true; } - bool HandleRequest(MasqueH2Connection* connection, int32_t stream_id, - const std::string& encapsulated_request) { - if (!ohttp_gateway_.has_value()) { - QUICHE_LOG(ERROR) << "Not ready to handle OHTTP request"; - return false; - } - absl::StatusOr<ObliviousHttpRequest> decrypted_request = - ohttp_gateway_->DecryptObliviousHttpRequest(encapsulated_request); - if (!decrypted_request.ok()) { - QUICHE_LOG(ERROR) << "Failed to decrypt OHTTP request: " - << decrypted_request.status(); - return false; - } - absl::StatusOr<BinaryHttpRequest> binary_request = - BinaryHttpRequest::Create(decrypted_request->GetPlaintextData()); - if (!binary_request.ok()) { - QUICHE_LOG(ERROR) << "Failed to parse binary request: " - << binary_request.status(); - return false; - } - const BinaryHttpRequest::ControlData& control_data = - binary_request->control_data(); - // TODO(dschinazi): Send the decapsulated request to the authority instead - // of replying with a fake local response. - absl::string_view request_body = binary_request->body(); - std::string response_body = absl::StrCat( - "OHTTP Response! Request method: ", control_data.method, - " scheme: ", control_data.scheme, " path: ", control_data.path, - " authority: ", control_data.authority, " body: \"", request_body, - "\""); - - BinaryHttpResponse binary_response(/*status_code=*/200); - binary_response.swap_body(response_body); - absl::StatusOr<std::string> encoded_response = binary_response.Serialize(); - if (!encoded_response.ok()) { - QUICHE_LOG(ERROR) << "Failed to encode response: " - << encoded_response.status(); - return false; - } - - auto context = std::move(*decrypted_request).ReleaseContext(); - absl::StatusOr<ObliviousHttpResponse> ohttp_response = - ohttp_gateway_->CreateObliviousHttpResponse(*encoded_response, context); - if (!ohttp_response.ok()) { - QUICHE_LOG(ERROR) << "Failed to create OHTTP response: " - << ohttp_response.status(); - return false; - } - std::string encapsulated_response = - ohttp_response->EncapsulateAndSerialize(); - QUICHE_LOG(INFO) << "Sending OHTTP response"; - - quiche::HttpHeaderBlock response_headers; - response_headers[":status"] = "200"; - response_headers["content-type"] = "message/ohttp-res"; - connection->SendResponse(stream_id, response_headers, - encapsulated_response); - return true; - } - - std::string concatenated_keys() const { return concatenated_keys_; } - - private: std::string hpke_private_key_; std::string hpke_public_key_; const EVP_HPKE_KEM* kem_ = EVP_hpke_x25519_hkdf_sha256(); @@ -358,12 +374,10 @@ using RequestId = MasqueConnectionPool::RequestId; using Message = MasqueConnectionPool::Message; - explicit MasqueTcpServer(MasqueOhttpGateway* masque_ohttp_gateway, - SSL_CTX* client_ssl_ctx, + explicit MasqueTcpServer(SSL_CTX* client_ssl_ctx, bool disable_certificate_verification, int address_family_for_lookup) : event_loop_(GetDefaultEventLoop()->Create(QuicDefaultClock::Get())), - masque_ohttp_gateway_(masque_ohttp_gateway), connection_pool_(event_loop_.get(), client_ssl_ctx, disable_certificate_verification, address_family_for_lookup, this) {} @@ -548,7 +562,8 @@ // the request, but handle it gracefully just in case. response_headers[":status"] = "400"; response_body = "Request missing pseudo-headers"; - } else if (method_pair->second == "GET" && accept_pair != headers.end() && + } else if (masque_ohttp_gateway_ && method_pair->second == "GET" && + accept_pair != headers.end() && accept_pair->second == "application/ohttp-keys") { response_headers[":status"] = "200"; response_headers["content-type"] = "application/ohttp-keys"; @@ -566,7 +581,8 @@ response_headers[":status"] = "500"; response_body = "Failed to handle OHTTP relay request"; } - } else if (method_pair->second == "POST" && + } else if (!gateway_path_.empty() && path_pair->second == gateway_path_ && + method_pair->second == "POST" && content_type_pair != headers.end() && content_type_pair->second == "message/ohttp-req") { if (HandleOhttpGatewayRequest(connection, stream_id, body)) { @@ -620,6 +636,17 @@ pending_request.connection->AttemptToSend(); } + bool SetupGateway(const std::string& gateway_path, + MasqueOhttpGateway* gateway) { + if (gateway_path.empty() != (gateway == nullptr)) { + QUICHE_LOG(ERROR) << "Invalid gateway configuration"; + return false; + } + gateway_path_ = gateway_path; + masque_ohttp_gateway_ = gateway; + return true; + } + bool SetupRelay(const std::string& relay) { if (relay.empty()) { return true; @@ -678,6 +705,7 @@ std::unique_ptr<QuicEventLoop> event_loop_; bssl::UniquePtr<SSL_CTX> ctx_; + std::string gateway_path_; MasqueOhttpGateway* masque_ohttp_gateway_; // Unowned. SocketFd server_socket_ = kInvalidSocketFd; std::vector<std::unique_ptr<MasqueH2SocketConnection>> connections_; @@ -713,11 +741,16 @@ quiche::QuicheSystemEventLoop system_event_loop("masque_tcp_server"); - MasqueOhttpGateway masque_ohttp_gateway; - if (!masque_ohttp_gateway.Setup( - quiche::GetQuicheCommandLineFlag(FLAGS_ohttp_key))) { - QUICHE_LOG(ERROR) << "Failed to setup OHTTP"; - return 1; + std::unique_ptr<MasqueOhttpGateway> masque_ohttp_gateway; + std::string gateway_path = + quiche::GetQuicheCommandLineFlag(FLAGS_gateway_path); + if (!gateway_path.empty()) { + masque_ohttp_gateway = MasqueOhttpGateway::Create( + quiche::GetQuicheCommandLineFlag(FLAGS_ohttp_key)); + if (!masque_ohttp_gateway) { + QUICHE_LOG(ERROR) << "Failed to create OHTTP gateway"; + return 1; + } } const bool disable_certificate_verification = @@ -748,10 +781,14 @@ return 1; } - MasqueTcpServer server(&masque_ohttp_gateway, client_ssl_ctx->get(), + MasqueTcpServer server(client_ssl_ctx->get(), disable_certificate_verification, address_family_for_lookup); + if (!server.SetupGateway(gateway_path, masque_ohttp_gateway.get())) { + QUICHE_LOG(ERROR) << "Invalid gateway configuration"; + return 1; + } if (!server.SetupRelay(quiche::GetQuicheCommandLineFlag(FLAGS_relay))) { QUICHE_LOG(ERROR) << "Invalid --relay input"; return 1;