Make gateway actually proxy requests in masque_tcp_server

Prior to this CL, the gateway would only send fake responses. Now it actually queries an upsteam

This CL also improves error handling.

PiperOrigin-RevId: 846925141
diff --git a/quiche/quic/masque/masque_tcp_server_bin.cc b/quiche/quic/masque/masque_tcp_server_bin.cc
index 6023d41..f43e9d2 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/status.h"
 #include "absl/status/statusor.h"
 #include "absl/strings/escaping.h"
+#include "absl/strings/numbers.h"
 #include "absl/strings/str_cat.h"
 #include "absl/strings/str_split.h"
 #include "absl/strings/string_view.h"
@@ -50,6 +51,7 @@
 #include "quiche/common/quiche_ip_address.h"
 #include "quiche/common/quiche_ip_address_family.h"
 #include "quiche/common/quiche_socket_address.h"
+#include "quiche/common/quiche_status_utils.h"
 #include "quiche/oblivious_http/common/oblivious_http_header_key_config.h"
 #include "quiche/oblivious_http/oblivious_http_gateway.h"
 
@@ -111,98 +113,114 @@
 
 class MasqueOhttpGateway {
  public:
+  class Visitor {
+   public:
+    virtual ~Visitor() = default;
+    virtual void SavePendingGatewayRequest(
+        MasqueH2Connection* connection, int32_t stream_id,
+        MasqueConnectionPool::RequestId request_id,
+        ObliviousHttpRequest::Context&& ohttp_context) = 0;
+  };
+
   static std::unique_ptr<MasqueOhttpGateway> Create(
       const std::string& ohttp_key) {
     auto ohttp_gateway = absl::WrapUnique(new MasqueOhttpGateway());
-    if (!ohttp_gateway->Setup(ohttp_key)) {
+    if (!ohttp_gateway->Setup(ohttp_key).ok()) {
       return nullptr;
     }
     return ohttp_gateway;
   }
 
-  bool HandleRequest(MasqueH2Connection* connection, int32_t stream_id,
-                     const std::string& encapsulated_request) {
+  absl::Status HandleRequest(MasqueConnectionPool* pool,
+                             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;
+      return absl::InternalError("Not ready to handle OHTTP request");
     }
     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;
-    }
+    QUICHE_RETURN_IF_ERROR(decrypted_request.status());
     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;
-    }
+    QUICHE_RETURN_IF_ERROR(binary_request.status());
     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);
+    MasqueConnectionPool::Message request;
+    request.headers[":method"] = control_data.method;
+    request.headers[":scheme"] = control_data.scheme;
+    request.headers[":authority"] = control_data.authority;
+    request.headers[":path"] = control_data.path;
+    request.body = binary_request->body();
+    absl::StatusOr<MasqueConnectionPool::RequestId> request_id =
+        pool->SendRequest(request);
+    QUICHE_RETURN_IF_ERROR(request_id.status());
+    QUICHE_LOG(INFO) << "Sent decapsulated request";
+    visitor_->SavePendingGatewayRequest(
+        connection, stream_id, *request_id,
+        std::move(*decrypted_request).ReleaseContext());
+    return absl::OkStatus();
+  }
+
+  absl::StatusOr<MasqueConnectionPool::Message> HandleResponse(
+      MasqueConnectionPool::Message&& response,
+      ObliviousHttpRequest::Context&& ohttp_context) {
+    if (!ohttp_gateway_.has_value()) {
+      return absl::InternalError("Not ready to handle OHTTP response");
+    }
+    auto status_pair = response.headers.find(":status");
+    if (status_pair == response.headers.end()) {
+      return absl::InternalError("Response is missing status code");
+    }
+    int status_code;
+    if (!absl::SimpleAtoi(status_pair->second, &status_code)) {
+      return absl::InternalError(
+          absl::StrCat("Failed to parse status code: ", status_pair->second));
+    }
+    BinaryHttpResponse binary_response(status_code);
+    for (const auto& [key, value] : response.headers) {
+      if (key != ":status") {
+        binary_response.AddHeaderField({std::string(key), std::string(value)});
+      }
+    }
+    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;
-    }
+    QUICHE_RETURN_IF_ERROR(encoded_response.status());
 
