| Index: net/http2/decoder/http2_frame_decoder.cc
|
| diff --git a/net/http2/decoder/http2_frame_decoder.cc b/net/http2/decoder/http2_frame_decoder.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..fe24f91644cdd27208f41ee9872a28407274d858
|
| --- /dev/null
|
| +++ b/net/http2/decoder/http2_frame_decoder.cc
|
| @@ -0,0 +1,426 @@
|
| +// Copyright 2016 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/http2/decoder/http2_frame_decoder.h"
|
| +
|
| +#include "net/http2/http2_constants.h"
|
| +
|
| +namespace net {
|
| +
|
| +std::ostream& operator<<(std::ostream& out, Http2FrameDecoder::State v) {
|
| + switch (v) {
|
| + case Http2FrameDecoder::State::kStartDecodingHeader:
|
| + return out << "kStartDecodingHeader";
|
| + case Http2FrameDecoder::State::kResumeDecodingHeader:
|
| + return out << "kResumeDecodingHeader";
|
| + case Http2FrameDecoder::State::kResumeDecodingPayload:
|
| + return out << "kResumeDecodingPayload";
|
| + case Http2FrameDecoder::State::kDiscardPayload:
|
| + return out << "kDiscardPayload";
|
| + }
|
| + return out << static_cast<int>(v);
|
| +}
|
| +
|
| +Http2FrameDecoder::Http2FrameDecoder(Http2FrameDecoderListener* listener)
|
| + : state_(State::kStartDecodingHeader),
|
| + maximum_payload_size_(Http2SettingsInfo::DefaultMaxFrameSize()) {
|
| + set_listener(listener);
|
| +}
|
| +
|
| +void Http2FrameDecoder::set_listener(Http2FrameDecoderListener* listener) {
|
| + if (listener == nullptr) {
|
| + listener = &no_op_listener_;
|
| + }
|
| + frame_decoder_state_.set_listener(listener);
|
| +}
|
| +
|
| +Http2FrameDecoderListener* Http2FrameDecoder::listener() const {
|
| + return frame_decoder_state_.listener();
|
| +}
|
| +
|
| +DecodeStatus Http2FrameDecoder::DecodeFrame(DecodeBuffer* db) {
|
| + DVLOG(2) << "Http2FrameDecoder::DecodeFrame state=" << state_;
|
| + switch (state_) {
|
| + case State::kStartDecodingHeader:
|
| + if (frame_decoder_state_.StartDecodingFrameHeader(db)) {
|
| + return StartDecodingPayload(db);
|
| + }
|
| + state_ = State::kResumeDecodingHeader;
|
| + return DecodeStatus::kDecodeInProgress;
|
| +
|
| + case State::kResumeDecodingHeader:
|
| + if (frame_decoder_state_.ResumeDecodingFrameHeader(db)) {
|
| + return StartDecodingPayload(db);
|
| + }
|
| + return DecodeStatus::kDecodeInProgress;
|
| +
|
| + case State::kResumeDecodingPayload:
|
| + return ResumeDecodingPayload(db);
|
| +
|
| + case State::kDiscardPayload:
|
| + return DiscardPayload(db);
|
| + }
|
| +
|
| + NOTREACHED();
|
| + return DecodeStatus::kDecodeError;
|
| +}
|
| +
|
| +size_t Http2FrameDecoder::remaining_payload() const {
|
| + return frame_decoder_state_.remaining_payload();
|
| +}
|
| +
|
| +uint32_t Http2FrameDecoder::remaining_padding() const {
|
| + return frame_decoder_state_.remaining_padding();
|
| +}
|
| +
|
| +DecodeStatus Http2FrameDecoder::StartDecodingPayload(DecodeBuffer* db) {
|
| + const Http2FrameHeader& header = frame_header();
|
| +
|
| + // TODO(jamessynge): Remove OnFrameHeader once done with supporting
|
| + // SpdyFramer's exact states.
|
| + if (!listener()->OnFrameHeader(header)) {
|
| + DVLOG(2) << "OnFrameHeader rejected the frame, will discard; header: "
|
| + << header;
|
| + state_ = State::kDiscardPayload;
|
| + frame_decoder_state_.InitializeRemainders();
|
| + return DecodeStatus::kDecodeError;
|
| + }
|
| +
|
| + if (header.payload_length > maximum_payload_size_) {
|
| + DVLOG(2) << "Payload length is greater than allowed: "
|
| + << header.payload_length << " > " << maximum_payload_size_
|
| + << "\n header: " << header;
|
| + state_ = State::kDiscardPayload;
|
| + frame_decoder_state_.InitializeRemainders();
|
| + listener()->OnFrameSizeError(header);
|
| + return DecodeStatus::kDecodeError;
|
| + }
|
| +
|
| + // The decode buffer can extend across many frames. Make sure that the
|
| + // buffer we pass to the start method that is specific to the frame type
|
| + // does not exend beyond this frame.
|
| + DecodeBufferSubset subset(db, header.payload_length);
|
| + DecodeStatus status;
|
| + switch (header.type) {
|
| + case Http2FrameType::DATA:
|
| + status = StartDecodingDataPayload(&subset);
|
| + break;
|
| +
|
| + case Http2FrameType::HEADERS:
|
| + status = StartDecodingHeadersPayload(&subset);
|
| + break;
|
| +
|
| + case Http2FrameType::PRIORITY:
|
| + status = StartDecodingPriorityPayload(&subset);
|
| + break;
|
| +
|
| + case Http2FrameType::RST_STREAM:
|
| + status = StartDecodingRstStreamPayload(&subset);
|
| + break;
|
| +
|
| + case Http2FrameType::SETTINGS:
|
| + status = StartDecodingSettingsPayload(&subset);
|
| + break;
|
| +
|
| + case Http2FrameType::PUSH_PROMISE:
|
| + status = StartDecodingPushPromisePayload(&subset);
|
| + break;
|
| +
|
| + case Http2FrameType::PING:
|
| + status = StartDecodingPingPayload(&subset);
|
| + break;
|
| +
|
| + case Http2FrameType::GOAWAY:
|
| + status = StartDecodingGoAwayPayload(&subset);
|
| + break;
|
| +
|
| + case Http2FrameType::WINDOW_UPDATE:
|
| + status = StartDecodingWindowUpdatePayload(&subset);
|
| + break;
|
| +
|
| + case Http2FrameType::CONTINUATION:
|
| + status = StartDecodingContinuationPayload(&subset);
|
| + break;
|
| +
|
| + case Http2FrameType::ALTSVC:
|
| + status = StartDecodingAltSvcPayload(&subset);
|
| + break;
|
| +
|
| + default:
|
| + status = StartDecodingUnknownPayload(&subset);
|
| + break;
|
| + }
|
| +
|
| + if (status == DecodeStatus::kDecodeDone) {
|
| + state_ = State::kStartDecodingHeader;
|
| + return status;
|
| + } else if (status == DecodeStatus::kDecodeInProgress) {
|
| + state_ = State::kResumeDecodingPayload;
|
| + return status;
|
| + } else {
|
| + state_ = State::kDiscardPayload;
|
| + return status;
|
| + }
|
| +}
|
| +
|
| +DecodeStatus Http2FrameDecoder::ResumeDecodingPayload(DecodeBuffer* db) {
|
| + // The decode buffer can extend across many frames. Make sure that the
|
| + // buffer we pass to the start method that is specific to the frame type
|
| + // does not exend beyond this frame.
|
| + size_t remaining = frame_decoder_state_.remaining_total_payload();
|
| + DCHECK_LE(remaining, frame_header().payload_length);
|
| + DecodeBufferSubset subset(db, remaining);
|
| + DecodeStatus status;
|
| + switch (frame_header().type) {
|
| + case Http2FrameType::DATA:
|
| + status = ResumeDecodingDataPayload(&subset);
|
| + break;
|
| +
|
| + case Http2FrameType::HEADERS:
|
| + status = ResumeDecodingHeadersPayload(&subset);
|
| + break;
|
| +
|
| + case Http2FrameType::PRIORITY:
|
| + status = ResumeDecodingPriorityPayload(&subset);
|
| + break;
|
| +
|
| + case Http2FrameType::RST_STREAM:
|
| + status = ResumeDecodingRstStreamPayload(&subset);
|
| + break;
|
| +
|
| + case Http2FrameType::SETTINGS:
|
| + status = ResumeDecodingSettingsPayload(&subset);
|
| + break;
|
| +
|
| + case Http2FrameType::PUSH_PROMISE:
|
| + status = ResumeDecodingPushPromisePayload(&subset);
|
| + break;
|
| +
|
| + case Http2FrameType::PING:
|
| + status = ResumeDecodingPingPayload(&subset);
|
| + break;
|
| +
|
| + case Http2FrameType::GOAWAY:
|
| + status = ResumeDecodingGoAwayPayload(&subset);
|
| + break;
|
| +
|
| + case Http2FrameType::WINDOW_UPDATE:
|
| + status = ResumeDecodingWindowUpdatePayload(&subset);
|
| + break;
|
| +
|
| + case Http2FrameType::CONTINUATION:
|
| + status = ResumeDecodingContinuationPayload(&subset);
|
| + break;
|
| +
|
| + case Http2FrameType::ALTSVC:
|
| + status = ResumeDecodingAltSvcPayload(&subset);
|
| + break;
|
| +
|
| + default:
|
| + status = ResumeDecodingUnknownPayload(&subset);
|
| + break;
|
| + }
|
| +
|
| + if (status == DecodeStatus::kDecodeDone) {
|
| + state_ = State::kStartDecodingHeader;
|
| + return status;
|
| + } else if (status == DecodeStatus::kDecodeInProgress) {
|
| + return status;
|
| + } else {
|
| + state_ = State::kDiscardPayload;
|
| + return status;
|
| + }
|
| +}
|
| +
|
| +// Clear any of the flags in the frame header that aren't set in valid_flags.
|
| +void Http2FrameDecoder::RetainFlags(uint8_t valid_flags) {
|
| + frame_decoder_state_.RetainFlags(valid_flags);
|
| +}
|
| +
|
| +// Clear all of the flags in the frame header; for use with frame types that
|
| +// don't define any flags, such as WINDOW_UPDATE.
|
| +void Http2FrameDecoder::ClearFlags() {
|
| + frame_decoder_state_.ClearFlags();
|
| +}
|
| +
|
| +DecodeStatus Http2FrameDecoder::StartDecodingAltSvcPayload(DecodeBuffer* db) {
|
| + ClearFlags();
|
| + return altsvc_payload_decoder_.StartDecodingPayload(&frame_decoder_state_,
|
| + db);
|
| +}
|
| +DecodeStatus Http2FrameDecoder::ResumeDecodingAltSvcPayload(DecodeBuffer* db) {
|
| + // The frame is not paddable.
|
| + DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
|
| + frame_decoder_state_.remaining_payload());
|
| + return altsvc_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_,
|
| + db);
|
| +}
|
| +
|
| +DecodeStatus Http2FrameDecoder::StartDecodingContinuationPayload(
|
| + DecodeBuffer* db) {
|
| + RetainFlags(Http2FrameFlag::FLAG_END_HEADERS);
|
| + return continuation_payload_decoder_.StartDecodingPayload(
|
| + &frame_decoder_state_, db);
|
| +}
|
| +DecodeStatus Http2FrameDecoder::ResumeDecodingContinuationPayload(
|
| + DecodeBuffer* db) {
|
| + // The frame is not paddable.
|
| + DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
|
| + frame_decoder_state_.remaining_payload());
|
| + return continuation_payload_decoder_.ResumeDecodingPayload(
|
| + &frame_decoder_state_, db);
|
| +}
|
| +
|
| +DecodeStatus Http2FrameDecoder::StartDecodingDataPayload(DecodeBuffer* db) {
|
| + RetainFlags(Http2FrameFlag::FLAG_END_STREAM | Http2FrameFlag::FLAG_PADDED);
|
| + return data_payload_decoder_.StartDecodingPayload(&frame_decoder_state_, db);
|
| +}
|
| +DecodeStatus Http2FrameDecoder::ResumeDecodingDataPayload(DecodeBuffer* db) {
|
| + return data_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_, db);
|
| +}
|
| +
|
| +DecodeStatus Http2FrameDecoder::StartDecodingGoAwayPayload(DecodeBuffer* db) {
|
| + ClearFlags();
|
| + return goaway_payload_decoder_.StartDecodingPayload(&frame_decoder_state_,
|
| + db);
|
| +}
|
| +DecodeStatus Http2FrameDecoder::ResumeDecodingGoAwayPayload(DecodeBuffer* db) {
|
| + // The frame is not paddable.
|
| + DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
|
| + frame_decoder_state_.remaining_payload());
|
| + return goaway_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_,
|
| + db);
|
| +}
|
| +
|
| +DecodeStatus Http2FrameDecoder::StartDecodingHeadersPayload(DecodeBuffer* db) {
|
| + RetainFlags(Http2FrameFlag::FLAG_END_STREAM |
|
| + Http2FrameFlag::FLAG_END_HEADERS | Http2FrameFlag::FLAG_PADDED |
|
| + Http2FrameFlag::FLAG_PRIORITY);
|
| + return headers_payload_decoder_.StartDecodingPayload(&frame_decoder_state_,
|
| + db);
|
| +}
|
| +DecodeStatus Http2FrameDecoder::ResumeDecodingHeadersPayload(DecodeBuffer* db) {
|
| + DCHECK_LE(frame_decoder_state_.remaining_payload_and_padding(),
|
| + frame_header().payload_length);
|
| + return headers_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_,
|
| + db);
|
| +}
|
| +
|
| +DecodeStatus Http2FrameDecoder::StartDecodingPingPayload(DecodeBuffer* db) {
|
| + RetainFlags(Http2FrameFlag::FLAG_ACK);
|
| + return ping_payload_decoder_.StartDecodingPayload(&frame_decoder_state_, db);
|
| +}
|
| +DecodeStatus Http2FrameDecoder::ResumeDecodingPingPayload(DecodeBuffer* db) {
|
| + // The frame is not paddable.
|
| + DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
|
| + frame_decoder_state_.remaining_payload());
|
| + return ping_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_, db);
|
| +}
|
| +
|
| +DecodeStatus Http2FrameDecoder::StartDecodingPriorityPayload(DecodeBuffer* db) {
|
| + ClearFlags();
|
| + return priority_payload_decoder_.StartDecodingPayload(&frame_decoder_state_,
|
| + db);
|
| +}
|
| +DecodeStatus Http2FrameDecoder::ResumeDecodingPriorityPayload(
|
| + DecodeBuffer* db) {
|
| + // The frame is not paddable.
|
| + DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
|
| + frame_decoder_state_.remaining_payload());
|
| + return priority_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_,
|
| + db);
|
| +}
|
| +
|
| +DecodeStatus Http2FrameDecoder::StartDecodingPushPromisePayload(
|
| + DecodeBuffer* db) {
|
| + RetainFlags(Http2FrameFlag::FLAG_END_HEADERS | Http2FrameFlag::FLAG_PADDED);
|
| + return push_promise_payload_decoder_.StartDecodingPayload(
|
| + &frame_decoder_state_, db);
|
| +}
|
| +DecodeStatus Http2FrameDecoder::ResumeDecodingPushPromisePayload(
|
| + DecodeBuffer* db) {
|
| + DCHECK_LE(frame_decoder_state_.remaining_payload_and_padding(),
|
| + frame_header().payload_length);
|
| + return push_promise_payload_decoder_.ResumeDecodingPayload(
|
| + &frame_decoder_state_, db);
|
| +}
|
| +
|
| +DecodeStatus Http2FrameDecoder::StartDecodingRstStreamPayload(
|
| + DecodeBuffer* db) {
|
| + ClearFlags();
|
| + return rst_stream_payload_decoder_.StartDecodingPayload(&frame_decoder_state_,
|
| + db);
|
| +}
|
| +DecodeStatus Http2FrameDecoder::ResumeDecodingRstStreamPayload(
|
| + DecodeBuffer* db) {
|
| + // The frame is not paddable.
|
| + DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
|
| + frame_decoder_state_.remaining_payload());
|
| + return rst_stream_payload_decoder_.ResumeDecodingPayload(
|
| + &frame_decoder_state_, db);
|
| +}
|
| +
|
| +DecodeStatus Http2FrameDecoder::StartDecodingSettingsPayload(DecodeBuffer* db) {
|
| + RetainFlags(Http2FrameFlag::FLAG_ACK);
|
| + return settings_payload_decoder_.StartDecodingPayload(&frame_decoder_state_,
|
| + db);
|
| +}
|
| +DecodeStatus Http2FrameDecoder::ResumeDecodingSettingsPayload(
|
| + DecodeBuffer* db) {
|
| + // The frame is not paddable.
|
| + DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
|
| + frame_decoder_state_.remaining_payload());
|
| + return settings_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_,
|
| + db);
|
| +}
|
| +
|
| +DecodeStatus Http2FrameDecoder::StartDecodingUnknownPayload(DecodeBuffer* db) {
|
| + // We don't known what type of frame this is, so we don't know which flags
|
| + // are valid, so we don't touch them.
|
| + return unknown_payload_decoder_.StartDecodingPayload(&frame_decoder_state_,
|
| + db);
|
| +}
|
| +DecodeStatus Http2FrameDecoder::ResumeDecodingUnknownPayload(DecodeBuffer* db) {
|
| + // We don't known what type of frame this is, so we treat it as not paddable.
|
| + DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
|
| + frame_decoder_state_.remaining_payload());
|
| + return unknown_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_,
|
| + db);
|
| +}
|
| +
|
| +DecodeStatus Http2FrameDecoder::StartDecodingWindowUpdatePayload(
|
| + DecodeBuffer* db) {
|
| + ClearFlags();
|
| + return window_update_payload_decoder_.StartDecodingPayload(
|
| + &frame_decoder_state_, db);
|
| +}
|
| +DecodeStatus Http2FrameDecoder::ResumeDecodingWindowUpdatePayload(
|
| + DecodeBuffer* db) {
|
| + // The frame is not paddable.
|
| + DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
|
| + frame_decoder_state_.remaining_payload());
|
| + return window_update_payload_decoder_.ResumeDecodingPayload(
|
| + &frame_decoder_state_, db);
|
| +}
|
| +
|
| +DecodeStatus Http2FrameDecoder::DiscardPayload(DecodeBuffer* db) {
|
| + DVLOG(2) << "remaining_payload=" << frame_decoder_state_.remaining_payload_
|
| + << "; remaining_padding=" << frame_decoder_state_.remaining_padding_;
|
| + frame_decoder_state_.remaining_payload_ +=
|
| + frame_decoder_state_.remaining_padding_;
|
| + frame_decoder_state_.remaining_padding_ = 0;
|
| + const size_t avail = frame_decoder_state_.AvailablePayload(db);
|
| + DVLOG(2) << "avail=" << avail;
|
| + if (avail > 0) {
|
| + frame_decoder_state_.ConsumePayload(avail);
|
| + db->AdvanceCursor(avail);
|
| + }
|
| + if (frame_decoder_state_.remaining_payload_ == 0) {
|
| + state_ = State::kStartDecodingHeader;
|
| + return DecodeStatus::kDecodeDone;
|
| + }
|
| + return DecodeStatus::kDecodeInProgress;
|
| +}
|
| +
|
| +} // namespace net
|
|
|