Project import generated by Copybara.

PiperOrigin-RevId: 237361882
Change-Id: I109a68f44db867b20f8c6a7732b0ce657133e52a
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc
new file mode 100644
index 0000000..01e630d
--- /dev/null
+++ b/quic/core/http/quic_spdy_session.cc
@@ -0,0 +1,696 @@
+// Copyright (c) 2015 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/quiche/src/quic/core/http/quic_spdy_session.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_headers_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h"
+
+using http2::Http2DecoderAdapter;
+using spdy::HpackEntry;
+using spdy::HpackHeaderTable;
+using spdy::Http2WeightToSpdy3Priority;
+using spdy::SETTINGS_ENABLE_PUSH;
+using spdy::SETTINGS_HEADER_TABLE_SIZE;
+using spdy::SETTINGS_MAX_HEADER_LIST_SIZE;
+using spdy::Spdy3PriorityToHttp2Weight;
+using spdy::SpdyErrorCode;
+using spdy::SpdyFramer;
+using spdy::SpdyFramerDebugVisitorInterface;
+using spdy::SpdyFramerVisitorInterface;
+using spdy::SpdyFrameType;
+using spdy::SpdyHeaderBlock;
+using spdy::SpdyHeadersHandlerInterface;
+using spdy::SpdyHeadersIR;
+using spdy::SpdyKnownSettingsId;
+using spdy::SpdyPingId;
+using spdy::SpdyPriority;
+using spdy::SpdyPriorityIR;
+using spdy::SpdyPushPromiseIR;
+using spdy::SpdySerializedFrame;
+using spdy::SpdySettingsId;
+using spdy::SpdySettingsIR;
+using spdy::SpdyStreamId;
+
+namespace quic {
+
+namespace {
+
+class HeaderTableDebugVisitor : public HpackHeaderTable::DebugVisitorInterface {
+ public:
+  HeaderTableDebugVisitor(const QuicClock* clock,
+                          std::unique_ptr<QuicHpackDebugVisitor> visitor)
+      : clock_(clock), headers_stream_hpack_visitor_(std::move(visitor)) {}
+  HeaderTableDebugVisitor(const HeaderTableDebugVisitor&) = delete;
+  HeaderTableDebugVisitor& operator=(const HeaderTableDebugVisitor&) = delete;
+
+  int64_t OnNewEntry(const HpackEntry& entry) override {
+    QUIC_DVLOG(1) << entry.GetDebugString();
+    return (clock_->ApproximateNow() - QuicTime::Zero()).ToMicroseconds();
+  }
+
+  void OnUseEntry(const HpackEntry& entry) override {
+    const QuicTime::Delta elapsed(
+        clock_->ApproximateNow() -
+        QuicTime::Delta::FromMicroseconds(entry.time_added()) -
+        QuicTime::Zero());
+    QUIC_DVLOG(1) << entry.GetDebugString() << " " << elapsed.ToMilliseconds()
+                  << " ms";
+    headers_stream_hpack_visitor_->OnUseEntry(elapsed);
+  }
+
+ private:
+  const QuicClock* clock_;
+  std::unique_ptr<QuicHpackDebugVisitor> headers_stream_hpack_visitor_;
+};
+
+}  // namespace
+
+// A SpdyFramerVisitor that passes HEADERS frames to the QuicSpdyStream, and
+// closes the connection if any unexpected frames are received.
+class QuicSpdySession::SpdyFramerVisitor
+    : public SpdyFramerVisitorInterface,
+      public SpdyFramerDebugVisitorInterface {
+ public:
+  explicit SpdyFramerVisitor(QuicSpdySession* session) : session_(session) {}
+  SpdyFramerVisitor(const SpdyFramerVisitor&) = delete;
+  SpdyFramerVisitor& operator=(const SpdyFramerVisitor&) = delete;
+
+  SpdyHeadersHandlerInterface* OnHeaderFrameStart(
+      SpdyStreamId /* stream_id */) override {
+    return &header_list_;
+  }
+
+  void OnHeaderFrameEnd(SpdyStreamId /* stream_id */) override {
+    if (session_->IsConnected()) {
+      session_->OnHeaderList(header_list_);
+    }
+    header_list_.Clear();
+  }
+
+  void OnStreamFrameData(SpdyStreamId stream_id,
+                         const char* data,
+                         size_t len) override {
+    CloseConnection("SPDY DATA frame received.",
+                    QUIC_INVALID_HEADERS_STREAM_DATA);
+  }
+
+  void OnStreamEnd(SpdyStreamId stream_id) override {
+    // The framer invokes OnStreamEnd after processing a frame that had the fin
+    // bit set.
+  }
+
+  void OnStreamPadding(SpdyStreamId stream_id, size_t len) override {
+    CloseConnection("SPDY frame padding received.",
+                    QUIC_INVALID_HEADERS_STREAM_DATA);
+  }
+
+  void OnError(Http2DecoderAdapter::SpdyFramerError error) override {
+    QuicErrorCode code = QUIC_INVALID_HEADERS_STREAM_DATA;
+    switch (error) {
+      case Http2DecoderAdapter::SpdyFramerError::SPDY_DECOMPRESS_FAILURE:
+        code = QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE;
+        break;
+      default:
+        break;
+    }
+    CloseConnection(
+        QuicStrCat("SPDY framing error: ",
+                   Http2DecoderAdapter::SpdyFramerErrorToString(error)),
+        code);
+  }
+
+  void OnDataFrameHeader(SpdyStreamId stream_id,
+                         size_t length,
+                         bool fin) override {
+    CloseConnection("SPDY DATA frame received.",
+                    QUIC_INVALID_HEADERS_STREAM_DATA);
+  }
+
+  void OnRstStream(SpdyStreamId stream_id, SpdyErrorCode error_code) override {
+    CloseConnection("SPDY RST_STREAM frame received.",
+                    QUIC_INVALID_HEADERS_STREAM_DATA);
+  }
+
+  void OnSetting(SpdySettingsId id, uint32_t value) override {
+    switch (id) {
+      case SETTINGS_HEADER_TABLE_SIZE:
+        session_->UpdateHeaderEncoderTableSize(value);
+        break;
+      case SETTINGS_ENABLE_PUSH:
+        if (session_->perspective() == Perspective::IS_SERVER) {
+          // See rfc7540, Section 6.5.2.
+          if (value > 1) {
+            CloseConnection(
+                QuicStrCat("Invalid value for SETTINGS_ENABLE_PUSH: ", value),
+                QUIC_INVALID_HEADERS_STREAM_DATA);
+            return;
+          }
+          session_->UpdateEnableServerPush(value > 0);
+          break;
+        } else {
+          CloseConnection(
+              QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ", id),
+              QUIC_INVALID_HEADERS_STREAM_DATA);
+        }
+        break;
+      // TODO(fayang): Need to support SETTINGS_MAX_HEADER_LIST_SIZE when
+      // clients are actually sending it.
+      case SETTINGS_MAX_HEADER_LIST_SIZE:
+        break;
+      default:
+        CloseConnection(
+            QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ", id),
+            QUIC_INVALID_HEADERS_STREAM_DATA);
+    }
+  }
+
+  void OnSettingsEnd() override {}
+
+  void OnPing(SpdyPingId unique_id, bool is_ack) override {
+    CloseConnection("SPDY PING frame received.",
+                    QUIC_INVALID_HEADERS_STREAM_DATA);
+  }
+
+  void OnGoAway(SpdyStreamId last_accepted_stream_id,
+                SpdyErrorCode error_code) override {
+    CloseConnection("SPDY GOAWAY frame received.",
+                    QUIC_INVALID_HEADERS_STREAM_DATA);
+  }
+
+  void OnHeaders(SpdyStreamId stream_id,
+                 bool has_priority,
+                 int weight,
+                 SpdyStreamId /*parent_stream_id*/,
+                 bool /*exclusive*/,
+                 bool fin,
+                 bool end) override {
+    if (!session_->IsConnected()) {
+      return;
+    }
+
+    // TODO(mpw): avoid down-conversion and plumb SpdyStreamPrecedence through
+    // QuicHeadersStream.
+    SpdyPriority priority =
+        has_priority ? Http2WeightToSpdy3Priority(weight) : 0;
+    session_->OnHeaders(stream_id, has_priority, priority, fin);
+  }
+
+  void OnWindowUpdate(SpdyStreamId stream_id, int delta_window_size) override {
+    CloseConnection("SPDY WINDOW_UPDATE frame received.",
+                    QUIC_INVALID_HEADERS_STREAM_DATA);
+  }
+
+  void OnPushPromise(SpdyStreamId stream_id,
+                     SpdyStreamId promised_stream_id,
+                     bool end) override {
+    if (!session_->supports_push_promise()) {
+      CloseConnection("PUSH_PROMISE not supported.",
+                      QUIC_INVALID_HEADERS_STREAM_DATA);
+      return;
+    }
+    if (!session_->IsConnected()) {
+      return;
+    }
+    session_->OnPushPromise(stream_id, promised_stream_id, end);
+  }
+
+  void OnContinuation(SpdyStreamId stream_id, bool end) override {}
+
+  void OnPriority(SpdyStreamId stream_id,
+                  SpdyStreamId parent_id,
+                  int weight,
+                  bool exclusive) override {
+    if (session_->connection()->transport_version() <= QUIC_VERSION_39) {
+      CloseConnection("SPDY PRIORITY frame received.",
+                      QUIC_INVALID_HEADERS_STREAM_DATA);
+      return;
+    }
+    if (!session_->IsConnected()) {
+      return;
+    }
+    // TODO (wangyix): implement real HTTP/2 weights and dependencies instead of
+    // converting to SpdyPriority.
+    SpdyPriority priority = Http2WeightToSpdy3Priority(weight);
+    session_->OnPriority(stream_id, priority);
+  }
+
+  bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) override {
+    CloseConnection("Unknown frame type received.",
+                    QUIC_INVALID_HEADERS_STREAM_DATA);
+    return false;
+  }
+
+  // SpdyFramerDebugVisitorInterface implementation
+  void OnSendCompressedFrame(SpdyStreamId stream_id,
+                             SpdyFrameType type,
+                             size_t payload_len,
+                             size_t frame_len) override {
+    if (payload_len == 0) {
+      QUIC_BUG << "Zero payload length.";
+      return;
+    }
+    int compression_pct = 100 - (100 * frame_len) / payload_len;
+    QUIC_DVLOG(1) << "Net.QuicHpackCompressionPercentage: " << compression_pct;
+  }
+
+  void OnReceiveCompressedFrame(SpdyStreamId stream_id,
+                                SpdyFrameType type,
+                                size_t frame_len) override {
+    if (session_->IsConnected()) {
+      session_->OnCompressedFrameSize(frame_len);
+    }
+  }
+
+  void set_max_uncompressed_header_bytes(
+      size_t set_max_uncompressed_header_bytes) {
+    header_list_.set_max_header_list_size(set_max_uncompressed_header_bytes);
+  }
+
+ private:
+  void CloseConnection(const QuicString& details, QuicErrorCode code) {
+    if (session_->IsConnected()) {
+      session_->CloseConnectionWithDetails(code, details);
+    }
+  }
+
+ private:
+  QuicSpdySession* session_;
+  QuicHeaderList header_list_;
+};
+
+QuicHpackDebugVisitor::QuicHpackDebugVisitor() {}
+
+QuicHpackDebugVisitor::~QuicHpackDebugVisitor() {}
+
+QuicSpdySession::QuicSpdySession(
+    QuicConnection* connection,
+    QuicSession::Visitor* visitor,
+    const QuicConfig& config,
+    const ParsedQuicVersionVector& supported_versions)
+    : QuicSession(connection, visitor, config, supported_versions),
+      max_inbound_header_list_size_(kDefaultMaxUncompressedHeaderSize),
+      server_push_enabled_(true),
+      stream_id_(
+          QuicUtils::GetInvalidStreamId(connection->transport_version())),
+      promised_stream_id_(
+          QuicUtils::GetInvalidStreamId(connection->transport_version())),
+      fin_(false),
+      frame_len_(0),
+      uncompressed_frame_len_(0),
+      supports_push_promise_(perspective() == Perspective::IS_CLIENT),
+      spdy_framer_(SpdyFramer::ENABLE_COMPRESSION),
+      spdy_framer_visitor_(new SpdyFramerVisitor(this)) {
+  h2_deframer_.set_visitor(spdy_framer_visitor_.get());
+  h2_deframer_.set_debug_visitor(spdy_framer_visitor_.get());
+  spdy_framer_.set_debug_visitor(spdy_framer_visitor_.get());
+}
+
+QuicSpdySession::~QuicSpdySession() {
+  // Set the streams' session pointers in closed and dynamic stream lists
+  // to null to avoid subsequent use of this session.
+  for (auto& stream : *closed_streams()) {
+    static_cast<QuicSpdyStream*>(stream.get())->ClearSession();
+  }
+  for (auto const& kv : zombie_streams()) {
+    static_cast<QuicSpdyStream*>(kv.second.get())->ClearSession();
+  }
+  for (auto const& kv : dynamic_streams()) {
+    static_cast<QuicSpdyStream*>(kv.second.get())->ClearSession();
+  }
+}
+
+void QuicSpdySession::Initialize() {
+  QuicSession::Initialize();
+
+  if (perspective() == Perspective::IS_SERVER) {
+    set_largest_peer_created_stream_id(
+        QuicUtils::GetHeadersStreamId(connection()->transport_version()));
+  } else {
+    QuicStreamId headers_stream_id = GetNextOutgoingBidirectionalStreamId();
+    DCHECK_EQ(headers_stream_id,
+              QuicUtils::GetHeadersStreamId(connection()->transport_version()));
+  }
+
+  if (VersionUsesQpack(connection()->transport_version())) {
+    qpack_encoder_ = QuicMakeUnique<QpackEncoder>(this, this);
+    qpack_decoder_ = QuicMakeUnique<QpackDecoder>(this, this);
+  }
+
+  headers_stream_ = QuicMakeUnique<QuicHeadersStream>((this));
+  DCHECK_EQ(QuicUtils::GetHeadersStreamId(connection()->transport_version()),
+            headers_stream_->id());
+  RegisterStaticStream(
+      QuicUtils::GetHeadersStreamId(connection()->transport_version()),
+      headers_stream_.get());
+
+  set_max_uncompressed_header_bytes(max_inbound_header_list_size_);
+
+  // Limit HPACK buffering to 2x header list size limit.
+  set_max_decode_buffer_size_bytes(2 * max_inbound_header_list_size_);
+}
+
+void QuicSpdySession::OnDecoderStreamError(QuicStringPiece error_message) {
+  DCHECK(VersionUsesQpack(connection()->transport_version()));
+
+  // TODO(112770235): Signal connection error on decoder stream errors.
+  QUIC_NOTREACHED();
+}
+
+void QuicSpdySession::WriteEncoderStreamData(QuicStringPiece data) {
+  DCHECK(VersionUsesQpack(connection()->transport_version()));
+
+  // TODO(112770235): Send encoder stream data on encoder stream.
+  QUIC_NOTREACHED();
+}
+
+void QuicSpdySession::OnEncoderStreamError(QuicStringPiece error_message) {
+  DCHECK(VersionUsesQpack(connection()->transport_version()));
+
+  // TODO(112770235): Signal connection error on encoder stream errors.
+  QUIC_NOTREACHED();
+}
+
+void QuicSpdySession::WriteDecoderStreamData(QuicStringPiece data) {
+  DCHECK(VersionUsesQpack(connection()->transport_version()));
+
+  // TODO(112770235): Send decoder stream data on decoder stream.
+  QUIC_NOTREACHED();
+}
+
+void QuicSpdySession::OnStreamHeadersPriority(QuicStreamId stream_id,
+                                              SpdyPriority priority) {
+  QuicSpdyStream* stream = GetSpdyDataStream(stream_id);
+  if (!stream) {
+    // It's quite possible to receive headers after a stream has been reset.
+    return;
+  }
+  stream->OnStreamHeadersPriority(priority);
+}
+
+void QuicSpdySession::OnStreamHeaderList(QuicStreamId stream_id,
+                                         bool fin,
+                                         size_t frame_len,
+                                         const QuicHeaderList& header_list) {
+  if (QuicContainsKey(static_streams(), stream_id)) {
+    connection()->CloseConnection(
+        QUIC_INVALID_HEADERS_STREAM_DATA, "stream is static",
+        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+    return;
+  }
+  QuicSpdyStream* stream = GetSpdyDataStream(stream_id);
+  if (stream == nullptr) {
+    // The stream no longer exists, but trailing headers may contain the final
+    // byte offset necessary for flow control and open stream accounting.
+    size_t final_byte_offset = 0;
+    for (const auto& header : header_list) {
+      const QuicString& header_key = header.first;
+      const QuicString& header_value = header.second;
+      if (header_key == kFinalOffsetHeaderKey) {
+        if (!QuicTextUtils::StringToSizeT(header_value, &final_byte_offset)) {
+          connection()->CloseConnection(
+              QUIC_INVALID_HEADERS_STREAM_DATA,
+              "Trailers are malformed (no final offset)",
+              ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+          return;
+        }
+        DVLOG(1) << "Received final byte offset in trailers for stream "
+                 << stream_id << ", which no longer exists.";
+        OnFinalByteOffsetReceived(stream_id, final_byte_offset);
+      }
+    }
+
+    // It's quite possible to receive headers after a stream has been reset.
+    return;
+  }
+  stream->OnStreamHeaderList(fin, frame_len, header_list);
+}
+
+void QuicSpdySession::OnPriorityFrame(QuicStreamId stream_id,
+                                      SpdyPriority priority) {
+  QuicSpdyStream* stream = GetSpdyDataStream(stream_id);
+  if (!stream) {
+    // It's quite possible to receive a PRIORITY frame after a stream has been
+    // reset.
+    return;
+  }
+  stream->OnPriorityFrame(priority);
+}
+
+size_t QuicSpdySession::ProcessHeaderData(const struct iovec& iov) {
+  return h2_deframer_.ProcessInput(static_cast<char*>(iov.iov_base),
+                                   iov.iov_len);
+}
+
+size_t QuicSpdySession::WriteHeadersOnHeadersStream(
+    QuicStreamId id,
+    SpdyHeaderBlock headers,
+    bool fin,
+    SpdyPriority priority,
+    QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+  return WriteHeadersOnHeadersStreamImpl(
+      id, std::move(headers), fin,
+      /* parent_stream_id = */ 0, Spdy3PriorityToHttp2Weight(priority),
+      /* exclusive = */ false, std::move(ack_listener));
+}
+
+size_t QuicSpdySession::WritePriority(QuicStreamId id,
+                                      QuicStreamId parent_stream_id,
+                                      int weight,
+                                      bool exclusive) {
+  if (connection()->transport_version() <= QUIC_VERSION_39) {
+    return 0;
+  }
+  SpdyPriorityIR priority_frame(id, parent_stream_id, weight, exclusive);
+  SpdySerializedFrame frame(spdy_framer_.SerializeFrame(priority_frame));
+  headers_stream_->WriteOrBufferData(
+      QuicStringPiece(frame.data(), frame.size()), false, nullptr);
+  return frame.size();
+}
+
+size_t QuicSpdySession::WritePushPromise(QuicStreamId original_stream_id,
+                                         QuicStreamId promised_stream_id,
+                                         SpdyHeaderBlock headers) {
+  if (perspective() == Perspective::IS_CLIENT) {
+    QUIC_BUG << "Client shouldn't send PUSH_PROMISE";
+    return 0;
+  }
+
+  SpdyPushPromiseIR push_promise(original_stream_id, promised_stream_id,
+                                 std::move(headers));
+  // PUSH_PROMISE must not be the last frame sent out, at least followed by
+  // response headers.
+  push_promise.set_fin(false);
+
+  SpdySerializedFrame frame(spdy_framer_.SerializeFrame(push_promise));
+  headers_stream_->WriteOrBufferData(
+      QuicStringPiece(frame.data(), frame.size()), false, nullptr);
+  return frame.size();
+}
+
+size_t QuicSpdySession::SendMaxHeaderListSize(size_t value) {
+  SpdySettingsIR settings_frame;
+  settings_frame.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, value);
+
+  SpdySerializedFrame frame(spdy_framer_.SerializeFrame(settings_frame));
+  headers_stream_->WriteOrBufferData(
+      QuicStringPiece(frame.data(), frame.size()), false, nullptr);
+  return frame.size();
+}
+
+QpackEncoder* QuicSpdySession::qpack_encoder() {
+  DCHECK(VersionUsesQpack(connection()->transport_version()));
+
+  return qpack_encoder_.get();
+}
+
+QpackDecoder* QuicSpdySession::qpack_decoder() {
+  DCHECK(VersionUsesQpack(connection()->transport_version()));
+
+  return qpack_decoder_.get();
+}
+
+QuicSpdyStream* QuicSpdySession::GetSpdyDataStream(
+    const QuicStreamId stream_id) {
+  return static_cast<QuicSpdyStream*>(GetOrCreateDynamicStream(stream_id));
+}
+
+void QuicSpdySession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
+  QuicSession::OnCryptoHandshakeEvent(event);
+  if (event == HANDSHAKE_CONFIRMED && config()->SupportMaxHeaderListSize()) {
+    SendMaxHeaderListSize(max_inbound_header_list_size_);
+  }
+}
+
+// True if there are open HTTP requests.
+bool QuicSpdySession::ShouldKeepConnectionAlive() const {
+  // Change to check if there are open HTTP requests.
+  // When IETF QUIC control and QPACK streams are used, those will need to be
+  // subtracted from this count to ensure only request streams are counted.
+  return GetNumOpenDynamicStreams() > 0;
+}
+
+bool QuicSpdySession::ShouldBufferIncomingStream(QuicStreamId id) const {
+  DCHECK_EQ(QUIC_VERSION_99, connection()->transport_version());
+  return !QuicUtils::IsBidirectionalStreamId(id);
+}
+
+size_t QuicSpdySession::WriteHeadersOnHeadersStreamImpl(
+    QuicStreamId id,
+    spdy::SpdyHeaderBlock headers,
+    bool fin,
+    QuicStreamId parent_stream_id,
+    int weight,
+    bool exclusive,
+    QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+  SpdyHeadersIR headers_frame(id, std::move(headers));
+  headers_frame.set_fin(fin);
+  if (perspective() == Perspective::IS_CLIENT) {
+    headers_frame.set_has_priority(true);
+    headers_frame.set_parent_stream_id(parent_stream_id);
+    headers_frame.set_weight(weight);
+    headers_frame.set_exclusive(exclusive);
+  }
+  SpdySerializedFrame frame(spdy_framer_.SerializeFrame(headers_frame));
+  headers_stream_->WriteOrBufferData(
+      QuicStringPiece(frame.data(), frame.size()), false,
+      std::move(ack_listener));
+  return frame.size();
+}
+
+void QuicSpdySession::OnPromiseHeaderList(QuicStreamId stream_id,
+                                          QuicStreamId promised_stream_id,
+                                          size_t frame_len,
+                                          const QuicHeaderList& header_list) {
+  QuicString error = "OnPromiseHeaderList should be overridden in client code.";
+  QUIC_BUG << error;
+  connection()->CloseConnection(QUIC_INTERNAL_ERROR, error,
+                                ConnectionCloseBehavior::SILENT_CLOSE);
+}
+
+bool QuicSpdySession::ShouldReleaseHeadersStreamSequencerBuffer() {
+  return false;
+}
+
+void QuicSpdySession::OnHeaders(SpdyStreamId stream_id,
+                                bool has_priority,
+                                SpdyPriority priority,
+                                bool fin) {
+  if (has_priority) {
+    if (perspective() == Perspective::IS_CLIENT) {
+      CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
+                                 "Server must not send priorities.");
+      return;
+    }
+    OnStreamHeadersPriority(stream_id, priority);
+  } else {
+    if (perspective() == Perspective::IS_SERVER) {
+      CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
+                                 "Client must send priorities.");
+      return;
+    }
+  }
+  DCHECK_EQ(QuicUtils::GetInvalidStreamId(connection()->transport_version()),
+            stream_id_);
+  DCHECK_EQ(QuicUtils::GetInvalidStreamId(connection()->transport_version()),
+            promised_stream_id_);
+  stream_id_ = stream_id;
+  fin_ = fin;
+}
+
+void QuicSpdySession::OnPushPromise(SpdyStreamId stream_id,
+                                    SpdyStreamId promised_stream_id,
+                                    bool end) {
+  DCHECK_EQ(QuicUtils::GetInvalidStreamId(connection()->transport_version()),
+            stream_id_);
+  DCHECK_EQ(QuicUtils::GetInvalidStreamId(connection()->transport_version()),
+            promised_stream_id_);
+  stream_id_ = stream_id;
+  promised_stream_id_ = promised_stream_id;
+}
+
+// TODO (wangyix): Why is SpdyStreamId used instead of QuicStreamId?
+// This occurs in many places in this file.
+void QuicSpdySession::OnPriority(SpdyStreamId stream_id,
+                                 SpdyPriority priority) {
+  if (perspective() == Perspective::IS_CLIENT) {
+    CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
+                               "Server must not send PRIORITY frames.");
+    return;
+  }
+  OnPriorityFrame(stream_id, priority);
+}
+
+void QuicSpdySession::OnHeaderList(const QuicHeaderList& header_list) {
+  QUIC_DVLOG(1) << "Received header list for stream " << stream_id_ << ": "
+                << header_list.DebugString();
+  if (promised_stream_id_ ==
+      QuicUtils::GetInvalidStreamId(connection()->transport_version())) {
+    OnStreamHeaderList(stream_id_, fin_, frame_len_, header_list);
+  } else {
+    OnPromiseHeaderList(stream_id_, promised_stream_id_, frame_len_,
+                        header_list);
+  }
+  // Reset state for the next frame.
+  promised_stream_id_ =
+      QuicUtils::GetInvalidStreamId(connection()->transport_version());
+  stream_id_ = QuicUtils::GetInvalidStreamId(connection()->transport_version());
+  fin_ = false;
+  frame_len_ = 0;
+  uncompressed_frame_len_ = 0;
+}
+
+void QuicSpdySession::OnCompressedFrameSize(size_t frame_len) {
+  frame_len_ += frame_len;
+}
+
+void QuicSpdySession::SetHpackEncoderDebugVisitor(
+    std::unique_ptr<QuicHpackDebugVisitor> visitor) {
+  spdy_framer_.SetEncoderHeaderTableDebugVisitor(
+      std::unique_ptr<HeaderTableDebugVisitor>(new HeaderTableDebugVisitor(
+          connection()->helper()->GetClock(), std::move(visitor))));
+}
+
+void QuicSpdySession::SetHpackDecoderDebugVisitor(
+    std::unique_ptr<QuicHpackDebugVisitor> visitor) {
+  h2_deframer_.SetDecoderHeaderTableDebugVisitor(
+      QuicMakeUnique<HeaderTableDebugVisitor>(
+          connection()->helper()->GetClock(), std::move(visitor)));
+}
+
+void QuicSpdySession::UpdateHeaderEncoderTableSize(uint32_t value) {
+  spdy_framer_.UpdateHeaderEncoderTableSize(value);
+}
+
+void QuicSpdySession::UpdateEnableServerPush(bool value) {
+  set_server_push_enabled(value);
+}
+
+void QuicSpdySession::set_max_uncompressed_header_bytes(
+    size_t set_max_uncompressed_header_bytes) {
+  spdy_framer_visitor_->set_max_uncompressed_header_bytes(
+      set_max_uncompressed_header_bytes);
+}
+
+void QuicSpdySession::CloseConnectionWithDetails(QuicErrorCode error,
+                                                 const QuicString& details) {
+  connection()->CloseConnection(
+      error, details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+}
+
+}  // namespace quic