xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/common/capsule.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright (c) 2021 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef QUICHE_COMMON_CAPSULE_H_
6 #define QUICHE_COMMON_CAPSULE_H_
7 
8 #include <cstdint>
9 #include <optional>
10 #include <string>
11 #include <vector>
12 
13 #include "absl/status/statusor.h"
14 #include "absl/strings/string_view.h"
15 #include "absl/types/variant.h"
16 #include "quiche/common/platform/api/quiche_export.h"
17 #include "quiche/common/platform/api/quiche_logging.h"
18 #include "quiche/common/quiche_buffer_allocator.h"
19 #include "quiche/common/quiche_ip_address.h"
20 #include "quiche/web_transport/web_transport.h"
21 
22 namespace quiche {
23 
24 enum class CapsuleType : uint64_t {
25   // Casing in this enum matches the IETF specifications.
26   DATAGRAM = 0x00,             // RFC 9297.
27   LEGACY_DATAGRAM = 0xff37a0,  // draft-ietf-masque-h3-datagram-04.
28   LEGACY_DATAGRAM_WITHOUT_CONTEXT =
29       0xff37a5,  // draft-ietf-masque-h3-datagram-05 to -08.
30 
31   // <https://datatracker.ietf.org/doc/draft-ietf-webtrans-http3/>
32   CLOSE_WEBTRANSPORT_SESSION = 0x2843,
33   DRAIN_WEBTRANSPORT_SESSION = 0x78ae,
34 
35   // draft-ietf-masque-connect-ip-03.
36   ADDRESS_ASSIGN = 0x1ECA6A00,
37   ADDRESS_REQUEST = 0x1ECA6A01,
38   ROUTE_ADVERTISEMENT = 0x1ECA6A02,
39 
40   // <https://ietf-wg-webtrans.github.io/draft-webtransport-http2/draft-ietf-webtrans-http2.html#name-webtransport-capsules>
41   WT_RESET_STREAM = 0x190b4d39,
42   WT_STOP_SENDING = 0x190b4d3a,
43   WT_STREAM = 0x190b4d3b,
44   WT_STREAM_WITH_FIN = 0x190b4d3c,
45   // Should be removed as a result of
46   // <https://github.com/ietf-wg-webtrans/draft-webtransport-http2/issues/27>.
47   // WT_MAX_DATA = 0x190b4d3d,
48   WT_MAX_STREAM_DATA = 0x190b4d3e,
49   WT_MAX_STREAMS_BIDI = 0x190b4d3f,
50   WT_MAX_STREAMS_UNIDI = 0x190b4d40,
51 
52   // TODO(b/264263113): implement those.
53   // PADDING = 0x190b4d38,
54   // WT_DATA_BLOCKED = 0x190b4d41,
55   // WT_STREAM_DATA_BLOCKED = 0x190b4d42,
56   // WT_STREAMS_BLOCKED_BIDI = 0x190b4d43,
57   // WT_STREAMS_BLOCKED_UNIDI = 0x190b4d44,
58 };
59 
60 QUICHE_EXPORT std::string CapsuleTypeToString(CapsuleType capsule_type);
61 QUICHE_EXPORT std::ostream& operator<<(std::ostream& os,
62                                        const CapsuleType& capsule_type);
63 
64 // General.
65 struct QUICHE_EXPORT DatagramCapsule {
66   absl::string_view http_datagram_payload;
67 
68   std::string ToString() const;
capsule_typeDatagramCapsule69   CapsuleType capsule_type() const { return CapsuleType::DATAGRAM; }
70   bool operator==(const DatagramCapsule& other) const {
71     return http_datagram_payload == other.http_datagram_payload;
72   }
73 };
74 
75 struct QUICHE_EXPORT LegacyDatagramCapsule {
76   absl::string_view http_datagram_payload;
77 
78   std::string ToString() const;
capsule_typeLegacyDatagramCapsule79   CapsuleType capsule_type() const { return CapsuleType::LEGACY_DATAGRAM; }
80   bool operator==(const LegacyDatagramCapsule& other) const {
81     return http_datagram_payload == other.http_datagram_payload;
82   }
83 };
84 
85 struct QUICHE_EXPORT LegacyDatagramWithoutContextCapsule {
86   absl::string_view http_datagram_payload;
87 
88   std::string ToString() const;
capsule_typeLegacyDatagramWithoutContextCapsule89   CapsuleType capsule_type() const {
90     return CapsuleType::LEGACY_DATAGRAM_WITHOUT_CONTEXT;
91   }
92   bool operator==(const LegacyDatagramWithoutContextCapsule& other) const {
93     return http_datagram_payload == other.http_datagram_payload;
94   }
95 };
96 
97 // WebTransport over HTTP/3.
98 struct QUICHE_EXPORT CloseWebTransportSessionCapsule {
99   webtransport::SessionErrorCode error_code;
100   absl::string_view error_message;
101 
102   std::string ToString() const;
capsule_typeCloseWebTransportSessionCapsule103   CapsuleType capsule_type() const {
104     return CapsuleType::CLOSE_WEBTRANSPORT_SESSION;
105   }
106   bool operator==(const CloseWebTransportSessionCapsule& other) const {
107     return error_code == other.error_code &&
108            error_message == other.error_message;
109   }
110 };
111 struct QUICHE_EXPORT DrainWebTransportSessionCapsule {
112   std::string ToString() const;
capsule_typeDrainWebTransportSessionCapsule113   CapsuleType capsule_type() const {
114     return CapsuleType::DRAIN_WEBTRANSPORT_SESSION;
115   }
116   bool operator==(const DrainWebTransportSessionCapsule&) const { return true; }
117 };
118 
119 // MASQUE CONNECT-IP.
120 struct QUICHE_EXPORT PrefixWithId {
121   uint64_t request_id;
122   quiche::QuicheIpPrefix ip_prefix;
123   bool operator==(const PrefixWithId& other) const;
124 };
125 struct QUICHE_EXPORT IpAddressRange {
126   quiche::QuicheIpAddress start_ip_address;
127   quiche::QuicheIpAddress end_ip_address;
128   uint8_t ip_protocol;
129   bool operator==(const IpAddressRange& other) const;
130 };
131 
132 struct QUICHE_EXPORT AddressAssignCapsule {
133   std::vector<PrefixWithId> assigned_addresses;
134   bool operator==(const AddressAssignCapsule& other) const;
135   std::string ToString() const;
capsule_typeAddressAssignCapsule136   CapsuleType capsule_type() const { return CapsuleType::ADDRESS_ASSIGN; }
137 };
138 struct QUICHE_EXPORT AddressRequestCapsule {
139   std::vector<PrefixWithId> requested_addresses;
140   bool operator==(const AddressRequestCapsule& other) const;
141   std::string ToString() const;
capsule_typeAddressRequestCapsule142   CapsuleType capsule_type() const { return CapsuleType::ADDRESS_REQUEST; }
143 };
144 struct QUICHE_EXPORT RouteAdvertisementCapsule {
145   std::vector<IpAddressRange> ip_address_ranges;
146   bool operator==(const RouteAdvertisementCapsule& other) const;
147   std::string ToString() const;
capsule_typeRouteAdvertisementCapsule148   CapsuleType capsule_type() const { return CapsuleType::ROUTE_ADVERTISEMENT; }
149 };
150 struct QUICHE_EXPORT UnknownCapsule {
151   uint64_t type;
152   absl::string_view payload;
153 
154   std::string ToString() const;
capsule_typeUnknownCapsule155   CapsuleType capsule_type() const { return static_cast<CapsuleType>(type); }
156   bool operator==(const UnknownCapsule& other) const {
157     return type == other.type && payload == other.payload;
158   }
159 };
160 
161 // WebTransport over HTTP/2.
162 struct QUICHE_EXPORT WebTransportStreamDataCapsule {
163   webtransport::StreamId stream_id;
164   absl::string_view data;
165   bool fin;
166 
167   bool operator==(const WebTransportStreamDataCapsule& other) const;
168   std::string ToString() const;
capsule_typeWebTransportStreamDataCapsule169   CapsuleType capsule_type() const {
170     return fin ? CapsuleType::WT_STREAM_WITH_FIN : CapsuleType::WT_STREAM;
171   }
172 };
173 struct QUICHE_EXPORT WebTransportResetStreamCapsule {
174   webtransport::StreamId stream_id;
175   uint64_t error_code;
176 
177   bool operator==(const WebTransportResetStreamCapsule& other) const;
178   std::string ToString() const;
capsule_typeWebTransportResetStreamCapsule179   CapsuleType capsule_type() const { return CapsuleType::WT_RESET_STREAM; }
180 };
181 struct QUICHE_EXPORT WebTransportStopSendingCapsule {
182   webtransport::StreamId stream_id;
183   uint64_t error_code;
184 
185   bool operator==(const WebTransportStopSendingCapsule& other) const;
186   std::string ToString() const;
capsule_typeWebTransportStopSendingCapsule187   CapsuleType capsule_type() const { return CapsuleType::WT_STOP_SENDING; }
188 };
189 struct QUICHE_EXPORT WebTransportMaxStreamDataCapsule {
190   webtransport::StreamId stream_id;
191   uint64_t max_stream_data;
192 
193   bool operator==(const WebTransportMaxStreamDataCapsule& other) const;
194   std::string ToString() const;
capsule_typeWebTransportMaxStreamDataCapsule195   CapsuleType capsule_type() const { return CapsuleType::WT_MAX_STREAM_DATA; }
196 };
197 struct QUICHE_EXPORT WebTransportMaxStreamsCapsule {
198   webtransport::StreamType stream_type;
199   uint64_t max_stream_count;
200 
201   bool operator==(const WebTransportMaxStreamsCapsule& other) const;
202   std::string ToString() const;
capsule_typeWebTransportMaxStreamsCapsule203   CapsuleType capsule_type() const {
204     return stream_type == webtransport::StreamType::kBidirectional
205                ? CapsuleType::WT_MAX_STREAMS_BIDI
206                : CapsuleType::WT_MAX_STREAMS_UNIDI;
207   }
208 };
209 
210 // Capsule from RFC 9297.
211 // IMPORTANT NOTE: Capsule does not own any of the absl::string_view memory it
212 // points to. Strings saved into a capsule must outlive the capsule object. Any
213 // code that sees a capsule in a callback needs to either process it immediately
214 // or perform its own deep copy.
215 class QUICHE_EXPORT Capsule {
216  public:
217   static Capsule Datagram(
218       absl::string_view http_datagram_payload = absl::string_view());
219   static Capsule LegacyDatagram(
220       absl::string_view http_datagram_payload = absl::string_view());
221   static Capsule LegacyDatagramWithoutContext(
222       absl::string_view http_datagram_payload = absl::string_view());
223   static Capsule CloseWebTransportSession(
224       webtransport::SessionErrorCode error_code = 0,
225       absl::string_view error_message = "");
226   static Capsule AddressRequest();
227   static Capsule AddressAssign();
228   static Capsule RouteAdvertisement();
229   static Capsule Unknown(
230       uint64_t capsule_type,
231       absl::string_view unknown_capsule_data = absl::string_view());
232 
233   template <typename CapsuleStruct>
Capsule(CapsuleStruct capsule)234   explicit Capsule(CapsuleStruct capsule) : capsule_(std::move(capsule)) {}
235   bool operator==(const Capsule& other) const;
236 
237   // Human-readable information string for debugging purposes.
238   std::string ToString() const;
239   friend QUICHE_EXPORT std::ostream& operator<<(std::ostream& os,
240                                                 const Capsule& capsule);
241 
capsule_type()242   CapsuleType capsule_type() const {
243     return absl::visit(
244         [](const auto& capsule) { return capsule.capsule_type(); }, capsule_);
245   }
datagram_capsule()246   DatagramCapsule& datagram_capsule() {
247     return absl::get<DatagramCapsule>(capsule_);
248   }
datagram_capsule()249   const DatagramCapsule& datagram_capsule() const {
250     return absl::get<DatagramCapsule>(capsule_);
251   }
legacy_datagram_capsule()252   LegacyDatagramCapsule& legacy_datagram_capsule() {
253     return absl::get<LegacyDatagramCapsule>(capsule_);
254   }
legacy_datagram_capsule()255   const LegacyDatagramCapsule& legacy_datagram_capsule() const {
256     return absl::get<LegacyDatagramCapsule>(capsule_);
257   }
258   LegacyDatagramWithoutContextCapsule&
legacy_datagram_without_context_capsule()259   legacy_datagram_without_context_capsule() {
260     return absl::get<LegacyDatagramWithoutContextCapsule>(capsule_);
261   }
262   const LegacyDatagramWithoutContextCapsule&
legacy_datagram_without_context_capsule()263   legacy_datagram_without_context_capsule() const {
264     return absl::get<LegacyDatagramWithoutContextCapsule>(capsule_);
265   }
close_web_transport_session_capsule()266   CloseWebTransportSessionCapsule& close_web_transport_session_capsule() {
267     return absl::get<CloseWebTransportSessionCapsule>(capsule_);
268   }
close_web_transport_session_capsule()269   const CloseWebTransportSessionCapsule& close_web_transport_session_capsule()
270       const {
271     return absl::get<CloseWebTransportSessionCapsule>(capsule_);
272   }
address_request_capsule()273   AddressRequestCapsule& address_request_capsule() {
274     return absl::get<AddressRequestCapsule>(capsule_);
275   }
address_request_capsule()276   const AddressRequestCapsule& address_request_capsule() const {
277     return absl::get<AddressRequestCapsule>(capsule_);
278   }
address_assign_capsule()279   AddressAssignCapsule& address_assign_capsule() {
280     return absl::get<AddressAssignCapsule>(capsule_);
281   }
address_assign_capsule()282   const AddressAssignCapsule& address_assign_capsule() const {
283     return absl::get<AddressAssignCapsule>(capsule_);
284   }
route_advertisement_capsule()285   RouteAdvertisementCapsule& route_advertisement_capsule() {
286     return absl::get<RouteAdvertisementCapsule>(capsule_);
287   }
route_advertisement_capsule()288   const RouteAdvertisementCapsule& route_advertisement_capsule() const {
289     return absl::get<RouteAdvertisementCapsule>(capsule_);
290   }
web_transport_stream_data()291   WebTransportStreamDataCapsule& web_transport_stream_data() {
292     return absl::get<WebTransportStreamDataCapsule>(capsule_);
293   }
web_transport_stream_data()294   const WebTransportStreamDataCapsule& web_transport_stream_data() const {
295     return absl::get<WebTransportStreamDataCapsule>(capsule_);
296   }
web_transport_reset_stream()297   WebTransportResetStreamCapsule& web_transport_reset_stream() {
298     return absl::get<WebTransportResetStreamCapsule>(capsule_);
299   }
web_transport_reset_stream()300   const WebTransportResetStreamCapsule& web_transport_reset_stream() const {
301     return absl::get<WebTransportResetStreamCapsule>(capsule_);
302   }
web_transport_stop_sending()303   WebTransportStopSendingCapsule& web_transport_stop_sending() {
304     return absl::get<WebTransportStopSendingCapsule>(capsule_);
305   }
web_transport_stop_sending()306   const WebTransportStopSendingCapsule& web_transport_stop_sending() const {
307     return absl::get<WebTransportStopSendingCapsule>(capsule_);
308   }
web_transport_max_stream_data()309   WebTransportMaxStreamDataCapsule& web_transport_max_stream_data() {
310     return absl::get<WebTransportMaxStreamDataCapsule>(capsule_);
311   }
web_transport_max_stream_data()312   const WebTransportMaxStreamDataCapsule& web_transport_max_stream_data()
313       const {
314     return absl::get<WebTransportMaxStreamDataCapsule>(capsule_);
315   }
web_transport_max_streams()316   WebTransportMaxStreamsCapsule& web_transport_max_streams() {
317     return absl::get<WebTransportMaxStreamsCapsule>(capsule_);
318   }
web_transport_max_streams()319   const WebTransportMaxStreamsCapsule& web_transport_max_streams() const {
320     return absl::get<WebTransportMaxStreamsCapsule>(capsule_);
321   }
unknown_capsule()322   UnknownCapsule& unknown_capsule() {
323     return absl::get<UnknownCapsule>(capsule_);
324   }
unknown_capsule()325   const UnknownCapsule& unknown_capsule() const {
326     return absl::get<UnknownCapsule>(capsule_);
327   }
328 
329  private:
330   absl::variant<DatagramCapsule, LegacyDatagramCapsule,
331                 LegacyDatagramWithoutContextCapsule,
332                 CloseWebTransportSessionCapsule,
333                 DrainWebTransportSessionCapsule, AddressRequestCapsule,
334                 AddressAssignCapsule, RouteAdvertisementCapsule,
335                 WebTransportStreamDataCapsule, WebTransportResetStreamCapsule,
336                 WebTransportStopSendingCapsule, WebTransportMaxStreamsCapsule,
337                 WebTransportMaxStreamDataCapsule, UnknownCapsule>
338       capsule_;
339 };
340 
341 namespace test {
342 class CapsuleParserPeer;
343 }  // namespace test
344 
345 class QUICHE_EXPORT CapsuleParser {
346  public:
347   class QUICHE_EXPORT Visitor {
348    public:
~Visitor()349     virtual ~Visitor() {}
350 
351     // Called when a capsule has been successfully parsed. The return value
352     // indicates whether the contents of the capsule are valid: if false is
353     // returned, the parse operation will be considered failed and
354     // OnCapsuleParseFailure will be called. Note that since Capsule does not
355     // own the memory backing its string_views, that memory is only valid until
356     // this callback returns. Visitors that wish to access the capsule later
357     // MUST make a deep copy before this returns.
358     virtual bool OnCapsule(const Capsule& capsule) = 0;
359 
360     virtual void OnCapsuleParseFailure(absl::string_view error_message) = 0;
361   };
362 
363   // |visitor| must be non-null, and must outlive CapsuleParser.
364   explicit CapsuleParser(Visitor* visitor);
365 
366   // Ingests a capsule fragment (any fragment of bytes from the capsule data
367   // stream) and parses and complete capsules it encounters. Returns false if a
368   // parsing error occurred.
369   bool IngestCapsuleFragment(absl::string_view capsule_fragment);
370 
371   void ErrorIfThereIsRemainingBufferedData();
372 
373   friend class test::CapsuleParserPeer;
374 
375  private:
376   // Attempts to parse a single capsule from |buffered_data_|. If a full capsule
377   // is not available, returns 0. If a parsing error occurs, returns an error.
378   // Otherwise, returns the number of bytes in the parsed capsule.
379   absl::StatusOr<size_t> AttemptParseCapsule();
380   void ReportParseFailure(absl::string_view error_message);
381 
382   // Whether a parsing error has occurred.
383   bool parsing_error_occurred_ = false;
384   // Visitor which will receive callbacks, unowned.
385   Visitor* visitor_;
386 
387   std::string buffered_data_;
388 };
389 
390 // Serializes |capsule| into a newly allocated buffer.
391 QUICHE_EXPORT quiche::QuicheBuffer SerializeCapsule(
392     const Capsule& capsule, quiche::QuicheBufferAllocator* allocator);
393 
394 // Serializes the header for a datagram of size |datagram_size|.
395 QUICHE_EXPORT QuicheBuffer SerializeDatagramCapsuleHeader(
396     uint64_t datagram_size, QuicheBufferAllocator* allocator);
397 
398 // Serializes the header for a WT_STREAM or a WT_STREAM_WITH_FIN capsule.
399 QUICHE_EXPORT QuicheBuffer SerializeWebTransportStreamCapsuleHeader(
400     webtransport::StreamId stream_id, bool fin, uint64_t write_size,
401     QuicheBufferAllocator* allocator);
402 
403 }  // namespace quiche
404 
405 #endif  // QUICHE_COMMON_CAPSULE_H_
406