Index: net/quic/quic_headers_stream.cc |
diff --git a/net/quic/quic_headers_stream.cc b/net/quic/quic_headers_stream.cc |
index 72c3ac9bbefd189fae366cf501f64a5943cb0b53..75ef2c7d1c321730c7cb2d13b4c5d7da12545b52 100644 |
--- a/net/quic/quic_headers_stream.cc |
+++ b/net/quic/quic_headers_stream.cc |
@@ -56,6 +56,46 @@ class HeaderTableDebugVisitor : public HpackHeaderTable::DebugVisitorInterface { |
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(QuicAckListenerInterface* stream_ack_listener, |
+ int extra_bytes) |
+ : stream_ack_listener_(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); |
+ } |
+ |
+ private: |
+ ~ForceHolAckListener() override {} |
+ |
+ scoped_refptr<QuicAckListenerInterface> stream_ack_listener_; |
+ int extra_bytes_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(ForceHolAckListener); |
+}; |
+ |
} // namespace |
QuicHeadersStream::HpackDebugVisitor::HpackDebugVisitor() {} |
@@ -97,6 +137,9 @@ class QuicHeadersStream::SpdyFramerVisitor |
void OnStreamFrameData(SpdyStreamId stream_id, |
const char* data, |
size_t len) override { |
+ if (stream_->OnStreamFrameData(stream_id, data, len)) { |
+ return; |
+ } |
CloseConnection("SPDY DATA frame received."); |
} |
@@ -133,6 +176,9 @@ class QuicHeadersStream::SpdyFramerVisitor |
void OnDataFrameHeader(SpdyStreamId stream_id, |
size_t length, |
bool fin) override { |
+ if (stream_->OnDataFrameHeader(stream_id, length, fin)) { |
+ return; |
+ } |
CloseConnection("SPDY DATA frame received."); |
} |
@@ -330,6 +376,63 @@ size_t QuicHeadersStream::WritePushPromise( |
return frame.size(); |
} |
+QuicConsumedData QuicHeadersStream::WritevStreamData( |
+ QuicStreamId id, |
+ QuicIOVector iov, |
+ QuicStreamOffset offset, |
+ bool fin, |
+ QuicAckListenerInterface* ack_notifier_delegate) { |
+ const size_t max_len = SpdyConstants::GetFrameMaximumSize(HTTP2) - |
+ SpdyConstants::GetDataFrameMinimumSize(HTTP2); |
+ |
+ QuicConsumedData result(0, false); |
+ size_t total_length = iov.total_length; |
+ |
+ // 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 eacho 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 offset = 0; |
+ const struct iovec* src_iov = &iov.iov[i]; |
+ do { |
+ size_t len = |
+ std::min(std::min(src_iov->iov_len - offset, max_len), total_length); |
+ char* data = static_cast<char*>(src_iov->iov_base) + offset; |
+ SpdyDataIR spdy_data(id, StringPiece(data, len)); |
+ offset += len; |
+ // fin handling, set it only it only very last generated HTTP/2 |
+ // DATA frame. |
+ bool last_iov = i == iov.iov_count - 1; |
+ bool last_fragment_within_iov = offset >= src_iov->iov_len; |
+ bool frame_fin = (last_iov && last_fragment_within_iov) ? fin : false; |
+ spdy_data.set_fin(frame_fin); |
+ if (frame_fin) { |
+ result.fin_consumed = true; |
+ } |
+ SpdySerializedFrame frame(spdy_framer_.SerializeFrame(spdy_data)); |
+ DVLOG(1) << "Encapsulating in DATA frame for stream " << id << " len " |
+ << len << " fin " << spdy_data.fin() << " remaining " |
+ << src_iov->iov_len - offset; |
+ |
+ scoped_refptr<ForceHolAckListener> ack_listener; |
+ if (ack_notifier_delegate != nullptr) { |
+ ack_listener = |
+ new ForceHolAckListener(ack_notifier_delegate, frame.size() - len); |
+ } |
+ |
+ WriteOrBufferData(StringPiece(frame.data(), frame.size()), false, |
+ ack_listener.get()); |
+ result.bytes_consumed += len; |
+ total_length -= len; |
+ if (total_length <= 0) { |
+ return result; |
+ } |
+ } while (offset < src_iov->iov_len); |
+ } |
+ return result; |
+} |
+ |
void QuicHeadersStream::OnDataAvailable() { |
char buffer[1024]; |
struct iovec iov; |
@@ -506,4 +609,39 @@ void QuicHeadersStream::UpdateHeaderEncoderTableSize(uint32_t value) { |
spdy_framer_.UpdateHeaderEncoderTableSize(value); |
} |
+bool QuicHeadersStream::OnDataFrameHeader(QuicStreamId stream_id, |
+ size_t length, |
+ bool fin) { |
+ if (!spdy_session_->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 QuicHeadersStream::OnStreamFrameData(QuicStreamId stream_id, |
+ const char* data, |
+ size_t len) { |
+ if (!spdy_session_->force_hol_blocking()) { |
+ return false; |
+ } |
+ if (!IsConnected()) { |
+ return true; |
+ } |
+ frame_len_ -= len; |
+ // Ignore fin_ while there is more data coming, if frame_len_ > 0. |
+ spdy_session_->OnStreamFrameData(stream_id, data, len, |
+ frame_len_ > 0 ? false : fin_); |
+ return true; |
+} |
+ |
} // namespace net |