Update toy MASQUE code to connect-udp-12

We confirmed that this interoperates with Apple's latest implementation.

PiperOrigin-RevId: 449547363
diff --git a/quiche/quic/masque/masque_client_bin.cc b/quiche/quic/masque/masque_client_bin.cc
index f973227..0868e7d 100644
--- a/quiche/quic/masque/masque_client_bin.cc
+++ b/quiche/quic/masque/masque_client_bin.cc
@@ -59,9 +59,11 @@
 
   std::string uri_template = urls[0];
   if (!absl::StrContains(uri_template, '/')) {
-    // Allow passing in authority instead of URI template.
+    // If an authority is passed in instead of a URI template, use the default
+    // URI template.
     uri_template =
-        absl::StrCat("https://", uri_template, "/{target_host}/{target_port}/");
+        absl::StrCat("https://", uri_template,
+                     "/.well-known/masque/udp/{target_host}/{target_port}/");
   }
   url::Parsed parsed_uri_template;
   url::ParseStandardURL(uri_template.c_str(), uri_template.length(),
diff --git a/quiche/quic/masque/masque_client_session.cc b/quiche/quic/masque/masque_client_session.cc
index 4dcdb45..28ba839 100644
--- a/quiche/quic/masque/masque_client_session.cc
+++ b/quiche/quic/masque/masque_client_session.cc
@@ -4,12 +4,14 @@
 
 #include "quiche/quic/masque/masque_client_session.h"
 
+#include <cstring>
 #include <string>
 
 #include "absl/algorithm/container.h"
 #include "absl/container/flat_hash_map.h"
 #include "absl/container/flat_hash_set.h"
 #include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
 #include "url/url_canon.h"
 #include "quiche/quic/core/http/spdy_utils.h"
 #include "quiche/quic/core/quic_data_reader.h"
@@ -122,7 +124,7 @@
   headers[":scheme"] = scheme;
   headers[":authority"] = authority;
   headers[":path"] = canonicalized_path;
-  headers["connect-udp-version"] = "6";
+  headers["connect-udp-version"] = "12";
   size_t bytes_sent =
       stream->SendRequest(std::move(headers), /*body=*/"", /*fin=*/false);
   if (bytes_sent == 0) {
@@ -145,8 +147,12 @@
     return;
   }
 
+  std::string http_payload;
+  http_payload.resize(1 + packet.size());
+  http_payload[0] = 0;
+  memcpy(&http_payload[1], packet.data(), packet.size());
   MessageStatus message_status =
-      SendHttp3Datagram(connect_udp->stream()->id(), packet);
+      SendHttp3Datagram(connect_udp->stream()->id(), http_payload);
 
   QUIC_DVLOG(1) << "Sent packet to " << target_server_address
                 << " compressed with stream ID " << connect_udp->stream()->id()
@@ -268,8 +274,21 @@
 void MasqueClientSession::ConnectUdpClientState::OnHttp3Datagram(
     QuicStreamId stream_id, absl::string_view payload) {
   QUICHE_DCHECK_EQ(stream_id, stream()->id());
-  encapsulated_client_session_->ProcessPacket(payload, target_server_address_);
-  QUIC_DVLOG(1) << "Sent " << payload.size()
+  QuicDataReader reader(payload);
+  uint64_t context_id;
+  if (!reader.ReadVarInt62(&context_id)) {
+    QUIC_DLOG(ERROR) << "Failed to read context ID";
+    return;
+  }
+  if (context_id != 0) {
+    QUIC_DLOG(ERROR) << "Ignoring HTTP Datagram with unexpected context ID "
+                     << context_id;
+    return;
+  }
+  absl::string_view http_payload = reader.ReadRemainingPayload();
+  encapsulated_client_session_->ProcessPacket(http_payload,
+                                              target_server_address_);
+  QUIC_DVLOG(1) << "Sent " << http_payload.size()
                 << " bytes to connection for stream ID " << stream_id;
 }
 
diff --git a/quiche/quic/masque/masque_server_session.cc b/quiche/quic/masque/masque_server_session.cc
index bfc8cdf..742b0c6 100644
--- a/quiche/quic/masque/masque_server_session.cc
+++ b/quiche/quic/masque/masque_server_session.cc
@@ -176,20 +176,21 @@
   }
   // Extract target host and port from path using default template.
   std::vector<absl::string_view> path_split = absl::StrSplit(path, '/');