-    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;
+        ohttp_gateway_->CreateObliviousHttpResponse(*encoded_response,
+                                                    ohttp_context);
+    QUICHE_RETURN_IF_ERROR(ohttp_response.status());
+    MasqueConnectionPool::Message outer_response;
+    outer_response.headers[":status"] = "200";
+    outer_response.headers["content-type"] = "message/ohttp-res";
+    outer_response.body = ohttp_response->EncapsulateAndSerialize();
+    return outer_response;
   }
 
   std::string concatenated_keys() const { return concatenated_keys_; }
+  void set_visitor(Visitor* visitor) { visitor_ = visitor; }
 
  private:
   MasqueOhttpGateway() = default;
 
-  bool Setup(const std::string& ohttp_key) {
+  absl::Status Setup(const std::string& ohttp_key) {
     hpke_key_.reset(EVP_HPKE_KEY_new());
     if (!ohttp_key.empty()) {
       if (!absl::HexStringToBytes(ohttp_key, &hpke_private_key_)) {
-        QUICHE_LOG(ERROR) << "OHTTP key is not a valid hex string";
-        return false;
+        return absl::InvalidArgumentError(
+            "OHTTP key is not a valid hex string");
       }
       if (EVP_HPKE_KEY_init(
               hpke_key_.get(), kem_,
               reinterpret_cast<const uint8_t*>(hpke_private_key_.data()),
               hpke_private_key_.size()) != 1) {
-        QUICHE_LOG(ERROR) << "Failed to ingest HPKE key";
-        return false;
+        return absl::InternalError("Failed to ingest HPKE key");
       }
     } else {
       if (EVP_HPKE_KEY_generate(hpke_key_.get(), kem_) != 1) {
-        QUICHE_LOG(ERROR) << "Failed to generate new HPKE key";
-        return false;
+        return absl::InternalError("Failed to generate new HPKE key");
       }
       size_t private_key_len = EVP_HPKE_KEM_private_key_len(kem_);
       hpke_private_key_ = std::string(private_key_len, '0');
@@ -211,8 +229,7 @@
               reinterpret_cast<uint8_t*>(hpke_private_key_.data()),
               &private_key_len, private_key_len) != 1 ||
           private_key_len != hpke_private_key_.size()) {
-        QUICHE_LOG(ERROR) << "Failed to extract new HPKE private key";
-        return false;
+        return absl::InternalError("Failed to extract new HPKE private key");
       }
       QUICHE_LOG(INFO) << "Generated new HPKE private key: "
                        << absl::BytesToHexString(hpke_private_key_);
@@ -224,8 +241,7 @@
             reinterpret_cast<uint8_t*>(hpke_public_key_.data()),
             &public_key_len, public_key_len) != 1 ||
         public_key_len != hpke_public_key_.size()) {
-      QUICHE_LOG(ERROR) << "Failed to extract new HPKE public key";
-      return false;
+      return absl::InternalError("Failed to extract new HPKE public key");
     }
     static constexpr uint8_t kOhttpKeyId = 0x01;
     static constexpr uint16_t kOhttpKemId = EVP_HPKE_DHKEM_X25519_HKDF_SHA256;
@@ -234,43 +250,28 @@
     absl::StatusOr<ObliviousHttpHeaderKeyConfig> ohttp_header_key_config =
         ObliviousHttpHeaderKeyConfig::Create(kOhttpKeyId, kOhttpKemId,
                                              kOhttpKdfId, kOhttpAeadId);
