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