Implement mapping WebTransport error codes to HTTP/3 ones.

This implements the scheme described in <https://github.com/ietf-wg-webtrans/draft-ietf-webtrans-http3/pull/59/files>.

PiperOrigin-RevId: 393865218
diff --git a/quic/core/http/web_transport_http3.cc b/quic/core/http/web_transport_http3.cc
index 771e3fa..e7015e2 100644
--- a/quic/core/http/web_transport_http3.cc
+++ b/quic/core/http/web_transport_http3.cc
@@ -4,9 +4,11 @@
 
 #include "quic/core/http/web_transport_http3.h"
 
+#include <limits>
 #include <memory>
 
 #include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
 #include "quic/core/http/quic_spdy_session.h"
 #include "quic/core/http/quic_spdy_stream.h"
 #include "quic/core/quic_data_reader.h"
@@ -243,17 +245,14 @@
 }
 
 WebTransportHttp3UnidirectionalStream::WebTransportHttp3UnidirectionalStream(
-    PendingStream* pending,
-    QuicSpdySession* session)
+    PendingStream* pending, QuicSpdySession* session)
     : QuicStream(pending, session, READ_UNIDIRECTIONAL, /*is_static=*/false),
       session_(session),
       adapter_(session, this, sequencer()),
       needs_to_send_preamble_(false) {}
 
 WebTransportHttp3UnidirectionalStream::WebTransportHttp3UnidirectionalStream(
-    QuicStreamId id,
-    QuicSpdySession* session,
-    WebTransportSessionId session_id)
+    QuicStreamId id, QuicSpdySession* session, WebTransportSessionId session_id)
     : QuicStream(id, session, /*is_static=*/false, WRITE_UNIDIRECTIONAL),
       session_(session),
       adapter_(session, this, sequencer()),
@@ -342,4 +341,33 @@
   session->OnStreamClosed(id());
 }
 
+namespace {
+constexpr uint64_t kWebTransportMappedErrorCodeFirst = 0x52e4a40fa8db;
+constexpr uint64_t kWebTransportMappedErrorCodeLast = 0x52e4a40fa9e2;
+}  // namespace
+
+absl::optional<WebTransportStreamError> Http3ErrorToWebTransport(
+    uint64_t http3_error_code) {
+  // Ensure the code is within the valid range.
+  if (http3_error_code < kWebTransportMappedErrorCodeFirst ||
+      http3_error_code > kWebTransportMappedErrorCodeLast) {
+    return absl::nullopt;
+  }
+  // Exclude GREASE codepoints.
+  if ((http3_error_code - 0x21) % 0x1f == 0) {
+    return absl::nullopt;
+  }
+
+  uint64_t shifted = http3_error_code - kWebTransportMappedErrorCodeFirst;
+  uint64_t result = shifted - shifted / 0x1f;
+  QUICHE_DCHECK_LE(result, std::numeric_limits<uint8_t>::max());
+  return result;
+}
+
+uint64_t WebTransportErrorToHttp3(
+    WebTransportStreamError webtransport_error_code) {
+  return kWebTransportMappedErrorCodeFirst + webtransport_error_code +
+         webtransport_error_code / 0x1e;
+}
+
 }  // namespace quic
diff --git a/quic/core/http/web_transport_http3.h b/quic/core/http/web_transport_http3.h
index df5c4f6..6b2c3cd 100644
--- a/quic/core/http/web_transport_http3.h
+++ b/quic/core/http/web_transport_http3.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "absl/base/attributes.h"
 #include "absl/container/flat_hash_set.h"
 #include "absl/types/optional.h"
 #include "quic/core/http/quic_spdy_session.h"
@@ -130,6 +131,15 @@
   void MaybeCloseIncompleteStream();
 };
 
+// Remaps HTTP/3 error code into a WebTransport error code.  Returns nullopt if
+// the provided code is outside of valid range.
+QUIC_EXPORT_PRIVATE absl::optional<WebTransportStreamError>
+Http3ErrorToWebTransport(uint64_t http3_error_code);
+
+// Remaps WebTransport error code into an HTTP/3 error code.
+QUIC_EXPORT_PRIVATE uint64_t
+WebTransportErrorToHttp3(WebTransportStreamError webtransport_error_code);
+
 }  // namespace quic
 
 #endif  // QUICHE_QUIC_CORE_HTTP_WEB_TRANSPORT_HTTP3_H_
