// 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/third_party/http2/decoder/http2_frame_decoder.h" #include "net/third_party/http2/http2_constants.h" #include "net/third_party/http2/tools/http2_bug_tracker.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(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) { 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::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::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. 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) { 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. 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::END_HEADERS | Http2FrameFlag::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::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 http2