blob: 7fe994ed81bc4377b6afda0a883f50beeb3a70b7 [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.
#include "net/third_party/quiche/src/quic/core/http/http_decoder.h"
#include <cstdint>
#include "net/third_party/quiche/src/quic/core/http/http_frames.h"
#include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h"
namespace quic {
HttpDecoder::HttpDecoder(Visitor* visitor)
: visitor_(visitor),
state_(STATE_READING_FRAME_TYPE),
current_frame_type_(0),
current_length_field_length_(0),
remaining_length_field_length_(0),
current_frame_length_(0),
remaining_frame_length_(0),
current_type_field_length_(0),
remaining_type_field_length_(0),
current_push_id_length_(0),
remaining_push_id_length_(0),
error_(QUIC_NO_ERROR),
error_detail_("") {
DCHECK(visitor_);
}
HttpDecoder::~HttpDecoder() {}
QuicByteCount HttpDecoder::ProcessInput(const char* data, QuicByteCount len) {
DCHECK_EQ(QUIC_NO_ERROR, error_);
DCHECK_NE(STATE_ERROR, state_);
QuicDataReader reader(data, len);
bool continue_processing = true;
while (continue_processing &&
(reader.BytesRemaining() != 0 || state_ == STATE_FINISH_PARSING)) {
// |continue_processing| must have been set to false upon error.
DCHECK_EQ(QUIC_NO_ERROR, error_);
DCHECK_NE(STATE_ERROR, state_);
switch (state_) {
case STATE_READING_FRAME_TYPE:
ReadFrameType(&reader);
break;
case STATE_READING_FRAME_LENGTH:
continue_processing = ReadFrameLength(&reader);
break;
case STATE_READING_FRAME_PAYLOAD:
continue_processing = ReadFramePayload(&reader);
break;
case STATE_FINISH_PARSING:
continue_processing = FinishParsing();
break;
case STATE_ERROR:
break;
default:
QUIC_BUG << "Invalid state: " << state_;
}
}
return len - reader.BytesRemaining();
}
void HttpDecoder::ReadFrameType(QuicDataReader* reader) {
DCHECK_NE(0u, reader->BytesRemaining());
if (current_type_field_length_ == 0) {
// A new frame is coming.
current_type_field_length_ = reader->PeekVarInt62Length();
DCHECK_NE(0u, current_type_field_length_);
if (current_type_field_length_ > reader->BytesRemaining()) {
// Buffer a new type field.
remaining_type_field_length_ = current_type_field_length_;
BufferFrameType(reader);
return;
}
// The reader has all type data needed, so no need to buffer.
bool success = reader->ReadVarInt62(&current_frame_type_);
DCHECK(success);
} else {
// Buffer the existing type field.
BufferFrameType(reader);
// The frame is still not buffered completely.
if (remaining_type_field_length_ != 0) {
return;
}
QuicDataReader type_reader(type_buffer_.data(), current_type_field_length_);
bool success = type_reader.ReadVarInt62(&current_frame_type_);
DCHECK(success);
}
state_ = STATE_READING_FRAME_LENGTH;
}
bool HttpDecoder::ReadFrameLength(QuicDataReader* reader) {
DCHECK_NE(0u, reader->BytesRemaining());
if (current_length_field_length_ == 0) {
// A new frame is coming.
current_length_field_length_ = reader->PeekVarInt62Length();
DCHECK_NE(0u, current_length_field_length_);
if (current_length_field_length_ > reader->BytesRemaining()) {
// Buffer a new length field.
remaining_length_field_length_ = current_length_field_length_;
BufferFrameLength(reader);
return true;
}
// The reader has all length data needed, so no need to buffer.
bool success = reader->ReadVarInt62(&current_frame_length_);
DCHECK(success);
} else {
// Buffer the existing length field.
BufferFrameLength(reader);
// The frame is still not buffered completely.
if (remaining_length_field_length_ != 0) {
return true;
}
QuicDataReader length_reader(length_buffer_.data(),
current_length_field_length_);
bool success = length_reader.ReadVarInt62(&current_frame_length_);
DCHECK(success);
}
if (current_frame_length_ > MaxFrameLength(current_frame_type_)) {
// TODO(b/124216424): Use HTTP_EXCESSIVE_LOAD.
RaiseError(QUIC_INVALID_FRAME_DATA, "Frame is too large");
return false;
}
// Calling the following visitor methods does not require parsing of any
// frame payload.
bool continue_processing = true;
const QuicByteCount header_length =
current_length_field_length_ + current_type_field_length_;
switch (current_frame_type_) {
case static_cast<uint64_t>(HttpFrameType::DATA):
continue_processing = visitor_->OnDataFrameStart(header_length);
break;
case static_cast<uint64_t>(HttpFrameType::HEADERS):
continue_processing = visitor_->OnHeadersFrameStart(header_length);
break;
case static_cast<uint64_t>(HttpFrameType::PRIORITY):
continue_processing = visitor_->OnPriorityFrameStart(header_length);
break;
case static_cast<uint64_t>(HttpFrameType::CANCEL_PUSH):
break;
case static_cast<uint64_t>(HttpFrameType::SETTINGS):
continue_processing = visitor_->OnSettingsFrameStart(header_length);
break;
case static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE):
// This edge case needs to be handled here, because ReadFramePayload()
// does not get called if |current_frame_length_| is zero.
if (current_frame_length_ == 0) {
RaiseError(QUIC_INVALID_FRAME_DATA, "Corrupt PUSH_PROMISE frame.");
return false;
}
continue_processing = visitor_->OnPushPromiseFrameStart(header_length);
break;
case static_cast<uint64_t>(HttpFrameType::GOAWAY):
break;
case static_cast<uint64_t>(HttpFrameType::MAX_PUSH_ID):
break;
case static_cast<uint64_t>(HttpFrameType::DUPLICATE_PUSH):
break;
default:
continue_processing =
visitor_->OnUnknownFrameStart(current_frame_type_, header_length);
break;
}
remaining_frame_length_ = current_frame_length_;
state_ = (remaining_frame_length_ == 0) ? STATE_FINISH_PARSING
: STATE_READING_FRAME_PAYLOAD;
return continue_processing;
}
bool HttpDecoder::ReadFramePayload(QuicDataReader* reader) {
DCHECK_NE(0u, reader->BytesRemaining());
DCHECK_NE(0u, remaining_frame_length_);
bool continue_processing = true;
switch (current_frame_type_) {
case static_cast<uint64_t>(HttpFrameType::DATA): {
QuicByteCount bytes_to_read = std::min<QuicByteCount>(
remaining_frame_length_, reader->BytesRemaining());
QuicStringPiece payload;
bool success = reader->ReadStringPiece(&payload, bytes_to_read);
DCHECK(success);
DCHECK(!payload.empty());
continue_processing = visitor_->OnDataFramePayload(payload);
remaining_frame_length_ -= payload.length();
break;
}
case static_cast<uint64_t>(HttpFrameType::HEADERS): {
QuicByteCount bytes_to_read = std::min<QuicByteCount>(
remaining_frame_length_, reader->BytesRemaining());
QuicStringPiece payload;
bool success = reader->ReadStringPiece(&payload, bytes_to_read);
DCHECK(success);
DCHECK(!payload.empty());
continue_processing = visitor_->OnHeadersFramePayload(payload);
remaining_frame_length_ -= payload.length();
break;
}
case static_cast<uint64_t>(HttpFrameType::PRIORITY): {
// TODO(rch): avoid buffering if the entire frame is present, and
// instead parse directly out of |reader|.
BufferFramePayload(reader);
break;
}
case static_cast<uint64_t>(HttpFrameType::CANCEL_PUSH): {
BufferFramePayload(reader);
break;
}
case static_cast<uint64_t>(HttpFrameType::SETTINGS): {
BufferFramePayload(reader);
break;
}
case static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE): {
PushId push_id;
if (current_frame_length_ == remaining_frame_length_) {
// A new Push Promise frame just arrived.
DCHECK_EQ(0u, current_push_id_length_);
current_push_id_length_ = reader->PeekVarInt62Length();
if (current_push_id_length_ > remaining_frame_length_) {
RaiseError(QUIC_INVALID_FRAME_DATA, "PUSH_PROMISE frame malformed.");
return false;
}
if (current_push_id_length_ > reader->BytesRemaining()) {
// Not all bytes of push id is present yet, buffer push id.
DCHECK_EQ(0u, remaining_push_id_length_);
remaining_push_id_length_ = current_push_id_length_;
BufferPushId(reader);
break;
}
bool success = reader->ReadVarInt62(&push_id);
DCHECK(success);
remaining_frame_length_ -= current_push_id_length_;
if (!visitor_->OnPushPromiseFramePushId(push_id,
current_push_id_length_)) {
continue_processing = false;
current_push_id_length_ = 0;
break;
}
current_push_id_length_ = 0;
} else if (remaining_push_id_length_ > 0) {
// Waiting for more bytes on push id.
BufferPushId(reader);
if (remaining_push_id_length_ != 0) {
break;
}
QuicDataReader push_id_reader(push_id_buffer_.data(),
current_push_id_length_);
bool success = push_id_reader.ReadVarInt62(&push_id);
DCHECK(success);
if (!visitor_->OnPushPromiseFramePushId(push_id,
current_push_id_length_)) {
continue_processing = false;
current_push_id_length_ = 0;
break;
}
current_push_id_length_ = 0;
}
// Read Push Promise headers.
DCHECK_LT(remaining_frame_length_, current_frame_length_);
QuicByteCount bytes_to_read = std::min<QuicByteCount>(
remaining_frame_length_, reader->BytesRemaining());
if (bytes_to_read == 0) {
break;
}
QuicStringPiece payload;
bool success = reader->ReadStringPiece(&payload, bytes_to_read);
DCHECK(success);
DCHECK(!payload.empty());
continue_processing = visitor_->OnPushPromiseFramePayload(payload);
remaining_frame_length_ -= payload.length();
break;
}
case static_cast<uint64_t>(HttpFrameType::GOAWAY): {
BufferFramePayload(reader);
break;
}
case static_cast<uint64_t>(HttpFrameType::MAX_PUSH_ID): {
BufferFramePayload(reader);
break;
}
case static_cast<uint64_t>(HttpFrameType::DUPLICATE_PUSH): {
BufferFramePayload(reader);
break;
}
default: {
QuicByteCount bytes_to_read = std::min<QuicByteCount>(
remaining_frame_length_, reader->BytesRemaining());
QuicStringPiece payload;
bool success = reader->ReadStringPiece(&payload, bytes_to_read);
DCHECK(success);
DCHECK(!payload.empty());
continue_processing = visitor_->OnUnknownFramePayload(payload);
remaining_frame_length_ -= payload.length();
break;
}
}
if (remaining_frame_length_ == 0) {
state_ = STATE_FINISH_PARSING;
}
return continue_processing;
}
bool HttpDecoder::FinishParsing() {
DCHECK_EQ(0u, remaining_frame_length_);
bool continue_processing = true;
switch (current_frame_type_) {
case static_cast<uint64_t>(HttpFrameType::DATA): {
continue_processing = visitor_->OnDataFrameEnd();
break;
}
case static_cast<uint64_t>(HttpFrameType::HEADERS): {
continue_processing = visitor_->OnHeadersFrameEnd();
break;
}
case static_cast<uint64_t>(HttpFrameType::PRIORITY): {
// TODO(rch): avoid buffering if the entire frame is present, and
// instead parse directly out of |reader|.
PriorityFrame frame;
QuicDataReader reader(buffer_.data(), current_frame_length_);
if (!ParsePriorityFrame(&reader, &frame)) {
return false;
}
continue_processing = visitor_->OnPriorityFrame(frame);
break;
}
case static_cast<uint64_t>(HttpFrameType::CANCEL_PUSH): {
CancelPushFrame frame;
QuicDataReader reader(buffer_.data(), current_frame_length_);
if (!reader.ReadVarInt62(&frame.push_id)) {
// TODO(b/124216424): Use HTTP_MALFORMED_FRAME.
RaiseError(QUIC_INVALID_FRAME_DATA, "Unable to read push_id");
return false;
}
if (!reader.IsDoneReading()) {
RaiseError(QUIC_INVALID_FRAME_DATA,
"Superfluous data in CANCEL_PUSH frame.");
return false;
}
continue_processing = visitor_->OnCancelPushFrame(frame);
break;
}
case static_cast<uint64_t>(HttpFrameType::SETTINGS): {
SettingsFrame frame;
QuicDataReader reader(buffer_.data(), current_frame_length_);
if (!ParseSettingsFrame(&reader, &frame)) {
return false;
}
continue_processing = visitor_->OnSettingsFrame(frame);
break;
}
case static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE): {
continue_processing = visitor_->OnPushPromiseFrameEnd();
break;
}
case static_cast<uint64_t>(HttpFrameType::GOAWAY): {
QuicDataReader reader(buffer_.data(), current_frame_length_);
GoAwayFrame frame;
static_assert(!std::is_same<decltype(frame.stream_id), uint64_t>::value,
"Please remove local |stream_id| variable and pass "
"&frame.stream_id directly to ReadVarInt62() when changing "
"QuicStreamId from uint32_t to uint64_t.");
uint64_t stream_id;
if (!reader.ReadVarInt62(&stream_id)) {
// TODO(b/124216424): Use HTTP_MALFORMED_FRAME.
RaiseError(QUIC_INVALID_FRAME_DATA, "Unable to read GOAWAY stream_id");
return false;
}
if (!reader.IsDoneReading()) {
RaiseError(QUIC_INVALID_FRAME_DATA,
"Superfluous data in GOAWAY frame.");
return false;
}
frame.stream_id = static_cast<QuicStreamId>(stream_id);
continue_processing = visitor_->OnGoAwayFrame(frame);
break;
}
case static_cast<uint64_t>(HttpFrameType::MAX_PUSH_ID): {
QuicDataReader reader(buffer_.data(), current_frame_length_);
MaxPushIdFrame frame;
if (!reader.ReadVarInt62(&frame.push_id)) {
// TODO(b/124216424): Use HTTP_MALFORMED_FRAME.
RaiseError(QUIC_INVALID_FRAME_DATA, "Unable to read push_id");
return false;
}
if (!reader.IsDoneReading()) {
RaiseError(QUIC_INVALID_FRAME_DATA,
"Superfluous data in MAX_PUSH_ID frame.");
return false;
}
continue_processing = visitor_->OnMaxPushIdFrame(frame);
break;
}
case static_cast<uint64_t>(HttpFrameType::DUPLICATE_PUSH): {
QuicDataReader reader(buffer_.data(), current_frame_length_);
DuplicatePushFrame frame;
if (!reader.ReadVarInt62(&frame.push_id)) {
// TODO(b/124216424): Use HTTP_MALFORMED_FRAME.
RaiseError(QUIC_INVALID_FRAME_DATA, "Unable to read push_id");
return false;
}
if (!reader.IsDoneReading()) {
RaiseError(QUIC_INVALID_FRAME_DATA,
"Superfluous data in DUPLICATE_PUSH frame.");
return false;
}
continue_processing = visitor_->OnDuplicatePushFrame(frame);
break;
}
default: {
continue_processing = visitor_->OnUnknownFrameEnd();
break;
}
}
current_length_field_length_ = 0;
current_type_field_length_ = 0;
state_ = STATE_READING_FRAME_TYPE;
return continue_processing;
}
void HttpDecoder::DiscardFramePayload(QuicDataReader* reader) {
QuicByteCount bytes_to_read = std::min<QuicByteCount>(
remaining_frame_length_, reader->BytesRemaining());
QuicStringPiece payload;
bool success = reader->ReadStringPiece(&payload, bytes_to_read);
DCHECK(success);
remaining_frame_length_ -= payload.length();
if (remaining_frame_length_ == 0) {
state_ = STATE_READING_FRAME_TYPE;
current_length_field_length_ = 0;
current_type_field_length_ = 0;
}
}
void HttpDecoder::BufferFramePayload(QuicDataReader* reader) {
if (current_frame_length_ == remaining_frame_length_) {
buffer_.erase(buffer_.size());
buffer_.reserve(current_frame_length_);
}
QuicByteCount bytes_to_read = std::min<QuicByteCount>(
remaining_frame_length_, reader->BytesRemaining());
bool success = reader->ReadBytes(
&(buffer_[0]) + current_frame_length_ - remaining_frame_length_,
bytes_to_read);
DCHECK(success);
remaining_frame_length_ -= bytes_to_read;
}
void HttpDecoder::BufferFrameLength(QuicDataReader* reader) {
QuicByteCount bytes_to_read = std::min<QuicByteCount>(
remaining_length_field_length_, reader->BytesRemaining());
bool success =
reader->ReadBytes(length_buffer_.data() + current_length_field_length_ -
remaining_length_field_length_,
bytes_to_read);
DCHECK(success);
remaining_length_field_length_ -= bytes_to_read;
}
void HttpDecoder::BufferFrameType(QuicDataReader* reader) {
QuicByteCount bytes_to_read = std::min<QuicByteCount>(
remaining_type_field_length_, reader->BytesRemaining());
bool success =
reader->ReadBytes(type_buffer_.data() + current_type_field_length_ -
remaining_type_field_length_,
bytes_to_read);
DCHECK(success);
remaining_type_field_length_ -= bytes_to_read;
}
void HttpDecoder::BufferPushId(QuicDataReader* reader) {
DCHECK_LE(remaining_push_id_length_, current_frame_length_);
QuicByteCount bytes_to_read = std::min<QuicByteCount>(
reader->BytesRemaining(), remaining_push_id_length_);
bool success =
reader->ReadBytes(push_id_buffer_.data() + current_push_id_length_ -
remaining_push_id_length_,
bytes_to_read);
DCHECK(success);
remaining_push_id_length_ -= bytes_to_read;
remaining_frame_length_ -= bytes_to_read;
}
void HttpDecoder::RaiseError(QuicErrorCode error, std::string error_detail) {
state_ = STATE_ERROR;
error_ = error;
error_detail_ = std::move(error_detail);
visitor_->OnError(this);
}
bool HttpDecoder::ParsePriorityFrame(QuicDataReader* reader,
PriorityFrame* frame) {
uint8_t flags;
if (!reader->ReadUInt8(&flags)) {
// TODO(b/124216424): Use HTTP_MALFORMED_FRAME.
RaiseError(QUIC_INVALID_FRAME_DATA, "Unable to read PRIORITY frame flags.");
return false;
}
// Assign two most significant bits to prioritized_type.
frame->prioritized_type = static_cast<PriorityElementType>((flags >> 6) & 3);
// Assign the next two most significant bits to dependency type.
frame->dependency_type = static_cast<PriorityElementType>((flags >> 4) & 3);
frame->exclusive = flags & kPriorityExclusiveBit;
// TODO(bnc): Close connection with HTTP_MALFORMED_FRAME
// if lowest three bits are not all zero.
if (frame->prioritized_type != ROOT_OF_TREE &&
!reader->ReadVarInt62(&frame->prioritized_element_id)) {
// TODO(b/124216424): Use HTTP_MALFORMED_FRAME.
RaiseError(QUIC_INVALID_FRAME_DATA,
"Unable to read prioritized_element_id.");
return false;
}
if (frame->dependency_type != ROOT_OF_TREE &&
!reader->ReadVarInt62(&frame->element_dependency_id)) {
// TODO(b/124216424): Use HTTP_MALFORMED_FRAME.
RaiseError(QUIC_INVALID_FRAME_DATA,
"Unable to read element_dependency_id.");
return false;
}
if (!reader->ReadUInt8(&frame->weight)) {
// TODO(b/124216424): Use HTTP_MALFORMED_FRAME.
RaiseError(QUIC_INVALID_FRAME_DATA,
"Unable to read PRIORITY frame weight.");
return false;
}
if (!reader->IsDoneReading()) {
// TODO(b/124216424): Use HTTP_MALFORMED_FRAME.
RaiseError(QUIC_INVALID_FRAME_DATA, "Superfluous data in PRIORITY frame.");
return false;
}
return true;
}
bool HttpDecoder::ParseSettingsFrame(QuicDataReader* reader,
SettingsFrame* frame) {
while (!reader->IsDoneReading()) {
uint64_t id;
if (!reader->ReadVarInt62(&id)) {
// TODO(b/124216424): Use HTTP_MALFORMED_FRAME.
RaiseError(QUIC_INVALID_FRAME_DATA,
"Unable to read settings frame identifier");
return false;
}
uint64_t content;
if (!reader->ReadVarInt62(&content)) {
// TODO(b/124216424): Use HTTP_MALFORMED_FRAME.
RaiseError(QUIC_INVALID_FRAME_DATA,
"Unable to read settings frame content");
return false;
}
auto result = frame->values.insert({id, content});
if (!result.second) {
// TODO(b/124216424): Use HTTP_SETTINGS_ERROR.
RaiseError(QUIC_INVALID_FRAME_DATA, "Duplicate SETTINGS identifier.");
return false;
}
}
return true;
}
QuicByteCount HttpDecoder::MaxFrameLength(uint64_t frame_type) {
switch (frame_type) {
case static_cast<uint64_t>(HttpFrameType::PRIORITY):
return kPriorityFirstByteLength + VARIABLE_LENGTH_INTEGER_LENGTH_8 * 2 +
kPriorityWeightLength;
case static_cast<uint64_t>(HttpFrameType::CANCEL_PUSH):
return sizeof(PushId);
case static_cast<uint64_t>(HttpFrameType::SETTINGS):
// This limit is arbitrary.
return 1024 * 1024;
case static_cast<uint64_t>(HttpFrameType::GOAWAY):
return sizeof(QuicStreamId);
case static_cast<uint64_t>(HttpFrameType::MAX_PUSH_ID):
return sizeof(PushId);
case static_cast<uint64_t>(HttpFrameType::DUPLICATE_PUSH):
return sizeof(PushId);
default:
// Other frames require no data buffering, so it's safe to have no limit.
return std::numeric_limits<QuicByteCount>::max();
}
}
} // namespace quic