-  if (path_split.size() != 4 || !path_split[0].empty() ||
-      path_split[1].empty() || path_split[2].empty() ||
-      !path_split[3].empty()) {
+  if (path_split.size() != 7 || !path_split[0].empty() ||
+      path_split[1] != ".well-known" || path_split[2] != "masque" ||
+      path_split[3] != "udp" || path_split[4].empty() ||
+      path_split[5].empty() || !path_split[6].empty()) {
     QUIC_DLOG(ERROR) << "MASQUE request with bad path \"" << path << "\"";
     return CreateBackendErrorResponse("400", "Bad path");
   }
-  absl::optional<std::string> host = quiche::AsciiUrlDecode(path_split[1]);
+  absl::optional<std::string> host = quiche::AsciiUrlDecode(path_split[4]);
   if (!host.has_value()) {
-    QUIC_DLOG(ERROR) << "Failed to decode host \"" << path_split[1] << "\"";
+    QUIC_DLOG(ERROR) << "Failed to decode host \"" << path_split[4] << "\"";
     return CreateBackendErrorResponse("500", "Failed to decode host");
   }
-  absl::optional<std::string> port = quiche::AsciiUrlDecode(path_split[2]);
+  absl::optional<std::string> port = quiche::AsciiUrlDecode(path_split[5]);
   if (!port.has_value()) {
-    QUIC_DLOG(ERROR) << "Failed to decode port \"" << path_split[2] << "\"";
+    QUIC_DLOG(ERROR) << "Failed to decode port \"" << path_split[5] << "\"";
     return CreateBackendErrorResponse("500", "Failed to decode port");
   }
 
@@ -283,11 +284,12 @@
                 << " server " << expected_target_server_address;
   QuicUdpSocketApi socket_api;
   BitMask64 packet_info_interested(QuicUdpPacketInfoBit::PEER_ADDRESS);
-  char packet_buffer[kMaxIncomingPacketSize];
+  char packet_buffer[1 + kMaxIncomingPacketSize];
+  packet_buffer[0] = 0;  // context ID.
   char control_buffer[kDefaultUdpPacketControlBufferSize];
   while (true) {
     QuicUdpSocketApi::ReadPacketResult read_result;
-    read_result.packet_buffer = {packet_buffer, sizeof(packet_buffer)};
+    read_result.packet_buffer = {packet_buffer + 1, sizeof(packet_buffer) - 1};
     read_result.control_buffer = {control_buffer, sizeof(control_buffer)};
     socket_api.ReadPacket(fd, packet_info_interested, &read_result);
     if (!read_result.ok) {
@@ -316,9 +318,9 @@
       return;
     }
     // The packet is valid, send it to the client in a DATAGRAM frame.
-    MessageStatus message_status = it->stream()->SendHttp3Datagram(
-        absl::string_view(read_result.packet_buffer.buffer,
-                          read_result.packet_buffer.buffer_len));
+    MessageStatus message_status =
+        it->stream()->SendHttp3Datagram(absl::string_view(
+            packet_buffer, read_result.packet_buffer.buffer_len + 1));
     QUIC_DVLOG(1) << "Sent UDP packet from " << expected_target_server_address
                   << " of length " << read_result.packet_buffer.buffer_len
                   << " with stream ID " << it->stream()->id()
@@ -409,12 +411,24 @@
 void MasqueServerSession::ConnectUdpServerState::OnHttp3Datagram(
     QuicStreamId stream_id, absl::string_view payload) {
   QUICHE_DCHECK_EQ(stream_id, stream()->id());
+  QuicDataReader reader(payload);
+  uint64_t context_id;
+  if (!reader.ReadVarInt62(&context_id)) {
+    QUIC_DLOG(ERROR) << "Failed to read context ID";
+    return;
+  }
+  if (context_id != 0) {
+    QUIC_DLOG(ERROR) << "Ignoring HTTP Datagram with unexpected context ID "
+                     << context_id;
+    return;
+  }
+  absl::string_view http_payload = reader.ReadRemainingPayload();
   QuicUdpSocketApi socket_api;
   QuicUdpPacketInfo packet_info;
   packet_info.SetPeerAddress(target_server_address_);
   WriteResult write_result = socket_api.WritePacket(
-      fd_, payload.data(), payload.length(), packet_info);
-  QUIC_DVLOG(1) << "Wrote packet of length " << payload.length() << " to "
+      fd_, http_payload.data(), http_payload.length(), packet_info);
+  QUIC_DVLOG(1) << "Wrote packet of length " << http_payload.length() << " to "
                 << target_server_address_ << " with result " << write_result;
 }