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);