diff --git a/quic/core/http/web_transport_http3_test.cc b/quic/core/http/web_transport_http3_test.cc
new file mode 100644
index 0000000..0cf5b94
--- /dev/null
+++ b/quic/core/http/web_transport_http3_test.cc
@@ -0,0 +1,52 @@
+// Copyright 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 "quic/core/http/web_transport_http3.h"
+
+#include <cstdint>
+#include <limits>
+
+#include "absl/types/optional.h"
+#include "quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace {
+
+using ::testing::Optional;
+
+TEST(WebTransportHttp3Test, ErrorCodesToHttp3) {
+  EXPECT_EQ(0x52e4a40fa8dbu, WebTransportErrorToHttp3(0x00));
+  EXPECT_EQ(0x52e4a40fa9e2u, WebTransportErrorToHttp3(0xff));
+
+  EXPECT_EQ(0x52e4a40fa8f7u, WebTransportErrorToHttp3(0x1c));
+  EXPECT_EQ(0x52e4a40fa8f8u, WebTransportErrorToHttp3(0x1d));
+  //        0x52e4a40fa8f9 is a GREASE codepoint
+  EXPECT_EQ(0x52e4a40fa8fau, WebTransportErrorToHttp3(0x1e));
+}
+
+TEST(WebTransportHttp3Test, ErrorCodesToWebTransport) {
+  EXPECT_THAT(Http3ErrorToWebTransport(0x52e4a40fa8db), Optional(0x00));
+  EXPECT_THAT(Http3ErrorToWebTransport(0x52e4a40fa9e2), Optional(0xff));
+
+  EXPECT_THAT(Http3ErrorToWebTransport(0x52e4a40fa8f7), Optional(0x1cu));
+  EXPECT_THAT(Http3ErrorToWebTransport(0x52e4a40fa8f8), Optional(0x1du));
+  EXPECT_THAT(Http3ErrorToWebTransport(0x52e4a40fa8f9), absl::nullopt);
+  EXPECT_THAT(Http3ErrorToWebTransport(0x52e4a40fa8fa), Optional(0x1eu));
+
+  EXPECT_EQ(Http3ErrorToWebTransport(0), absl::nullopt);
+  EXPECT_EQ(Http3ErrorToWebTransport(std::numeric_limits<uint64_t>::max()),
+            absl::nullopt);
+}
+
+TEST(WebTransportHttp3Test, ErrorCodeRoundTrip) {
+  for (int error = 0; error < 256; error++) {
+    uint64_t http_error = WebTransportErrorToHttp3(error);
+    absl::optional<WebTransportStreamError> mapped_back =
+        quic::Http3ErrorToWebTransport(http_error);
+    EXPECT_THAT(mapped_back, Optional(error));
+  }
+}
+
+}  // namespace
+}  // namespace quic
diff --git a/quic/core/quic_types.h b/quic/core/quic_types.h
index 2ac3d39..20927b2 100644
--- a/quic/core/quic_types.h
+++ b/quic/core/quic_types.h
@@ -56,6 +56,8 @@
 
 // WebTransport session IDs are stream IDs.
 using WebTransportSessionId = uint64_t;
+// WebTransport stream reset codes are 8-bit.
+using WebTransportStreamError = uint8_t;
 
 enum : size_t { kQuicPathFrameBufferSize = 8 };
 using QuicPathFrameBuffer = std::array<uint8_t, kQuicPathFrameBufferSize>;
@@ -79,8 +81,7 @@
   // default gtest object printer to read uninitialize memory. So we need
   // to teach gtest how to print this object.
   QUIC_EXPORT_PRIVATE friend std::ostream& operator<<(
-      std::ostream& os,
-      const QuicConsumedData& s);
+      std::ostream& os, const QuicConsumedData& s);
 
   // How many bytes were consumed.
   size_t bytes_consumed;
@@ -198,8 +199,7 @@
     TransmissionType transmission_type);
 
 QUIC_EXPORT_PRIVATE std::ostream& operator<<(
-    std::ostream& os,
-    TransmissionType transmission_type);
+    std::ostream& os, TransmissionType transmission_type);
 
 enum HasRetransmittableData : uint8_t {
   NO_RETRANSMITTABLE_DATA,
@@ -220,8 +220,7 @@
 QUIC_EXPORT_PRIVATE std::string ConnectionCloseSourceToString(
     ConnectionCloseSource connection_close_source);
 QUIC_EXPORT_PRIVATE std::ostream& operator<<(
-    std::ostream& os,
-    const ConnectionCloseSource& connection_close_source);
+    std::ostream& os, const ConnectionCloseSource& connection_close_source);
 
 // Should a connection be closed silently or not.
 enum class ConnectionCloseBehavior {
@@ -233,8 +232,7 @@
 QUIC_EXPORT_PRIVATE std::string ConnectionCloseBehaviorToString(
     ConnectionCloseBehavior connection_close_behavior);
 QUIC_EXPORT_PRIVATE std::ostream& operator<<(
-    std::ostream& os,
-    const ConnectionCloseBehavior& connection_close_behavior);
+    std::ostream& os, const ConnectionCloseBehavior& connection_close_behavior);
 
 enum QuicFrameType : uint8_t {
   // Regular frame types. The values set here cannot change without the
@@ -574,8 +572,7 @@
         receive_timestamp(receive_timestamp) {}
 
   friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
-      std::ostream& os,
-      const AckedPacket& acked_packet);
+      std::ostream& os, const AckedPacket& acked_packet);
 
   QuicPacketNumber packet_number;
   // Number of bytes sent in the packet that was acknowledged.
@@ -595,8 +592,7 @@
       : packet_number(packet_number), bytes_lost(bytes_lost) {}
 
   friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
-      std::ostream& os,
-      const LostPacket& lost_packet);
+      std::ostream& os, const LostPacket& lost_packet);
 
   QuicPacketNumber packet_number;
   // Number of bytes sent in the packet that was lost.
@@ -744,8 +740,7 @@
 };
 
 QUIC_EXPORT_PRIVATE std::ostream& operator<<(
-    std::ostream& os,
-    const QuicConnectionCloseType type);
+    std::ostream& os, const QuicConnectionCloseType type);
 
 QUIC_EXPORT_PRIVATE std::string QuicConnectionCloseTypeString(
     QuicConnectionCloseType type);