blob: 4d415b6fa79f0cbabb431c700858ed1ddbe9023a [file] [log] [blame]
// Copyright (c) 2018 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.
#ifndef QUICHE_QUIC_CORE_HTTP_HTTP_DECODER_H_
#define QUICHE_QUIC_CORE_HTTP_HTTP_DECODER_H_
#include <cstdint>
#include "absl/strings/string_view.h"
#include "quic/core/http/http_frames.h"
#include "quic/core/quic_error_codes.h"
#include "quic/core/quic_types.h"
#include "quic/platform/api/quic_export.h"
namespace quic {
namespace test {
class HttpDecoderPeer;
} // namespace test
class QuicDataReader;
// A class for decoding the HTTP frames that are exchanged in an HTTP over QUIC
// session.
class QUIC_EXPORT_PRIVATE HttpDecoder {
public:
struct QUIC_EXPORT_PRIVATE Options {
// Indicates that WEBTRANSPORT_STREAM should be parsed.
bool allow_web_transport_stream = false;
};
class QUIC_EXPORT_PRIVATE Visitor {
public:
virtual ~Visitor() {}
// Called if an error is detected.
virtual void OnError(HttpDecoder* decoder) = 0;
// All the following methods return true to continue decoding,
// and false to pause it.
// On*FrameStart() methods are called after the frame header is completely
// processed. At that point it is safe to consume |header_length| bytes.
// Called when a CANCEL_PUSH frame has been successfully parsed.
// TODO(b/171463363): Remove.
virtual bool OnCancelPushFrame(const CancelPushFrame& frame) = 0;
// Called when a MAX_PUSH_ID frame has been successfully parsed.
virtual bool OnMaxPushIdFrame(const MaxPushIdFrame& frame) = 0;
// Called when a GOAWAY frame has been successfully parsed.
virtual bool OnGoAwayFrame(const GoAwayFrame& frame) = 0;
// Called when a SETTINGS frame has been received.
virtual bool OnSettingsFrameStart(QuicByteCount header_length) = 0;
// Called when a SETTINGS frame has been successfully parsed.
virtual bool OnSettingsFrame(const SettingsFrame& frame) = 0;
// Called when a DATA frame has been received.
// |header_length| and |payload_length| are the length of DATA frame header
// and payload, respectively.
virtual bool OnDataFrameStart(QuicByteCount header_length,
QuicByteCount payload_length) = 0;
// Called when part of the payload of a DATA frame has been read. May be
// called multiple times for a single frame. |payload| is guaranteed to be
// non-empty.
virtual bool OnDataFramePayload(absl::string_view payload) = 0;
// Called when a DATA frame has been completely processed.
virtual bool OnDataFrameEnd() = 0;
// Called when a HEADERS frame has been received.
// |header_length| and |payload_length| are the length of HEADERS frame
// header and payload, respectively.
virtual bool OnHeadersFrameStart(QuicByteCount header_length,
QuicByteCount payload_length) = 0;
// Called when part of the payload of a HEADERS frame has been read. May be
// called multiple times for a single frame. |payload| is guaranteed to be
// non-empty.
virtual bool OnHeadersFramePayload(absl::string_view payload) = 0;
// Called when a HEADERS frame has been completely processed.
virtual bool OnHeadersFrameEnd() = 0;
// TODO(b/171463363): Remove all.
// Called when a PUSH_PROMISE frame has been received.
virtual bool OnPushPromiseFrameStart(QuicByteCount header_length) = 0;
// Called when the Push ID field of a PUSH_PROMISE frame has been parsed.
// Called exactly once for a valid PUSH_PROMISE frame.
// |push_id_length| is the length of the push ID field.
// |header_block_length| is the length of the compressed header block.
virtual bool OnPushPromiseFramePushId(
PushId push_id,
QuicByteCount push_id_length,
QuicByteCount header_block_length) = 0;
// Called when part of the header block of a PUSH_PROMISE frame has been
// read. May be called multiple times for a single frame. |payload| is
// guaranteed to be non-empty.
virtual bool OnPushPromiseFramePayload(absl::string_view payload) = 0;
// Called when a PUSH_PROMISE frame has been completely processed.
virtual bool OnPushPromiseFrameEnd() = 0;
// Called when a PRIORITY_UPDATE frame has been received.
// |header_length| contains PRIORITY_UPDATE frame length and payload length.
virtual bool OnPriorityUpdateFrameStart(QuicByteCount header_length) = 0;
// Called when a PRIORITY_UPDATE frame has been successfully parsed.
virtual bool OnPriorityUpdateFrame(const PriorityUpdateFrame& frame) = 0;
// Called when an ACCEPT_CH frame has been received.
// |header_length| contains ACCEPT_CH frame length and payload length.
virtual bool OnAcceptChFrameStart(QuicByteCount header_length) = 0;
// Called when an ACCEPT_CH frame has been successfully parsed.
virtual bool OnAcceptChFrame(const AcceptChFrame& frame) = 0;
// Called when a WEBTRANSPORT_STREAM frame type and the session ID varint
// immediately following it has been received. Any further parsing should
// be done by the stream itself, and not the parser. Note that this does not
// return bool, because WEBTRANSPORT_STREAM always causes the parsing
// process to cease.
virtual void OnWebTransportStreamFrameType(
QuicByteCount header_length,
WebTransportSessionId session_id) = 0;
// Called when a frame of unknown type |frame_type| has been received.
// Frame type might be reserved, Visitor must make sure to ignore.
// |header_length| and |payload_length| are the length of the frame header
// and payload, respectively.
virtual bool OnUnknownFrameStart(uint64_t frame_type,
QuicByteCount header_length,
QuicByteCount payload_length) = 0;
// Called when part of the payload of the unknown frame has been read. May
// be called multiple times for a single frame. |payload| is guaranteed to
// be non-empty.
virtual bool OnUnknownFramePayload(absl::string_view payload) = 0;
// Called when the unknown frame has been completely processed.
virtual bool OnUnknownFrameEnd() = 0;
};
// |visitor| must be non-null, and must outlive HttpDecoder.
explicit HttpDecoder(Visitor* visitor);
explicit HttpDecoder(Visitor* visitor, Options options);
~HttpDecoder();
// Processes the input and invokes the appropriate visitor methods, until a
// visitor method returns false or an error occurs. Returns the number of
// bytes processed. Does not process any input if called after an error.
// Paused processing can be resumed by calling ProcessInput() again with the
// unprocessed portion of data. Must not be called after an error has
// occurred.
QuicByteCount ProcessInput(const char* data, QuicByteCount len);
// Decode settings frame from |data|.
// Upon successful decoding, |frame| will be populated, and returns true.
// This method is not used for regular processing of incoming data.
static bool DecodeSettings(const char* data,
QuicByteCount len,
SettingsFrame* frame);
// Returns an error code other than QUIC_NO_ERROR if and only if
// Visitor::OnError() has been called.
QuicErrorCode error() const { return error_; }
const std::string& error_detail() const { return error_detail_; }
// Returns true if input data processed so far ends on a frame boundary.
bool AtFrameBoundary() const { return state_ == STATE_READING_FRAME_TYPE; }
private:
friend test::HttpDecoderPeer;
// Represents the current state of the parsing state machine.
enum HttpDecoderState {
STATE_READING_FRAME_LENGTH,
STATE_READING_FRAME_TYPE,
STATE_READING_FRAME_PAYLOAD,
STATE_FINISH_PARSING,
STATE_PARSING_NO_LONGER_POSSIBLE,
STATE_ERROR
};
// Reads the type of a frame from |reader|. Sets error_ and error_detail_
// if there are any errors. Also calls OnDataFrameStart() or
// OnHeadersFrameStart() for appropriate frame types. Returns whether the
// processing should continue.
bool ReadFrameType(QuicDataReader* reader);
// Reads the length of a frame from |reader|. Sets error_ and error_detail_
// if there are any errors. Returns whether processing should continue.
bool ReadFrameLength(QuicDataReader* reader);
// Depending on the frame type, reads and processes the payload of the current
// frame from |reader| and calls visitor methods, or calls
// BufferOrParsePayload(). Returns whether processing should continue.
bool ReadFramePayload(QuicDataReader* reader);
// For frame types parsed by BufferOrParsePayload(), this method is only
// called if frame payload is empty, at it calls BufferOrParsePayload(). For
// other frame types, this method directly calls visitor methods to signal
// that frame had been parsed completely. Returns whether processing should
// continue.
bool FinishParsing(QuicDataReader* reader);
// Read payload of unknown frame from |reader| and call
// Visitor::OnUnknownFramePayload(). Returns true decoding should continue,
// false if it should be paused.
bool HandleUnknownFramePayload(QuicDataReader* reader);
// Discards any remaining frame payload from |reader|.
void DiscardFramePayload(QuicDataReader* reader);
// Buffers any remaining frame payload from |*reader| into |buffer_| if
// necessary. Parses the frame payload if complete. Parses out of |*reader|
// without unnecessary copy if |*reader| has entire payload.
// Returns whether processing should continue.
bool BufferOrParsePayload(QuicDataReader* reader);
// Parses the entire payload of certain kinds of frames that are parsed in a
// single pass. |reader| must have at least |current_frame_length_| bytes.
// Returns whether processing should continue.
bool ParseEntirePayload(QuicDataReader* reader);
// Buffers any remaining frame length field from |reader| into
// |length_buffer_|.
void BufferFrameLength(QuicDataReader* reader);
// Buffers any remaining frame type field from |reader| into |type_buffer_|.
void BufferFrameType(QuicDataReader* reader);
// Buffers at most |remaining_push_id_length_| from |reader| to
// |push_id_buffer_|. TODO(b/171463363): Remove.
void BufferPushId(QuicDataReader* reader);
// Sets |error_| and |error_detail_| accordingly.
void RaiseError(QuicErrorCode error, std::string error_detail);
// Parses the payload of a SETTINGS frame from |reader| into |frame|.
bool ParseSettingsFrame(QuicDataReader* reader, SettingsFrame* frame);
// Parses the payload of a PRIORITY_UPDATE frame (draft-01, type 0x0f)
// from |reader| into |frame|.
// TODO(b/147306124): Remove.
bool ParsePriorityUpdateFrame(QuicDataReader* reader,
PriorityUpdateFrame* frame);
// Parses the payload of a PRIORITY_UPDATE frame (draft-02, type 0xf0700)
// from |reader| into |frame|.
// TODO(b/147306124): Rename to ParsePriorityUpdateFrame().
bool ParseNewPriorityUpdateFrame(QuicDataReader* reader,
PriorityUpdateFrame* frame);
// Parses the payload of an ACCEPT_CH frame from |reader| into |frame|.
bool ParseAcceptChFrame(QuicDataReader* reader, AcceptChFrame* frame);
// Returns the max frame size of a given |frame_type|.
QuicByteCount MaxFrameLength(uint64_t frame_type);
// Visitor to invoke when messages are parsed.
Visitor* const visitor_; // Unowned.
// Whether WEBTRANSPORT_STREAM should be parsed.
bool allow_web_transport_stream_;
// Current state of the parsing.
HttpDecoderState state_;
// Type of the frame currently being parsed.
uint64_t current_frame_type_;
// Size of the frame's length field.
QuicByteCount current_length_field_length_;
// Remaining length that's needed for the frame's length field.
QuicByteCount remaining_length_field_length_;
// Length of the payload of the frame currently being parsed.
QuicByteCount current_frame_length_;
// Remaining payload bytes to be parsed.
QuicByteCount remaining_frame_length_;
// Length of the frame's type field.
QuicByteCount current_type_field_length_;
// Remaining length that's needed for the frame's type field.
QuicByteCount remaining_type_field_length_;
// Length of PUSH_PROMISE frame's push id.
// TODO(b/171463363): Remove.
QuicByteCount current_push_id_length_;
// Remaining length that's needed for PUSH_PROMISE frame's push id field.
// TODO(b/171463363): Remove.
QuicByteCount remaining_push_id_length_;
// Last error.
QuicErrorCode error_;
// The issue which caused |error_|
std::string error_detail_;
// Remaining unparsed data.
std::string buffer_;
// Remaining unparsed length field data.
std::array<char, sizeof(uint64_t)> length_buffer_;
// Remaining unparsed type field data.
std::array<char, sizeof(uint64_t)> type_buffer_;
// Remaining unparsed push id data.
// TODO(b/171463363): Remove.
std::array<char, sizeof(uint64_t)> push_id_buffer_;
// Latched value of
// gfe2_reloadable_flag_quic_error_on_http3_push.
const bool error_on_http3_push_;
};
} // namespace quic
#endif // QUICHE_QUIC_CORE_HTTP_HTTP_DECODER_H_