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