blob: d9319223b3fb2399da035874f62c351299df8d8c [file] [log] [blame]
// Copyright (c) 2023 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.
// A parser for draft-ietf-moq-transport-00.
#ifndef QUICHE_QUIC_MOQT_MOQT_PARSER_H_
#define QUICHE_QUIC_MOQT_MOQT_PARSER_H_
#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "quiche/quic/core/quic_data_reader.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/moqt/moqt_messages.h"
#include "quiche/common/platform/api/quiche_export.h"
namespace moqt {
class QUICHE_EXPORT MoqtParserVisitor {
public:
virtual ~MoqtParserVisitor() = default;
// If |end_of_message| is true, |payload| contains the last bytes of the
// OBJECT payload. If not, there will be subsequent calls with further payload
// data. The parser retains ownership of |message| and |payload|, so the
// visitor needs to copy anything it wants to retain.
virtual void OnObjectMessage(const MoqtObject& message,
absl::string_view payload,
bool end_of_message) = 0;
// All of these are called only when the entire specified message length has
// arrived, which requires a stream FIN if the length is zero. The parser
// retains ownership of the memory.
virtual void OnSetupMessage(const MoqtSetup& message) = 0;
virtual void OnSubscribeRequestMessage(
const MoqtSubscribeRequest& message) = 0;
virtual void OnSubscribeOkMessage(const MoqtSubscribeOk& message) = 0;
virtual void OnSubscribeErrorMessage(const MoqtSubscribeError& message) = 0;
virtual void OnAnnounceMessage(const MoqtAnnounce& message) = 0;
virtual void OnAnnounceOkMessage(const MoqtAnnounceOk& message) = 0;
virtual void OnAnnounceErrorMessage(const MoqtAnnounceError& message) = 0;
// In an exception to the above, the parser calls this when it gets two bytes,
// whether or not it includes stream FIN. When a zero-length message has
// special meaning, a message with an actual length of zero is tricky!
virtual void OnGoAwayMessage() = 0;
virtual void OnParsingError(absl::string_view reason) = 0;
};
class QUICHE_EXPORT MoqtParser {
public:
MoqtParser(quic::Perspective perspective, bool uses_web_transport,
MoqtParserVisitor& visitor)
: visitor_(visitor),
perspective_(perspective),
uses_web_transport_(uses_web_transport) {}
~MoqtParser() = default;
// Take a buffer from the transport in |data|. Parse each complete message and
// call the appropriate visitor function. If |end_of_stream| is true, there
// is no more data arriving on the stream, so the parser will deliver any
// message encoded as to run to the end of the stream.
// All bytes can be freed. Calls OnParsingError() when there is a parsing
// error.
// Any calls after sending |end_of_stream| = true will be ignored.
void ProcessData(absl::string_view data, bool end_of_stream);
private:
// Copies the minimum amount of data in |reader| to buffered_message_ in order
// to process what is in there, and does the processing. Returns true if
// additional processing can occur, false otherwise.
bool MaybeMergeDataWithBuffer(quic::QuicDataReader& reader,
bool end_of_stream);
// The central switch statement to dispatch a message to the correct
// Process* function. Returns nullopt if it could not parse the full messsage
// (except for object payload). Otherwise, returns the number of bytes
// processed.
absl::optional<size_t> ProcessMessage(absl::string_view data);
// A helper function to parse just the varints in an OBJECT.
absl::optional<size_t> ProcessObjectVarints(absl::string_view data);
// The Process* functions parse the serialized data into the appropriate
// structs, and call the relevant visitor function for further action. Returns
// the number of bytes consumed if the message is complete; returns nullopt
// otherwise. These functions can throw a fatal error if the message length
// is insufficient.
absl::optional<size_t> ProcessObject(absl::string_view data);
absl::optional<size_t> ProcessSetup(absl::string_view data);
absl::optional<size_t> ProcessSubscribeRequest(absl::string_view data);
absl::optional<size_t> ProcessSubscribeOk(absl::string_view data);
absl::optional<size_t> ProcessSubscribeError(absl::string_view data);
absl::optional<size_t> ProcessAnnounce(absl::string_view data);
absl::optional<size_t> ProcessAnnounceOk(absl::string_view data);
absl::optional<size_t> ProcessAnnounceError(absl::string_view data);
absl::optional<size_t> ProcessGoAway(absl::string_view data);
// If the message length field is zero, it runs to the end of the stream.
bool NoMessageLength() { return *message_length_ == 0; }
// If type and or length are not already stored for this message, reads it out
// of the data in |reader| and stores it in the appropriate members. Returns
// false if length is not available.
bool GetMessageTypeAndLength(quic::QuicDataReader& reader);
void EndOfMessage();
// Get a string_view of the part of the reader covered by message_length_,
// with exceptions for OBJECT messages.
absl::string_view FetchMessage(quic::QuicDataReader& reader);
void ParseError(absl::string_view reason);
// Reads an integer whose length is specified by a preceding VarInt62 and
// returns it in |result|. Returns false if parsing fails.
bool ReadIntegerPieceVarInt62(quic::QuicDataReader& reader, uint64_t& result);
MoqtParserVisitor& visitor_;
// Client or server?
quic::Perspective perspective_;
bool uses_web_transport_;
bool no_more_data_ = false; // Fatal error or end_of_stream. No more parsing.
bool parsing_error_ = false;
std::string buffered_message_;
absl::optional<MoqtMessageType> message_type_ = absl::nullopt;
absl::optional<size_t> message_length_ = absl::nullopt;
// Metadata for an object which is delivered in parts.
absl::optional<MoqtObject> object_metadata_ = absl::nullopt;
bool processing_ = false; // True if currently in ProcessData(), to prevent
// re-entrancy.
};
} // namespace moqt
#endif // QUICHE_QUIC_MOQT_MOQT_PARSER_H_