-    if (!ohttp_header_key_config.ok()) {
-      QUICHE_LOG(ERROR) << "Failed to create OHTTP header key config: "
-                        << ohttp_header_key_config.status();
-      return false;
-    }
+    QUICHE_RETURN_IF_ERROR(ohttp_header_key_config.status());
     QUICHE_LOG(INFO) << "Using OHTTP header key config: "
                      << ohttp_header_key_config->DebugString();
     absl::StatusOr<ObliviousHttpKeyConfigs> ohttp_key_configs =
         ObliviousHttpKeyConfigs::Create(*ohttp_header_key_config,
                                         hpke_public_key_);
-    if (!ohttp_key_configs.ok()) {
-      QUICHE_LOG(ERROR) << "Failed to create OHTTP key configs: "
-                        << ohttp_key_configs.status();
-      return false;
-    }
+    QUICHE_RETURN_IF_ERROR(ohttp_key_configs.status());
     QUICHE_LOG(INFO) << "Using OHTTP key configs: " << std::endl
                      << ohttp_key_configs->DebugString();
     absl::StatusOr<std::string> concatenated_keys =
         ohttp_key_configs->GenerateConcatenatedKeys();
-    if (!concatenated_keys.ok()) {
-      QUICHE_LOG(ERROR) << "Failed to generate concatenated keys: "
-                        << concatenated_keys.status();
-      return false;
-    }
+    QUICHE_RETURN_IF_ERROR(concatenated_keys.status());
     concatenated_keys_ = *concatenated_keys;
     absl::StatusOr<ObliviousHttpGateway> ohttp_gateway =
         ObliviousHttpGateway::Create(hpke_private_key_,
                                      *ohttp_header_key_config);
-    if (!ohttp_gateway.ok()) {
-      QUICHE_LOG(ERROR) << "Failed to create OHTTP gateway: "
-                        << ohttp_gateway.status();
-      return false;
-    }
+    QUICHE_RETURN_IF_ERROR(ohttp_gateway.status());
     ohttp_gateway_.emplace(std::move(*ohttp_gateway));
-    return true;
+    return absl::OkStatus();
   }
 
+  Visitor* visitor_ = nullptr;
   std::string hpke_private_key_;
   std::string hpke_public_key_;
   const EVP_HPKE_KEM* kem_ = EVP_hpke_x25519_hkdf_sha256();
@@ -369,7 +370,8 @@
 
 class MasqueTcpServer : public QuicSocketEventListener,
                         public MasqueH2Connection::Visitor,
-                        public MasqueConnectionPool::Visitor {
+                        public MasqueConnectionPool::Visitor,
+                        public MasqueOhttpGateway::Visitor {
  public:
   using RequestId = MasqueConnectionPool::RequestId;
   using Message = MasqueConnectionPool::Message;
@@ -515,11 +517,11 @@
         connections_.end());
   }
 
