Index: net/quic/core/quic_spdy_session.cc |
diff --git a/net/quic/core/quic_spdy_session.cc b/net/quic/core/quic_spdy_session.cc |
index e3fd1ea8e68bd366494269ec3080124ee6d988b5..cff601ed7c06c9c810980ae4aca5c2b5263e2f34 100644 |
--- a/net/quic/core/quic_spdy_session.cc |
+++ b/net/quic/core/quic_spdy_session.cc |
@@ -8,18 +8,322 @@ |
#include "net/quic/core/quic_bug_tracker.h" |
#include "net/quic/core/quic_headers_stream.h" |
+#include "net/quic/platform/api/quic_str_cat.h" |
using base::StringPiece; |
using std::string; |
namespace net { |
+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)) {} |
+ |
+ int64_t OnNewEntry(const HpackEntry& entry) override { |
+ 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()); |
+ 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_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(HeaderTableDebugVisitor); |
+}; |
+ |
+// When forced HOL blocking is enabled, extra bytes in the form of |
+// HTTP/2 DATA frame headers are inserted on the way down to the |
+// session layer. |ForceAckListener| filters the |OnPacketAcked()| |
+// notifications generated by the session layer to not count the extra |
+// bytes. Otherwise, code that is using ack listener on streams might |
+// consider it an error if more bytes are acked than were written to |
+// the stream, it is the case with some internal stats gathering code. |
+class ForceHolAckListener : public QuicAckListenerInterface { |
+ public: |
+ // |extra_bytes| should be initialized to the size of the HTTP/2 |
+ // DATA frame header inserted when forced HOL blocking is enabled. |
+ ForceHolAckListener( |
+ QuicReferenceCountedPointer<QuicAckListenerInterface> stream_ack_listener, |
+ int extra_bytes) |
+ : stream_ack_listener_(std::move(stream_ack_listener)), |
+ extra_bytes_(extra_bytes) { |
+ DCHECK_GE(extra_bytes, 0); |
+ } |
+ |
+ void OnPacketAcked(int acked_bytes, QuicTime::Delta ack_delay_time) override { |
+ if (extra_bytes_ > 0) { |
+ // Don't count the added HTTP/2 DATA frame header bytes |
+ int delta = std::min(extra_bytes_, acked_bytes); |
+ extra_bytes_ -= delta; |
+ acked_bytes -= delta; |
+ } |
+ stream_ack_listener_->OnPacketAcked(acked_bytes, ack_delay_time); |
+ } |
+ |
+ void OnPacketRetransmitted(int retransmitted_bytes) override { |
+ stream_ack_listener_->OnPacketRetransmitted(retransmitted_bytes); |
+ } |
+ |
+ protected: |
+ ~ForceHolAckListener() override {} |
+ |
+ private: |
+ QuicReferenceCountedPointer<QuicAckListenerInterface> stream_ack_listener_; |
+ int extra_bytes_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(ForceHolAckListener); |
+}; |
+ |
+} // 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) {} |
+ |
+ SpdyHeadersHandlerInterface* OnHeaderFrameStart( |
+ SpdyStreamId /* stream_id */) override { |
+ return &header_list_; |
+ } |
+ |
+ void OnHeaderFrameEnd(SpdyStreamId /* stream_id */, |
+ bool end_headers) override { |
+ if (end_headers) { |
+ if (session_->IsConnected()) { |
+ session_->OnHeaderList(header_list_); |
+ } |
+ header_list_.Clear(); |
+ } |
+ } |
+ |
+ void OnStreamFrameData(SpdyStreamId stream_id, |
+ const char* data, |
+ size_t len) override { |
+ if (session_->OnStreamFrameData(stream_id, data, len)) { |
+ return; |
+ } |
+ CloseConnection("SPDY DATA frame received."); |
+ } |
+ |
+ 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."); |
+ } |
+ |
+ void OnError(SpdyFramer* framer) override { |
+ CloseConnection( |
+ QuicStrCat("SPDY framing error: ", |
+ SpdyFramer::ErrorCodeToString(framer->error_code()))); |
+ } |
+ |
+ void OnDataFrameHeader(SpdyStreamId stream_id, |
+ size_t length, |
+ bool fin) override { |
+ if (session_->OnDataFrameHeader(stream_id, length, fin)) { |
+ return; |
+ } |
+ CloseConnection("SPDY DATA frame received."); |
+ } |
+ |
+ void OnRstStream(SpdyStreamId stream_id, |
+ SpdyRstStreamStatus status) override { |
+ CloseConnection("SPDY RST_STREAM frame received."); |
+ } |
+ |
+ void OnSetting(SpdySettingsIds id, uint32_t value) override { |
+ if (!FLAGS_quic_reloadable_flag_quic_respect_http2_settings_frame) { |
+ CloseConnection("SPDY SETTINGS frame received."); |
+ return; |
+ } |
+ switch (id) { |
+ case SETTINGS_HEADER_TABLE_SIZE: |
+ session_->UpdateHeaderEncoderTableSize(value); |
+ break; |
+ case SETTINGS_ENABLE_PUSH: |
+ if (FLAGS_quic_reloadable_flag_quic_enable_server_push_by_default && |
+ session_->perspective() == Perspective::IS_SERVER) { |
+ // See rfc7540, Section 6.5.2. |
+ if (value > 1) { |
+ CloseConnection( |
+ QuicStrCat("Invalid value for SETTINGS_ENABLE_PUSH: ", value)); |
+ return; |
+ } |
+ session_->UpdateEnableServerPush(value > 0); |
+ break; |
+ } else { |
+ CloseConnection( |
+ QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ", id)); |
+ } |
+ break; |
+ // TODO(fayang): Need to support SETTINGS_MAX_HEADER_LIST_SIZE when |
+ // clients are actually sending it. |
+ case SETTINGS_MAX_HEADER_LIST_SIZE: |
+ if (FLAGS_quic_reloadable_flag_quic_send_max_header_list_size) { |
+ break; |
+ } |
+ default: |
+ CloseConnection( |
+ QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ", id)); |
+ } |
+ } |
+ |
+ void OnSettingsAck() override { |
+ if (!FLAGS_quic_reloadable_flag_quic_respect_http2_settings_frame) { |
+ CloseConnection("SPDY SETTINGS frame received."); |
+ } |
+ } |
+ |
+ void OnSettingsEnd() override { |
+ if (!FLAGS_quic_reloadable_flag_quic_respect_http2_settings_frame) { |
+ CloseConnection("SPDY SETTINGS frame received."); |
+ } |
+ } |
+ |
+ void OnPing(SpdyPingId unique_id, bool is_ack) override { |
+ CloseConnection("SPDY PING frame received."); |
+ } |
+ |
+ void OnGoAway(SpdyStreamId last_accepted_stream_id, |
+ SpdyGoAwayStatus status) override { |
+ CloseConnection("SPDY GOAWAY frame received."); |
+ } |
+ |
+ 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."); |
+ } |
+ |
+ void OnPushPromise(SpdyStreamId stream_id, |
+ SpdyStreamId promised_stream_id, |
+ bool end) override { |
+ if (!session_->supports_push_promise()) { |
+ CloseConnection("PUSH_PROMISE not supported."); |
+ 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 { |
+ CloseConnection("SPDY PRIORITY frame received."); |
+ } |
+ |
+ bool OnUnknownFrame(SpdyStreamId stream_id, int frame_type) override { |
+ CloseConnection("Unknown frame type received."); |
+ 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; |
+ 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_uncompressed_header_bytes( |
+ set_max_uncompressed_header_bytes); |
+ } |
+ |
+ private: |
+ void CloseConnection(const string& details) { |
+ if (session_->IsConnected()) { |
+ session_->CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA, |
+ details); |
+ } |
+ } |
+ |
+ private: |
+ QuicSpdySession* session_; |
+ QuicHeaderList header_list_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(SpdyFramerVisitor); |
+}; |
+ |
+QuicHpackDebugVisitor::QuicHpackDebugVisitor() {} |
+ |
+QuicHpackDebugVisitor::~QuicHpackDebugVisitor() {} |
+ |
QuicSpdySession::QuicSpdySession(QuicConnection* connection, |
QuicSession::Visitor* visitor, |
const QuicConfig& config) |
: QuicSession(connection, visitor, config), |
force_hol_blocking_(false), |
- server_push_enabled_(false) {} |
+ server_push_enabled_(false), |
+ stream_id_(kInvalidStreamId), |
+ promised_stream_id_(kInvalidStreamId), |
+ fin_(false), |
+ frame_len_(0), |
+ uncompressed_frame_len_(0), |
+ supports_push_promise_(perspective() == Perspective::IS_CLIENT), |
+ cur_max_timestamp_(QuicTime::Zero()), |
+ prev_max_timestamp_(QuicTime::Zero()), |
+ spdy_framer_(SpdyFramer::ENABLE_COMPRESSION), |
+ spdy_framer_visitor_(new SpdyFramerVisitor(this)) { |
+ spdy_framer_.set_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 |
@@ -69,6 +373,14 @@ void QuicSpdySession::OnStreamHeaderList(QuicStreamId stream_id, |
stream->OnStreamHeaderList(fin, frame_len, header_list); |
} |
+size_t QuicSpdySession::ProcessHeaderData(const struct iovec& iov, |
+ QuicTime timestamp) { |
+ DCHECK(timestamp.IsInitialized()); |
+ UpdateCurMaxTimeStamp(timestamp); |
+ return spdy_framer_.ProcessInput(static_cast<char*>(iov.iov_base), |
+ iov.iov_len); |
+} |
+ |
size_t QuicSpdySession::WriteHeaders( |
QuicStreamId id, |
SpdyHeaderBlock headers, |
@@ -76,8 +388,132 @@ size_t QuicSpdySession::WriteHeaders( |
SpdyPriority priority, |
QuicReferenceCountedPointer<QuicAckListenerInterface> |
ack_notifier_delegate) { |
- return headers_stream_->WriteHeaders(id, std::move(headers), fin, priority, |
- std::move(ack_notifier_delegate)); |
+ return WriteHeadersImpl(id, std::move(headers), fin, priority, |
+ std::move(ack_notifier_delegate)); |
+} |
+ |
+size_t QuicSpdySession::WriteHeadersImpl( |
+ QuicStreamId id, |
+ SpdyHeaderBlock headers, |
+ bool fin, |
+ SpdyPriority priority, |
+ QuicReferenceCountedPointer<QuicAckListenerInterface> |
+ ack_notifier_delegate) { |
+ 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_weight(Spdy3PriorityToHttp2Weight(priority)); |
+ } |
+ SpdySerializedFrame frame(spdy_framer_.SerializeFrame(headers_frame)); |
+ headers_stream_->WriteOrBufferData(StringPiece(frame.data(), frame.size()), |
+ false, std::move(ack_notifier_delegate)); |
+ 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(StringPiece(frame.data(), frame.size()), |
+ false, nullptr); |
+ return frame.size(); |
+} |
+ |
+void QuicSpdySession::WriteDataFrame( |
+ QuicStreamId id, |
+ StringPiece data, |
+ bool fin, |
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { |
+ SpdyDataIR spdy_data(id, data); |
+ spdy_data.set_fin(fin); |
+ SpdySerializedFrame frame(spdy_framer_.SerializeFrame(spdy_data)); |
+ QuicReferenceCountedPointer<ForceHolAckListener> force_hol_ack_listener; |
+ if (ack_listener != nullptr) { |
+ force_hol_ack_listener = new ForceHolAckListener( |
+ std::move(ack_listener), frame.size() - data.length()); |
+ } |
+ // Use buffered writes so that coherence of framing is preserved |
+ // between streams. |
+ headers_stream_->WriteOrBufferData(StringPiece(frame.data(), frame.size()), |
+ false, std::move(force_hol_ack_listener)); |
+} |
+ |
+QuicConsumedData QuicSpdySession::WritevStreamData( |
+ QuicStreamId id, |
+ QuicIOVector iov, |
+ QuicStreamOffset offset, |
+ bool fin, |
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { |
+ const size_t max_len = |
+ kSpdyInitialFrameSizeLimit - kDataFrameMinimumSize; |
+ |
+ QuicConsumedData result(0, false); |
+ size_t total_length = iov.total_length; |
+ |
+ if (total_length == 0 && fin) { |
+ WriteDataFrame(id, StringPiece(), true, std::move(ack_listener)); |
+ result.fin_consumed = true; |
+ return result; |
+ } |
+ |
+ // Encapsulate the data into HTTP/2 DATA frames. The outer loop |
+ // handles each element of the source iov, the inner loop handles |
+ // the possibility of fragmenting each of those into multiple DATA |
+ // frames, as the DATA frames have a max size of 16KB. |
+ for (int i = 0; i < iov.iov_count; i++) { |
+ size_t src_iov_offset = 0; |
+ const struct iovec* src_iov = &iov.iov[i]; |
+ do { |
+ if (headers_stream_->queued_data_bytes() > 0) { |
+ // Limit the amount of buffering to the minimum needed to |
+ // preserve framing. |
+ return result; |
+ } |
+ size_t len = std::min( |
+ std::min(src_iov->iov_len - src_iov_offset, max_len), total_length); |
+ char* data = static_cast<char*>(src_iov->iov_base) + src_iov_offset; |
+ src_iov_offset += len; |
+ offset += len; |
+ // fin handling, only set it for the final HTTP/2 DATA frame. |
+ bool last_iov = i == iov.iov_count - 1; |
+ bool last_fragment_within_iov = src_iov_offset >= src_iov->iov_len; |
+ bool frame_fin = (last_iov && last_fragment_within_iov) ? fin : false; |
+ WriteDataFrame(id, StringPiece(data, len), frame_fin, ack_listener); |
+ result.bytes_consumed += len; |
+ if (frame_fin) { |
+ result.fin_consumed = true; |
+ } |
+ DCHECK_GE(total_length, len); |
+ total_length -= len; |
+ if (total_length <= 0) { |
+ return result; |
+ } |
+ } while (src_iov_offset < src_iov->iov_len); |
+ } |
+ |
+ return result; |
+} |
+ |
+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(StringPiece(frame.data(), frame.size()), |
+ false, nullptr); |
+ return frame.size(); |
} |
void QuicSpdySession::OnHeadersHeadOfLineBlocking(QuicTime::Delta delta) { |
@@ -107,7 +543,7 @@ void QuicSpdySession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { |
QuicSession::OnCryptoHandshakeEvent(event); |
if (FLAGS_quic_reloadable_flag_quic_send_max_header_list_size && |
event == HANDSHAKE_CONFIRMED && config()->SupportMaxHeaderListSize()) { |
- headers_stream()->SendMaxHeaderListSize(kDefaultMaxUncompressedHeaderSize); |
+ SendMaxHeaderListSize(kDefaultMaxUncompressedHeaderSize); |
} |
} |
@@ -124,7 +560,7 @@ void QuicSpdySession::OnPromiseHeaderList(QuicStreamId stream_id, |
void QuicSpdySession::OnConfigNegotiated() { |
QuicSession::OnConfigNegotiated(); |
if (config()->HasClientSentConnectionOption(kDHDT, perspective())) { |
- headers_stream_->DisableHpackDynamicTable(); |
+ DisableHpackDynamicTable(); |
} |
const QuicVersion version = connection()->version(); |
if (FLAGS_quic_reloadable_flag_quic_enable_force_hol_blocking && |
@@ -167,4 +603,143 @@ 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(kInvalidStreamId, stream_id_); |
+ DCHECK_EQ(kInvalidStreamId, promised_stream_id_); |
+ stream_id_ = stream_id; |
+ fin_ = fin; |
+} |
+ |
+void QuicSpdySession::OnPushPromise(SpdyStreamId stream_id, |
+ SpdyStreamId promised_stream_id, |
+ bool end) { |
+ DCHECK_EQ(kInvalidStreamId, stream_id_); |
+ DCHECK_EQ(kInvalidStreamId, promised_stream_id_); |
+ stream_id_ = stream_id; |
+ promised_stream_id_ = promised_stream_id; |
+} |
+ |
+void QuicSpdySession::OnHeaderList(const QuicHeaderList& header_list) { |
+ DVLOG(1) << "Received header list for stream " << stream_id_ << ": " |
+ << header_list.DebugString(); |
+ if (prev_max_timestamp_ > cur_max_timestamp_) { |
+ // prev_max_timestamp_ > cur_max_timestamp_ implies that |
+ // headers from lower numbered streams actually came off the |
+ // wire after headers for the current stream, hence there was |
+ // HOL blocking. |
+ QuicTime::Delta delta = prev_max_timestamp_ - cur_max_timestamp_; |
+ DVLOG(1) << "stream " << stream_id_ |
+ << ": Net.QuicSession.HeadersHOLBlockedTime " |
+ << delta.ToMilliseconds(); |
+ OnHeadersHeadOfLineBlocking(delta); |
+ } |
+ prev_max_timestamp_ = std::max(prev_max_timestamp_, cur_max_timestamp_); |
+ cur_max_timestamp_ = QuicTime::Zero(); |
+ if (promised_stream_id_ == kInvalidStreamId) { |
+ 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_ = kInvalidStreamId; |
+ stream_id_ = kInvalidStreamId; |
+ fin_ = false; |
+ frame_len_ = 0; |
+ uncompressed_frame_len_ = 0; |
+} |
+ |
+void QuicSpdySession::OnCompressedFrameSize(size_t frame_len) { |
+ frame_len_ += frame_len; |
+} |
+ |
+void QuicSpdySession::DisableHpackDynamicTable() { |
+ spdy_framer_.UpdateHeaderEncoderTableSize(0); |
+} |
+ |
+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) { |
+ spdy_framer_.SetDecoderHeaderTableDebugVisitor( |
+ std::unique_ptr<HeaderTableDebugVisitor>(new 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); |
+} |
+ |
+bool QuicSpdySession::OnDataFrameHeader(QuicStreamId stream_id, |
+ size_t length, |
+ bool fin) { |
+ if (!force_hol_blocking()) { |
+ return false; |
+ } |
+ if (!IsConnected()) { |
+ return true; |
+ } |
+ DVLOG(1) << "DATA frame header for stream " << stream_id << " length " |
+ << length << " fin " << fin; |
+ fin_ = fin; |
+ frame_len_ = length; |
+ if (fin && length == 0) { |
+ OnStreamFrameData(stream_id, "", 0); |
+ } |
+ return true; |
+} |
+ |
+bool QuicSpdySession::OnStreamFrameData(QuicStreamId stream_id, |
+ const char* data, |
+ size_t len) { |
+ if (!force_hol_blocking()) { |
+ return false; |
+ } |
+ if (!IsConnected()) { |
+ return true; |
+ } |
+ frame_len_ -= len; |
+ // Ignore fin_ while there is more data coming, if frame_len_ > 0. |
+ OnStreamFrameData(stream_id, data, len, frame_len_ > 0 ? false : fin_); |
+ return true; |
+} |
+ |
+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 string& details) { |
+ connection()->CloseConnection( |
+ error, details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
+} |
+ |
} // namespace net |