| 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
 | 
| 
 |