-  bool HandleOhttpGatewayRequest(MasqueH2Connection* connection,
-                                 int32_t stream_id,
-                                 const std::string& encapsulated_request) {
-    return masque_ohttp_gateway_->HandleRequest(connection, stream_id,
-                                                encapsulated_request);
+  absl::Status HandleOhttpGatewayRequest(
+      MasqueH2Connection* connection, int32_t stream_id,
+      const std::string& encapsulated_request) {
+    return masque_ohttp_gateway_->HandleRequest(
+        &connection_pool_, connection, stream_id, encapsulated_request);
   }
 
   absl::Status HandleOhttpRelayRequest(MasqueH2Connection* connection,
@@ -535,11 +537,7 @@
     request.body = encapsulated_request;
     absl::StatusOr<RequestId> request_id =
         connection_pool_.SendRequest(request);
-    if (!request_id.ok()) {
-      QUICHE_LOG(ERROR) << "Failed to send relayed request: "
-                        << request_id.status();
-      return request_id.status();
-    }
+    QUICHE_RETURN_IF_ERROR(request_id.status());
     QUICHE_LOG(INFO) << "Sent relayed request";
     PendingRequest pending_request;
     pending_request.connection = connection;
@@ -573,23 +571,29 @@
                method_pair->second == "POST" &&
                content_type_pair != headers.end() &&
                content_type_pair->second == "message/ohttp-req") {
-      if (HandleOhttpRelayRequest(connection, stream_id, body,
-                                  relay_pair->second)
-              .ok()) {
+      absl::Status status = HandleOhttpRelayRequest(connection, stream_id, body,
+                                                    relay_pair->second);
+      if (status.ok()) {
         return;
       } else {
+        QUICHE_LOG(ERROR) << "Failed to handle OHTTP relay request for "
+                          << path_pair->second << ": " << status;
         response_headers[":status"] = "500";
-        response_body = "Failed to handle OHTTP relay request";
+        response_body = status.message();
       }
     } 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)) {
+      absl::Status status =
+          HandleOhttpGatewayRequest(connection, stream_id, body);
+      if (status.ok()) {
         return;
       } else {
         response_headers[":status"] = "500";
-        response_body = "Failed to handle OHTTP gateway request";
+        QUICHE_LOG(ERROR) << "Failed to handle OHTTP gateway request: "
+                          << status;
+        response_body = status.message();
       }
     } else if (method_pair->second == "GET" && path_pair->second == "/") {
       response_headers[":status"] = "200";
@@ -621,15 +625,33 @@
     quiche::HttpHeaderBlock response_headers;
     std::string response_body;
     if (response.ok()) {
-      QUICHE_LOG(INFO) << "Forwarding relayed response to stream ID "
-                       << pending_request.stream_id;
-      response_headers = std::move(response->headers);
-      response_body = std::move(response->body);
+      if (pending_request.ohttp_context.has_value()) {
+        absl::StatusOr<MasqueConnectionPool::Message> gateway_response =
+            masque_ohttp_gateway_->HandleResponse(
+                std::move(*response),
+                std::move(*pending_request.ohttp_context));
+        if (!gateway_response.ok()) {
+          response_headers[":status"] = "500";
+          response_body = absl::StrCat("Failed to handle gateway response: ",
+                                       gateway_response.status().message());
+          QUICHE_LOG(ERROR) << response_body;
+        } else {
+          response_headers = std::move(gateway_response->headers);
+          response_body = std::move(gateway_response->body);
+          QUICHE_LOG(INFO) << "Sending OHTTP response";
+        }
+      } else {
+        QUICHE_LOG(INFO) << "Forwarding relayed response to stream ID "
+                         << pending_request.stream_id;
+        response_headers = std::move(response->headers);
+        response_body = std::move(response->body);
+      }
     } else {
       QUICHE_LOG(ERROR) << "Received relayed error response: "
                         << response.status();
       response_headers[":status"] = "500";
-      response_body = "Relayed request failed";
+      response_body =
+          absl::StrCat("Relayed request failed: ", response.status().message());
     }
     pending_request.connection->SendResponse(pending_request.stream_id,
                                              response_headers, response_body);
@@ -644,6 +666,9 @@
     }
     gateway_path_ = gateway_path;
     masque_ohttp_gateway_ = gateway;
+    if (masque_ohttp_gateway_) {
+      masque_ohttp_gateway_->set_visitor(this);
+    }
     return true;
   }
 
@@ -672,10 +697,22 @@
     return true;
   }
 
+  void SavePendingGatewayRequest(
+      MasqueH2Connection* connection, int32_t stream_id,
+      MasqueConnectionPool::RequestId request_id,
+      ObliviousHttpRequest::Context&& ohttp_context) override {
+    PendingRequest pending_request;
+    pending_request.connection = connection;
+    pending_request.stream_id = stream_id;
+    pending_request.ohttp_context = std::move(ohttp_context);
+    pending_requests_.insert({request_id, std::move(pending_request)});
+  }
+
  private:
   struct PendingRequest {
     MasqueH2Connection* connection = nullptr;  // Not owned.
     int32_t stream_id = -1;
+    std::optional<ObliviousHttpRequest::Context> ohttp_context;
   };
 
   void AcceptConnection() {