| // 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 "http2/decoder/http2_frame_decoder.h" |
| |
| #include "http2/decoder/decode_status.h" |
| #include "http2/hpack/varint/hpack_varint_decoder.h" |
| #include "http2/http2_constants.h" |
| #include "http2/platform/api/http2_bug_tracker.h" |
| #include "http2/platform/api/http2_flag_utils.h" |
| #include "http2/platform/api/http2_flags.h" |
| #include "http2/platform/api/http2_macros.h" |
| |
| namespace http2 { |
| |
| 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"; |
| } |
| // Since the value doesn't come over the wire, only a programming bug should |
| // result in reaching this point. |
| int unknown = static_cast<int>(v); |
| HTTP2_BUG << "Http2FrameDecoder::State " << unknown; |
| return out << "Http2FrameDecoder::State(" << unknown << ")"; |
| } |
| |
| 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) { |
| HTTP2_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); |
| } |
| |
| HTTP2_UNREACHABLE(); |
| 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)) { |
| HTTP2_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_) { |
| HTTP2_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; |
| |
| case Http2FrameType::PRIORITY_UPDATE: |
| if (GetHttp2RestartFlag(http2_parse_priority_update_frame)) { |
| HTTP2_RESTART_FLAG_COUNT_N(http2_parse_priority_update_frame, 1, 2); |
| status = StartDecodingPriorityUpdatePayload(&subset); |
| } else { |
| status = StartDecodingUnknownPayload(&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(); |
| QUICHE_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; |
| |
| case Http2FrameType::PRIORITY_UPDATE: |
| if (GetHttp2RestartFlag(http2_parse_priority_update_frame)) { |
| HTTP2_RESTART_FLAG_COUNT_N(http2_parse_priority_update_frame, 2, 2); |
| status = ResumeDecodingPriorityUpdatePayload(&subset); |
| } else { |
| status = ResumeDecodingUnknownPayload(&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. |
| QUICHE_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::END_HEADERS); |
| return continuation_payload_decoder_.StartDecodingPayload( |
| &frame_decoder_state_, db); |
| } |
| DecodeStatus Http2FrameDecoder::ResumeDecodingContinuationPayload( |
| DecodeBuffer* db) { |
| // The frame is not paddable. |
| QUICHE_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::END_STREAM | Http2FrameFlag::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. |
| QUICHE_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::END_STREAM | Http2FrameFlag::END_HEADERS | |
| Http2FrameFlag::PADDED | Http2FrameFlag::PRIORITY); |
| return headers_payload_decoder_.StartDecodingPayload(&frame_decoder_state_, |
| db); |
| } |
| DecodeStatus Http2FrameDecoder::ResumeDecodingHeadersPayload(DecodeBuffer* db) { |
| QUICHE_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::ACK); |
| return ping_payload_decoder_.StartDecodingPayload(&frame_decoder_state_, db); |
| } |
| DecodeStatus Http2FrameDecoder::ResumeDecodingPingPayload(DecodeBuffer* db) { |
| // The frame is not paddable. |
| QUICHE_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. |
| QUICHE_DCHECK_EQ(frame_decoder_state_.remaining_total_payload(), |
| frame_decoder_state_.remaining_payload()); |
| return priority_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_, |
| db); |
| } |
| |
| DecodeStatus Http2FrameDecoder::StartDecodingPriorityUpdatePayload( |
| DecodeBuffer* db) { |
| ClearFlags(); |
| return priority_payload_update_decoder_.StartDecodingPayload( |
| &frame_decoder_state_, db); |
| } |
| DecodeStatus Http2FrameDecoder::ResumeDecodingPriorityUpdatePayload( |
| DecodeBuffer* db) { |
| // The frame is not paddable. |
| QUICHE_DCHECK_EQ(frame_decoder_state_.remaining_total_payload(), |
| frame_decoder_state_.remaining_payload()); |
| return priority_payload_update_decoder_.ResumeDecodingPayload( |
| &frame_decoder_state_, db); |
| } |
| |
| DecodeStatus Http2FrameDecoder::StartDecodingPushPromisePayload( |
| DecodeBuffer* db) { |
| RetainFlags(Http2FrameFlag::END_HEADERS | Http2FrameFlag::PADDED); |
| return push_promise_payload_decoder_.StartDecodingPayload( |
| &frame_decoder_state_, db); |
| } |
| DecodeStatus Http2FrameDecoder::ResumeDecodingPushPromisePayload( |
| DecodeBuffer* db) { |
| QUICHE_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. |
| QUICHE_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::ACK); |
| return settings_payload_decoder_.StartDecodingPayload(&frame_decoder_state_, |
| db); |
| } |
| DecodeStatus Http2FrameDecoder::ResumeDecodingSettingsPayload( |
| DecodeBuffer* db) { |
| // The frame is not paddable. |
| QUICHE_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. |
| QUICHE_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. |
| QUICHE_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) { |
| HTTP2_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); |
| HTTP2_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 http2 |