1 // Copyright (c) 2023 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 // A parser for draft-ietf-moq-transport-01. 6 7 #ifndef QUICHE_QUIC_MOQT_MOQT_PARSER_H_ 8 #define QUICHE_QUIC_MOQT_MOQT_PARSER_H_ 9 10 #include <cstddef> 11 #include <cstdint> 12 #include <optional> 13 #include <string> 14 15 #include "absl/strings/string_view.h" 16 #include "quiche/quic/core/quic_data_reader.h" 17 #include "quiche/quic/moqt/moqt_messages.h" 18 #include "quiche/common/platform/api/quiche_export.h" 19 20 namespace moqt { 21 22 class QUICHE_EXPORT MoqtParserVisitor { 23 public: 24 virtual ~MoqtParserVisitor() = default; 25 26 // If |end_of_message| is true, |payload| contains the last bytes of the 27 // OBJECT payload. If not, there will be subsequent calls with further payload 28 // data. The parser retains ownership of |message| and |payload|, so the 29 // visitor needs to copy anything it wants to retain. 30 virtual void OnObjectMessage(const MoqtObject& message, 31 absl::string_view payload, 32 bool end_of_message) = 0; 33 // All of these are called only when the entire message has arrived. The 34 // parser retains ownership of the memory. 35 virtual void OnClientSetupMessage(const MoqtClientSetup& message) = 0; 36 virtual void OnServerSetupMessage(const MoqtServerSetup& message) = 0; 37 virtual void OnSubscribeMessage(const MoqtSubscribe& message) = 0; 38 virtual void OnSubscribeOkMessage(const MoqtSubscribeOk& message) = 0; 39 virtual void OnSubscribeErrorMessage(const MoqtSubscribeError& message) = 0; 40 virtual void OnUnsubscribeMessage(const MoqtUnsubscribe& message) = 0; 41 virtual void OnSubscribeDoneMessage(const MoqtSubscribeDone& message) = 0; 42 virtual void OnAnnounceMessage(const MoqtAnnounce& message) = 0; 43 virtual void OnAnnounceOkMessage(const MoqtAnnounceOk& message) = 0; 44 virtual void OnAnnounceErrorMessage(const MoqtAnnounceError& message) = 0; 45 virtual void OnUnannounceMessage(const MoqtUnannounce& message) = 0; 46 virtual void OnGoAwayMessage(const MoqtGoAway& message) = 0; 47 48 virtual void OnParsingError(MoqtError code, absl::string_view reason) = 0; 49 }; 50 51 class QUICHE_EXPORT MoqtParser { 52 public: MoqtParser(bool uses_web_transport,MoqtParserVisitor & visitor)53 MoqtParser(bool uses_web_transport, MoqtParserVisitor& visitor) 54 : visitor_(visitor), uses_web_transport_(uses_web_transport) {} 55 ~MoqtParser() = default; 56 57 // Take a buffer from the transport in |data|. Parse each complete message and 58 // call the appropriate visitor function. If |fin| is true, there 59 // is no more data arriving on the stream, so the parser will deliver any 60 // message encoded as to run to the end of the stream. 61 // All bytes can be freed. Calls OnParsingError() when there is a parsing 62 // error. 63 // Any calls after sending |fin| = true will be ignored. 64 // TODO(martinduke): Figure out what has to happen if the message arrives via 65 // datagram rather than a stream. 66 void ProcessData(absl::string_view data, bool fin); 67 68 // Provide a separate path for datagrams. Returns the payload bytes, or empty 69 // string_view on error. The caller provides the whole datagram in |data|. 70 // The function puts the object metadata in |object_metadata|. 71 static absl::string_view ProcessDatagram(absl::string_view data, 72 MoqtObject& object_metadata); 73 74 private: 75 // The central switch statement to dispatch a message to the correct 76 // Process* function. Returns 0 if it could not parse the full messsage 77 // (except for object payload). Otherwise, returns the number of bytes 78 // processed. 79 size_t ProcessMessage(absl::string_view data, bool fin); 80 // The Process* functions parse the serialized data into the appropriate 81 // structs, and call the relevant visitor function for further action. Returns 82 // the number of bytes consumed if the message is complete; returns 0 83 // otherwise. 84 size_t ProcessObject(quic::QuicDataReader& reader, MoqtMessageType type, 85 bool fin); 86 size_t ProcessClientSetup(quic::QuicDataReader& reader); 87 size_t ProcessServerSetup(quic::QuicDataReader& reader); 88 size_t ProcessSubscribe(quic::QuicDataReader& reader); 89 size_t ProcessSubscribeOk(quic::QuicDataReader& reader); 90 size_t ProcessSubscribeError(quic::QuicDataReader& reader); 91 size_t ProcessUnsubscribe(quic::QuicDataReader& reader); 92 size_t ProcessSubscribeDone(quic::QuicDataReader& reader); 93 size_t ProcessAnnounce(quic::QuicDataReader& reader); 94 size_t ProcessAnnounceOk(quic::QuicDataReader& reader); 95 size_t ProcessAnnounceError(quic::QuicDataReader& reader); 96 size_t ProcessUnannounce(quic::QuicDataReader& reader); 97 size_t ProcessGoAway(quic::QuicDataReader& reader); 98 99 static size_t ParseObjectHeader(quic::QuicDataReader& reader, 100 MoqtObject& object, MoqtMessageType type); 101 102 // If |error| is not provided, assumes kProtocolViolation. 103 void ParseError(absl::string_view reason); 104 void ParseError(MoqtError error, absl::string_view reason); 105 106 // Reads an integer whose length is specified by a preceding VarInt62 and 107 // returns it in |result|. Returns false if parsing fails. 108 bool ReadVarIntPieceVarInt62(quic::QuicDataReader& reader, uint64_t& result); 109 // Read a Location field from SUBSCRIBE REQUEST 110 bool ReadLocation(quic::QuicDataReader& reader, 111 std::optional<MoqtSubscribeLocation>& loc); 112 // Read a parameter and return the value as a string_view. Returns false if 113 // |reader| does not have enough data. 114 bool ReadParameter(quic::QuicDataReader& reader, uint64_t& type, 115 absl::string_view& value); 116 // Convert a string view to a varint. Throws an error and returns false if the 117 // string_view is not exactly the right length. 118 bool StringViewToVarInt(absl::string_view& sv, uint64_t& vi); 119 120 // Simplify understanding of state. 121 // Returns true if the stream has delivered all object metadata common to all 122 // objects on that stream. ObjectStreamInitialized()123 bool ObjectStreamInitialized() const { return object_metadata_.has_value(); } 124 // Returns true if the stream has delivered all metadata but not all payload 125 // for the most recent object. ObjectPayloadInProgress()126 bool ObjectPayloadInProgress() const { 127 return (object_metadata_.has_value() && 128 (object_metadata_->forwarding_preference == 129 MoqtForwardingPreference::kObject || 130 object_metadata_->forwarding_preference == 131 MoqtForwardingPreference::kDatagram || 132 payload_length_remaining_ > 0)); 133 } 134 135 MoqtParserVisitor& visitor_; 136 bool uses_web_transport_; 137 bool no_more_data_ = false; // Fatal error or fin. No more parsing. 138 bool parsing_error_ = false; 139 140 std::string buffered_message_; 141 142 // Metadata for an object which is delivered in parts. 143 // If object_metadata_ is nullopt, nothing has been processed on the stream. 144 // If object_metadata_ exists but payload_length is nullopt or 145 // payload_length_remaining_ is nonzero, the object payload is in mid- 146 // delivery. 147 // If object_metadata_ exists and payload_length_remaining_ is zero, an object 148 // has been completely delivered and the next object header on the stream has 149 // not been delivered. 150 // Use ObjectStreamInitialized() and ObjectPayloadInProgress() to keep the 151 // state straight. 152 std::optional<MoqtObject> object_metadata_ = std::nullopt; 153 size_t payload_length_remaining_ = 0; 154 155 bool processing_ = false; // True if currently in ProcessData(), to prevent 156 // re-entrancy. 157 }; 158 159 } // namespace moqt 160 161 #endif // QUICHE_QUIC_MOQT_MOQT_PARSER_H_ 162