// Copyright 2017 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/quic/http/decoder/quic_http_frame_decoder.h" #include "net/quic/http/quic_http_constants.h" #include "net/quic/platform/api/quic_bug_tracker.h" #include "net/quic/platform/api/quic_logging.h" namespace net { std::ostream& operator<<(std::ostream& out, QuicHttpFrameDecoder::State v) { switch (v) { case QuicHttpFrameDecoder::State::kStartDecodingHeader: return out << "kStartDecodingHeader"; case QuicHttpFrameDecoder::State::kResumeDecodingHeader: return out << "kResumeDecodingHeader"; case QuicHttpFrameDecoder::State::kResumeDecodingPayload: return out << "kResumeDecodingPayload"; case QuicHttpFrameDecoder::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); QUIC_BUG << "QuicHttpFrameDecoder::State " << unknown; return out << "QuicHttpFrameDecoder::State(" << unknown << ")"; } QuicHttpFrameDecoder::QuicHttpFrameDecoder( QuicHttpFrameDecoderListener* listener) : state_(State::kStartDecodingHeader), maximum_payload_size_(QuicHttpSettingsInfo::DefaultMaxFrameSize()) { set_listener(listener); } void QuicHttpFrameDecoder::set_listener( QuicHttpFrameDecoderListener* listener) { if (listener == nullptr) { listener = &no_op_listener_; } frame_decoder_state_.set_listener(listener); } QuicHttpFrameDecoderListener* QuicHttpFrameDecoder::listener() const { return frame_decoder_state_.listener(); } QuicHttpDecodeStatus QuicHttpFrameDecoder::DecodeFrame( QuicHttpDecodeBuffer* db) { DVLOG(2) << "QuicHttpFrameDecoder::DecodeFrame state=" << state_; switch (state_) { case State::kStartDecodingHeader: if (frame_decoder_state_.StartDecodingFrameHeader(db)) { return StartDecodingPayload(db); } state_ = State::kResumeDecodingHeader; return QuicHttpDecodeStatus::kDecodeInProgress; case State::kResumeDecodingHeader: if (frame_decoder_state_.ResumeDecodingFrameHeader(db)) { return StartDecodingPayload(db); } return QuicHttpDecodeStatus::kDecodeInProgress; case State::kResumeDecodingPayload: return ResumeDecodingPayload(db); case State::kDiscardPayload: return DiscardPayload(db); } QUIC_NOTREACHED(); return QuicHttpDecodeStatus::kDecodeError; } size_t QuicHttpFrameDecoder::remaining_payload() const { return frame_decoder_state_.remaining_payload(); } uint32_t QuicHttpFrameDecoder::remaining_padding() const { return frame_decoder_state_.remaining_padding(); } QuicHttpDecodeStatus QuicHttpFrameDecoder::StartDecodingPayload( QuicHttpDecodeBuffer* db) { const QuicHttpFrameHeader& 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 QuicHttpDecodeStatus::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 QuicHttpDecodeStatus::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. QuicHttpDecodeBufferSubset subset(db, header.payload_length); QuicHttpDecodeStatus status; switch (header.type) { case QuicHttpFrameType::DATA: status = StartDecodingDataPayload(&subset); break; case QuicHttpFrameType::HEADERS: status = StartDecodingHeadersPayload(&subset); break; case QuicHttpFrameType::QUIC_HTTP_PRIORITY: status = StartDecodingPriorityPayload(&subset); break; case QuicHttpFrameType::RST_STREAM: status = StartDecodingRstStreamPayload(&subset); break; case QuicHttpFrameType::SETTINGS: status = StartDecodingSettingsPayload(&subset); break; case QuicHttpFrameType::PUSH_PROMISE: status = StartDecodingPushPromisePayload(&subset); break; case QuicHttpFrameType::PING: status = StartDecodingPingPayload(&subset); break; case QuicHttpFrameType::GOAWAY: status = StartDecodingGoAwayPayload(&subset); break; case QuicHttpFrameType::WINDOW_UPDATE: status = StartDecodingWindowUpdatePayload(&subset); break; case QuicHttpFrameType::CONTINUATION: status = StartDecodingContinuationPayload(&subset); break; case QuicHttpFrameType::ALTSVC: status = StartDecodingAltSvcPayload(&subset); break; default: status = StartDecodingUnknownPayload(&subset); break; } if (status == QuicHttpDecodeStatus::kDecodeDone) { state_ = State::kStartDecodingHeader; return status; } else if (status == QuicHttpDecodeStatus::kDecodeInProgress) { state_ = State::kResumeDecodingPayload; return status; } else { state_ = State::kDiscardPayload; return status; } } QuicHttpDecodeStatus QuicHttpFrameDecoder::ResumeDecodingPayload( QuicHttpDecodeBuffer* 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); QuicHttpDecodeBufferSubset subset(db, remaining); QuicHttpDecodeStatus status; switch (frame_header().type) { case QuicHttpFrameType::DATA: status = ResumeDecodingDataPayload(&subset); break; case QuicHttpFrameType::HEADERS: status = ResumeDecodingHeadersPayload(&subset); break; case QuicHttpFrameType::QUIC_HTTP_PRIORITY: status = ResumeDecodingPriorityPayload(&subset); break; case QuicHttpFrameType::RST_STREAM: status = ResumeDecodingRstStreamPayload(&subset); break; case QuicHttpFrameType::SETTINGS: status = ResumeDecodingSettingsPayload(&subset); break; case QuicHttpFrameType::PUSH_PROMISE: status = ResumeDecodingPushPromisePayload(&subset); break; case QuicHttpFrameType::PING: status = ResumeDecodingPingPayload(&subset); break; case QuicHttpFrameType::GOAWAY: status = ResumeDecodingGoAwayPayload(&subset); break; case QuicHttpFrameType::WINDOW_UPDATE: status = ResumeDecodingWindowUpdatePayload(&subset); break; case QuicHttpFrameType::CONTINUATION: status = ResumeDecodingContinuationPayload(&subset); break; case QuicHttpFrameType::ALTSVC: status = ResumeDecodingAltSvcPayload(&subset); break; default: status = ResumeDecodingUnknownPayload(&subset); break; } if (status == QuicHttpDecodeStatus::kDecodeDone) { state_ = State::kStartDecodingHeader; return status; } else if (status == QuicHttpDecodeStatus::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 QuicHttpFrameDecoder::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 QuicHttpFrameDecoder::ClearFlags() { frame_decoder_state_.ClearFlags(); } QuicHttpDecodeStatus QuicHttpFrameDecoder::StartDecodingAltSvcPayload( QuicHttpDecodeBuffer* db) { ClearFlags(); return altsvc_payload_decoder_.StartDecodingPayload(&frame_decoder_state_, db); } QuicHttpDecodeStatus QuicHttpFrameDecoder::ResumeDecodingAltSvcPayload( QuicHttpDecodeBuffer* 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); } QuicHttpDecodeStatus QuicHttpFrameDecoder::StartDecodingContinuationPayload( QuicHttpDecodeBuffer* db) { RetainFlags(QuicHttpFrameFlag::QUIC_HTTP_END_HEADERS); return continuation_payload_decoder_.StartDecodingPayload( &frame_decoder_state_, db); } QuicHttpDecodeStatus QuicHttpFrameDecoder::ResumeDecodingContinuationPayload( QuicHttpDecodeBuffer* 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); } QuicHttpDecodeStatus QuicHttpFrameDecoder::StartDecodingDataPayload( QuicHttpDecodeBuffer* db) { RetainFlags(QuicHttpFrameFlag::QUIC_HTTP_END_STREAM | QuicHttpFrameFlag::QUIC_HTTP_PADDED); return data_payload_decoder_.StartDecodingPayload(&frame_decoder_state_, db); } QuicHttpDecodeStatus QuicHttpFrameDecoder::ResumeDecodingDataPayload( QuicHttpDecodeBuffer* db) { return data_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_, db); } QuicHttpDecodeStatus QuicHttpFrameDecoder::StartDecodingGoAwayPayload( QuicHttpDecodeBuffer* db) { ClearFlags(); return goaway_payload_decoder_.StartDecodingPayload(&frame_decoder_state_, db); } QuicHttpDecodeStatus QuicHttpFrameDecoder::ResumeDecodingGoAwayPayload( QuicHttpDecodeBuffer* 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); } QuicHttpDecodeStatus QuicHttpFrameDecoder::StartDecodingHeadersPayload( QuicHttpDecodeBuffer* db) { RetainFlags(QuicHttpFrameFlag::QUIC_HTTP_END_STREAM | QuicHttpFrameFlag::QUIC_HTTP_END_HEADERS | QuicHttpFrameFlag::QUIC_HTTP_PADDED | QuicHttpFrameFlag::QUIC_HTTP_PRIORITY); return headers_payload_decoder_.StartDecodingPayload(&frame_decoder_state_, db); } QuicHttpDecodeStatus QuicHttpFrameDecoder::ResumeDecodingHeadersPayload( QuicHttpDecodeBuffer* db) { DCHECK_LE(frame_decoder_state_.remaining_payload_and_padding(), frame_header().payload_length); return headers_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_, db); } QuicHttpDecodeStatus QuicHttpFrameDecoder::StartDecodingPingPayload( QuicHttpDecodeBuffer* db) { RetainFlags(QuicHttpFrameFlag::QUIC_HTTP_ACK); return ping_payload_decoder_.StartDecodingPayload(&frame_decoder_state_, db); } QuicHttpDecodeStatus QuicHttpFrameDecoder::ResumeDecodingPingPayload( QuicHttpDecodeBuffer* 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); } QuicHttpDecodeStatus QuicHttpFrameDecoder::StartDecodingPriorityPayload( QuicHttpDecodeBuffer* db) { ClearFlags(); return priority_payload_decoder_.StartDecodingPayload(&frame_decoder_state_, db); } QuicHttpDecodeStatus QuicHttpFrameDecoder::ResumeDecodingPriorityPayload( QuicHttpDecodeBuffer* 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); } QuicHttpDecodeStatus QuicHttpFrameDecoder::StartDecodingPushPromisePayload( QuicHttpDecodeBuffer* db) { RetainFlags(QuicHttpFrameFlag::QUIC_HTTP_END_HEADERS | QuicHttpFrameFlag::QUIC_HTTP_PADDED); return push_promise_payload_decoder_.StartDecodingPayload( &frame_decoder_state_, db); } QuicHttpDecodeStatus QuicHttpFrameDecoder::ResumeDecodingPushPromisePayload( QuicHttpDecodeBuffer* db) { DCHECK_LE(frame_decoder_state_.remaining_payload_and_padding(), frame_header().payload_length); return push_promise_payload_decoder_.ResumeDecodingPayload( &frame_decoder_state_, db); } QuicHttpDecodeStatus QuicHttpFrameDecoder::StartDecodingRstStreamPayload( QuicHttpDecodeBuffer* db) { ClearFlags(); return rst_stream_payload_decoder_.StartDecodingPayload(&frame_decoder_state_, db); } QuicHttpDecodeStatus QuicHttpFrameDecoder::ResumeDecodingRstStreamPayload( QuicHttpDecodeBuffer* 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); } QuicHttpDecodeStatus QuicHttpFrameDecoder::StartDecodingSettingsPayload( QuicHttpDecodeBuffer* db) { RetainFlags(QuicHttpFrameFlag::QUIC_HTTP_ACK); return settings_payload_decoder_.StartDecodingPayload(&frame_decoder_state_, db); } QuicHttpDecodeStatus QuicHttpFrameDecoder::ResumeDecodingSettingsPayload( QuicHttpDecodeBuffer* 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); } QuicHttpDecodeStatus QuicHttpFrameDecoder::StartDecodingUnknownPayload( QuicHttpDecodeBuffer* 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); } QuicHttpDecodeStatus QuicHttpFrameDecoder::ResumeDecodingUnknownPayload( QuicHttpDecodeBuffer* 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); } QuicHttpDecodeStatus QuicHttpFrameDecoder::StartDecodingWindowUpdatePayload( QuicHttpDecodeBuffer* db) { ClearFlags(); return window_update_payload_decoder_.StartDecodingPayload( &frame_decoder_state_, db); } QuicHttpDecodeStatus QuicHttpFrameDecoder::ResumeDecodingWindowUpdatePayload( QuicHttpDecodeBuffer* 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); } QuicHttpDecodeStatus QuicHttpFrameDecoder::DiscardPayload( QuicHttpDecodeBuffer* 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 QuicHttpDecodeStatus::kDecodeDone; } return QuicHttpDecodeStatus::kDecodeInProgress; } } // namespace net