OLD | NEW |
| (Empty) |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "net/quic/quic_headers_stream.h" | |
6 | |
7 #include <utility> | |
8 | |
9 #include "base/macros.h" | |
10 #include "base/metrics/histogram_macros.h" | |
11 #include "base/strings/string_number_conversions.h" | |
12 #include "base/strings/stringprintf.h" | |
13 #include "net/quic/quic_bug_tracker.h" | |
14 #include "net/quic/quic_flags.h" | |
15 #include "net/quic/quic_header_list.h" | |
16 #include "net/quic/quic_spdy_session.h" | |
17 #include "net/quic/quic_time.h" | |
18 #include "net/spdy/spdy_protocol.h" | |
19 | |
20 using base::StringPiece; | |
21 using net::HTTP2; | |
22 using net::SpdyFrameType; | |
23 using std::string; | |
24 | |
25 namespace net { | |
26 | |
27 namespace { | |
28 | |
29 class HeaderTableDebugVisitor : public HpackHeaderTable::DebugVisitorInterface { | |
30 public: | |
31 HeaderTableDebugVisitor( | |
32 const QuicClock* clock, | |
33 std::unique_ptr<QuicHeadersStream::HpackDebugVisitor> visitor) | |
34 : clock_(clock), headers_stream_hpack_visitor_(std::move(visitor)) {} | |
35 | |
36 int64_t OnNewEntry(const HpackEntry& entry) override { | |
37 DVLOG(1) << entry.GetDebugString(); | |
38 return (clock_->ApproximateNow() - QuicTime::Zero()).ToMicroseconds(); | |
39 } | |
40 | |
41 void OnUseEntry(const HpackEntry& entry) override { | |
42 const QuicTime::Delta elapsed( | |
43 clock_->ApproximateNow() - | |
44 QuicTime::Delta::FromMicroseconds(entry.time_added()) - | |
45 QuicTime::Zero()); | |
46 DVLOG(1) << entry.GetDebugString() << " " << elapsed.ToMilliseconds() | |
47 << " ms"; | |
48 headers_stream_hpack_visitor_->OnUseEntry(elapsed); | |
49 } | |
50 | |
51 private: | |
52 const QuicClock* clock_; | |
53 std::unique_ptr<QuicHeadersStream::HpackDebugVisitor> | |
54 headers_stream_hpack_visitor_; | |
55 | |
56 DISALLOW_COPY_AND_ASSIGN(HeaderTableDebugVisitor); | |
57 }; | |
58 | |
59 // When forced HOL blocking is enabled, extra bytes in the form of | |
60 // HTTP/2 DATA frame headers are inserted on the way down to the | |
61 // session layer. |ForceAckListener| filters the |OnPacketAcked()| | |
62 // notifications generated by the session layer to not count the extra | |
63 // bytes. Otherwise, code that is using ack listener on streams might | |
64 // consider it an error if more bytes are acked than were written to | |
65 // the stream, it is the case with some internal stats gathering code. | |
66 class ForceHolAckListener : public QuicAckListenerInterface { | |
67 public: | |
68 // |extra_bytes| should be initialized to the size of the HTTP/2 | |
69 // DATA frame header inserted when forced HOL blocking is enabled. | |
70 ForceHolAckListener(QuicAckListenerInterface* stream_ack_listener, | |
71 int extra_bytes) | |
72 : stream_ack_listener_(stream_ack_listener), extra_bytes_(extra_bytes) { | |
73 DCHECK_GE(extra_bytes, 0); | |
74 } | |
75 | |
76 void OnPacketAcked(int acked_bytes, QuicTime::Delta ack_delay_time) override { | |
77 if (extra_bytes_ > 0) { | |
78 // Don't count the added HTTP/2 DATA frame header bytes | |
79 int delta = std::min(extra_bytes_, acked_bytes); | |
80 extra_bytes_ -= delta; | |
81 acked_bytes -= delta; | |
82 } | |
83 stream_ack_listener_->OnPacketAcked(acked_bytes, ack_delay_time); | |
84 } | |
85 | |
86 void OnPacketRetransmitted(int retransmitted_bytes) override { | |
87 stream_ack_listener_->OnPacketRetransmitted(retransmitted_bytes); | |
88 } | |
89 | |
90 private: | |
91 ~ForceHolAckListener() override {} | |
92 | |
93 scoped_refptr<QuicAckListenerInterface> stream_ack_listener_; | |
94 int extra_bytes_; | |
95 | |
96 DISALLOW_COPY_AND_ASSIGN(ForceHolAckListener); | |
97 }; | |
98 | |
99 } // namespace | |
100 | |
101 QuicHeadersStream::HpackDebugVisitor::HpackDebugVisitor() {} | |
102 | |
103 QuicHeadersStream::HpackDebugVisitor::~HpackDebugVisitor() {} | |
104 | |
105 // A SpdyFramer visitor which passed SYN_STREAM and SYN_REPLY frames to | |
106 // the QuicSpdyStream, and closes the connection if any unexpected frames | |
107 // are received. | |
108 class QuicHeadersStream::SpdyFramerVisitor | |
109 : public SpdyFramerVisitorInterface, | |
110 public SpdyFramerDebugVisitorInterface { | |
111 public: | |
112 explicit SpdyFramerVisitor(QuicHeadersStream* stream) : stream_(stream) {} | |
113 | |
114 // SpdyFramerVisitorInterface implementation | |
115 void OnSynStream(SpdyStreamId stream_id, | |
116 SpdyStreamId associated_stream_id, | |
117 SpdyPriority priority, | |
118 bool fin, | |
119 bool unidirectional) override { | |
120 CloseConnection("SPDY SYN_STREAM frame received."); | |
121 } | |
122 | |
123 void OnSynReply(SpdyStreamId stream_id, bool fin) override { | |
124 CloseConnection("SPDY SYN_REPLY frame received."); | |
125 } | |
126 | |
127 bool OnControlFrameHeaderData(SpdyStreamId stream_id, | |
128 const char* header_data, | |
129 size_t len) override { | |
130 if (!stream_->IsConnected()) { | |
131 return false; | |
132 } | |
133 stream_->OnControlFrameHeaderData(stream_id, header_data, len); | |
134 return true; | |
135 } | |
136 | |
137 void OnStreamFrameData(SpdyStreamId stream_id, | |
138 const char* data, | |
139 size_t len) override { | |
140 if (stream_->OnStreamFrameData(stream_id, data, len)) { | |
141 return; | |
142 } | |
143 CloseConnection("SPDY DATA frame received."); | |
144 } | |
145 | |
146 void OnStreamEnd(SpdyStreamId stream_id) override { | |
147 // The framer invokes OnStreamEnd after processing a SYN_STREAM | |
148 // or SYN_REPLY frame that had the fin bit set. | |
149 } | |
150 | |
151 void OnStreamPadding(SpdyStreamId stream_id, size_t len) override { | |
152 CloseConnection("SPDY frame padding received."); | |
153 } | |
154 | |
155 SpdyHeadersHandlerInterface* OnHeaderFrameStart( | |
156 SpdyStreamId /* stream_id */) override { | |
157 return &header_list_; | |
158 } | |
159 | |
160 void OnHeaderFrameEnd(SpdyStreamId /* stream_id */, | |
161 bool end_headers) override { | |
162 if (end_headers) { | |
163 if (stream_->IsConnected()) { | |
164 stream_->OnHeaderList(header_list_); | |
165 } | |
166 header_list_.Clear(); | |
167 } | |
168 } | |
169 | |
170 void OnError(SpdyFramer* framer) override { | |
171 CloseConnection(base::StringPrintf( | |
172 "SPDY framing error: %s", | |
173 SpdyFramer::ErrorCodeToString(framer->error_code()))); | |
174 } | |
175 | |
176 void OnDataFrameHeader(SpdyStreamId stream_id, | |
177 size_t length, | |
178 bool fin) override { | |
179 if (stream_->OnDataFrameHeader(stream_id, length, fin)) { | |
180 return; | |
181 } | |
182 CloseConnection("SPDY DATA frame received."); | |
183 } | |
184 | |
185 void OnRstStream(SpdyStreamId stream_id, | |
186 SpdyRstStreamStatus status) override { | |
187 CloseConnection("SPDY RST_STREAM frame received."); | |
188 } | |
189 | |
190 void OnSetting(SpdySettingsIds id, uint8_t flags, uint32_t value) override { | |
191 if (!FLAGS_quic_respect_http2_settings_frame) { | |
192 CloseConnection("SPDY SETTINGS frame received."); | |
193 return; | |
194 } | |
195 switch (id) { | |
196 case SETTINGS_HEADER_TABLE_SIZE: | |
197 stream_->UpdateHeaderEncoderTableSize(value); | |
198 break; | |
199 // TODO(fayang): Need to support SETTINGS_MAX_HEADER_LIST_SIZE when | |
200 // clients are actually sending it. | |
201 default: | |
202 CloseConnection("Unsupported field of HTTP/2 SETTINGS frame: " + | |
203 base::IntToString(id)); | |
204 } | |
205 } | |
206 | |
207 void OnSettingsAck() override { | |
208 if (!FLAGS_quic_respect_http2_settings_frame) { | |
209 CloseConnection("SPDY SETTINGS frame received."); | |
210 } | |
211 } | |
212 | |
213 void OnSettingsEnd() override { | |
214 if (!FLAGS_quic_respect_http2_settings_frame) { | |
215 CloseConnection("SPDY SETTINGS frame received."); | |
216 } | |
217 } | |
218 | |
219 void OnPing(SpdyPingId unique_id, bool is_ack) override { | |
220 CloseConnection("SPDY PING frame received."); | |
221 } | |
222 | |
223 void OnGoAway(SpdyStreamId last_accepted_stream_id, | |
224 SpdyGoAwayStatus status) override { | |
225 CloseConnection("SPDY GOAWAY frame received."); | |
226 } | |
227 | |
228 void OnHeaders(SpdyStreamId stream_id, | |
229 bool has_priority, | |
230 int weight, | |
231 SpdyStreamId parent_stream_id, | |
232 bool exclusive, | |
233 bool fin, | |
234 bool end) override { | |
235 if (!stream_->IsConnected()) { | |
236 return; | |
237 } | |
238 | |
239 // TODO(mpw): avoid down-conversion and plumb SpdyStreamPrecedence through | |
240 // QuicHeadersStream. | |
241 SpdyPriority priority = | |
242 has_priority ? Http2WeightToSpdy3Priority(weight) : 0; | |
243 stream_->OnHeaders(stream_id, has_priority, priority, fin); | |
244 } | |
245 | |
246 void OnWindowUpdate(SpdyStreamId stream_id, int delta_window_size) override { | |
247 CloseConnection("SPDY WINDOW_UPDATE frame received."); | |
248 } | |
249 | |
250 void OnPushPromise(SpdyStreamId stream_id, | |
251 SpdyStreamId promised_stream_id, | |
252 bool end) override { | |
253 if (!stream_->supports_push_promise()) { | |
254 CloseConnection("PUSH_PROMISE not supported."); | |
255 return; | |
256 } | |
257 if (!stream_->IsConnected()) { | |
258 return; | |
259 } | |
260 stream_->OnPushPromise(stream_id, promised_stream_id, end); | |
261 } | |
262 | |
263 void OnContinuation(SpdyStreamId stream_id, bool end) override {} | |
264 | |
265 void OnPriority(SpdyStreamId stream_id, | |
266 SpdyStreamId parent_id, | |
267 int weight, | |
268 bool exclusive) override { | |
269 CloseConnection("SPDY PRIORITY frame received."); | |
270 } | |
271 | |
272 bool OnUnknownFrame(SpdyStreamId stream_id, int frame_type) override { | |
273 CloseConnection("Unknown frame type received."); | |
274 return false; | |
275 } | |
276 | |
277 // SpdyFramerDebugVisitorInterface implementation | |
278 void OnSendCompressedFrame(SpdyStreamId stream_id, | |
279 SpdyFrameType type, | |
280 size_t payload_len, | |
281 size_t frame_len) override { | |
282 if (payload_len == 0) { | |
283 QUIC_BUG << "Zero payload length."; | |
284 return; | |
285 } | |
286 int compression_pct = 100 - (100 * frame_len) / payload_len; | |
287 DVLOG(1) << "Net.QuicHpackCompressionPercentage: " << compression_pct; | |
288 UMA_HISTOGRAM_PERCENTAGE("Net.QuicHpackCompressionPercentage", | |
289 compression_pct); | |
290 } | |
291 | |
292 void OnReceiveCompressedFrame(SpdyStreamId stream_id, | |
293 SpdyFrameType type, | |
294 size_t frame_len) override { | |
295 if (stream_->IsConnected()) { | |
296 stream_->OnCompressedFrameSize(frame_len); | |
297 } | |
298 } | |
299 | |
300 private: | |
301 void CloseConnection(const string& details) { | |
302 if (stream_->IsConnected()) { | |
303 stream_->CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA, | |
304 details); | |
305 } | |
306 } | |
307 | |
308 private: | |
309 QuicHeadersStream* stream_; | |
310 QuicHeaderList header_list_; | |
311 | |
312 DISALLOW_COPY_AND_ASSIGN(SpdyFramerVisitor); | |
313 }; | |
314 | |
315 QuicHeadersStream::QuicHeadersStream(QuicSpdySession* session) | |
316 : ReliableQuicStream(kHeadersStreamId, session), | |
317 spdy_session_(session), | |
318 stream_id_(kInvalidStreamId), | |
319 promised_stream_id_(kInvalidStreamId), | |
320 fin_(false), | |
321 frame_len_(0), | |
322 uncompressed_frame_len_(0), | |
323 measure_headers_hol_blocking_time_( | |
324 FLAGS_quic_measure_headers_hol_blocking_time), | |
325 supports_push_promise_(session->perspective() == Perspective::IS_CLIENT && | |
326 FLAGS_quic_supports_push_promise), | |
327 cur_max_timestamp_(QuicTime::Zero()), | |
328 prev_max_timestamp_(QuicTime::Zero()), | |
329 spdy_framer_(HTTP2), | |
330 spdy_framer_visitor_(new SpdyFramerVisitor(this)) { | |
331 spdy_framer_.set_visitor(spdy_framer_visitor_.get()); | |
332 spdy_framer_.set_debug_visitor(spdy_framer_visitor_.get()); | |
333 // The headers stream is exempt from connection level flow control. | |
334 DisableConnectionFlowControlForThisStream(); | |
335 } | |
336 | |
337 QuicHeadersStream::~QuicHeadersStream() {} | |
338 | |
339 size_t QuicHeadersStream::WriteHeaders(QuicStreamId stream_id, | |
340 SpdyHeaderBlock headers, | |
341 bool fin, | |
342 SpdyPriority priority, | |
343 QuicAckListenerInterface* ack_listener) { | |
344 SpdyHeadersIR headers_frame(stream_id, std::move(headers)); | |
345 headers_frame.set_fin(fin); | |
346 if (session()->perspective() == Perspective::IS_CLIENT) { | |
347 headers_frame.set_has_priority(true); | |
348 headers_frame.set_weight(Spdy3PriorityToHttp2Weight(priority)); | |
349 } | |
350 SpdySerializedFrame frame(spdy_framer_.SerializeFrame(headers_frame)); | |
351 WriteOrBufferData(StringPiece(frame.data(), frame.size()), false, | |
352 ack_listener); | |
353 return frame.size(); | |
354 } | |
355 | |
356 size_t QuicHeadersStream::WritePushPromise( | |
357 QuicStreamId original_stream_id, | |
358 QuicStreamId promised_stream_id, | |
359 SpdyHeaderBlock headers, | |
360 QuicAckListenerInterface* ack_listener) { | |
361 if (session()->perspective() == Perspective::IS_CLIENT) { | |
362 QUIC_BUG << "Client shouldn't send PUSH_PROMISE"; | |
363 return 0; | |
364 } | |
365 | |
366 SpdyPushPromiseIR push_promise(original_stream_id, promised_stream_id, | |
367 std::move(headers)); | |
368 | |
369 // PUSH_PROMISE must not be the last frame sent out, at least followed by | |
370 // response headers. | |
371 push_promise.set_fin(false); | |
372 | |
373 SpdySerializedFrame frame(spdy_framer_.SerializeFrame(push_promise)); | |
374 WriteOrBufferData(StringPiece(frame.data(), frame.size()), false, | |
375 ack_listener); | |
376 return frame.size(); | |
377 } | |
378 | |
379 QuicConsumedData QuicHeadersStream::WritevStreamData( | |
380 QuicStreamId id, | |
381 QuicIOVector iov, | |
382 QuicStreamOffset offset, | |
383 bool fin, | |
384 QuicAckListenerInterface* ack_notifier_delegate) { | |
385 const size_t max_len = kSpdyInitialFrameSizeLimit - | |
386 SpdyConstants::GetDataFrameMinimumSize(HTTP2); | |
387 | |
388 QuicConsumedData result(0, false); | |
389 size_t total_length = iov.total_length; | |
390 | |
391 // Encapsulate the data into HTTP/2 DATA frames. The outer loop | |
392 // handles each element of the source iov, the inner loop handles | |
393 // the possibility of fragmenting eacho of those into multiple DATA | |
394 // frames, as the DATA frames have a max size of 16KB. | |
395 for (int i = 0; i < iov.iov_count; i++) { | |
396 size_t offset = 0; | |
397 const struct iovec* src_iov = &iov.iov[i]; | |
398 do { | |
399 size_t len = | |
400 std::min(std::min(src_iov->iov_len - offset, max_len), total_length); | |
401 char* data = static_cast<char*>(src_iov->iov_base) + offset; | |
402 SpdyDataIR spdy_data(id, StringPiece(data, len)); | |
403 offset += len; | |
404 // fin handling, set it only it only very last generated HTTP/2 | |
405 // DATA frame. | |
406 bool last_iov = i == iov.iov_count - 1; | |
407 bool last_fragment_within_iov = offset >= src_iov->iov_len; | |
408 bool frame_fin = (last_iov && last_fragment_within_iov) ? fin : false; | |
409 spdy_data.set_fin(frame_fin); | |
410 if (frame_fin) { | |
411 result.fin_consumed = true; | |
412 } | |
413 SpdySerializedFrame frame(spdy_framer_.SerializeFrame(spdy_data)); | |
414 DVLOG(1) << "Encapsulating in DATA frame for stream " << id << " len " | |
415 << len << " fin " << spdy_data.fin() << " remaining " | |
416 << src_iov->iov_len - offset; | |
417 | |
418 scoped_refptr<ForceHolAckListener> ack_listener; | |
419 if (ack_notifier_delegate != nullptr) { | |
420 ack_listener = | |
421 new ForceHolAckListener(ack_notifier_delegate, frame.size() - len); | |
422 } | |
423 | |
424 WriteOrBufferData(StringPiece(frame.data(), frame.size()), false, | |
425 ack_listener.get()); | |
426 result.bytes_consumed += len; | |
427 total_length -= len; | |
428 if (total_length <= 0) { | |
429 return result; | |
430 } | |
431 } while (offset < src_iov->iov_len); | |
432 } | |
433 return result; | |
434 } | |
435 | |
436 void QuicHeadersStream::OnDataAvailable() { | |
437 char buffer[1024]; | |
438 struct iovec iov; | |
439 QuicTime timestamp(QuicTime::Zero()); | |
440 while (true) { | |
441 iov.iov_base = buffer; | |
442 iov.iov_len = arraysize(buffer); | |
443 if (measure_headers_hol_blocking_time_) { | |
444 if (!sequencer()->GetReadableRegion(&iov, ×tamp)) { | |
445 // No more data to read. | |
446 break; | |
447 } | |
448 DCHECK(timestamp.IsInitialized()); | |
449 cur_max_timestamp_ = std::max(timestamp, cur_max_timestamp_); | |
450 } else { | |
451 if (sequencer()->GetReadableRegions(&iov, 1) != 1) { | |
452 // No more data to read. | |
453 break; | |
454 } | |
455 } | |
456 if (spdy_framer_.ProcessInput(static_cast<char*>(iov.iov_base), | |
457 iov.iov_len) != iov.iov_len) { | |
458 // Error processing data. | |
459 return; | |
460 } | |
461 sequencer()->MarkConsumed(iov.iov_len); | |
462 } | |
463 } | |
464 | |
465 void QuicHeadersStream::OnHeaders(SpdyStreamId stream_id, | |
466 bool has_priority, | |
467 SpdyPriority priority, | |
468 bool fin) { | |
469 if (has_priority) { | |
470 if (session()->perspective() == Perspective::IS_CLIENT) { | |
471 CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA, | |
472 "Server must not send priorities."); | |
473 return; | |
474 } | |
475 spdy_session_->OnStreamHeadersPriority(stream_id, priority); | |
476 } else { | |
477 if (session()->perspective() == Perspective::IS_SERVER) { | |
478 CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA, | |
479 "Client must send priorities."); | |
480 return; | |
481 } | |
482 } | |
483 DCHECK_EQ(kInvalidStreamId, stream_id_); | |
484 DCHECK_EQ(kInvalidStreamId, promised_stream_id_); | |
485 stream_id_ = stream_id; | |
486 fin_ = fin; | |
487 } | |
488 | |
489 void QuicHeadersStream::OnPushPromise(SpdyStreamId stream_id, | |
490 SpdyStreamId promised_stream_id, | |
491 bool end) { | |
492 DCHECK_EQ(kInvalidStreamId, stream_id_); | |
493 DCHECK_EQ(kInvalidStreamId, promised_stream_id_); | |
494 stream_id_ = stream_id; | |
495 promised_stream_id_ = promised_stream_id; | |
496 } | |
497 | |
498 void QuicHeadersStream::OnControlFrameHeaderData(SpdyStreamId stream_id, | |
499 const char* header_data, | |
500 size_t len) { | |
501 DCHECK_EQ(stream_id_, stream_id); | |
502 if (len == 0) { | |
503 DCHECK_NE(0u, stream_id_); | |
504 DCHECK_NE(0u, frame_len_); | |
505 if (measure_headers_hol_blocking_time_) { | |
506 if (prev_max_timestamp_ > cur_max_timestamp_) { | |
507 // prev_max_timestamp_ > cur_max_timestamp_ implies that | |
508 // headers from lower numbered streams actually came off the | |
509 // wire after headers for the current stream, hence there was | |
510 // HOL blocking. | |
511 QuicTime::Delta delta = prev_max_timestamp_ - cur_max_timestamp_; | |
512 DVLOG(1) << "stream " << stream_id | |
513 << ": Net.QuicSession.HeadersHOLBlockedTime " | |
514 << delta.ToMilliseconds(); | |
515 spdy_session_->OnHeadersHeadOfLineBlocking(delta); | |
516 } | |
517 prev_max_timestamp_ = std::max(prev_max_timestamp_, cur_max_timestamp_); | |
518 cur_max_timestamp_ = QuicTime::Zero(); | |
519 } | |
520 if (promised_stream_id_ == kInvalidStreamId) { | |
521 spdy_session_->OnStreamHeadersComplete(stream_id_, fin_, frame_len_); | |
522 } else { | |
523 spdy_session_->OnPromiseHeadersComplete(stream_id_, promised_stream_id_, | |
524 frame_len_); | |
525 } | |
526 if (uncompressed_frame_len_ != 0) { | |
527 int compression_pct = 100 - (100 * frame_len_) / uncompressed_frame_len_; | |
528 DVLOG(1) << "Net.QuicHpackDecompressionPercentage: " << compression_pct; | |
529 UMA_HISTOGRAM_PERCENTAGE("Net.QuicHpackDecompressionPercentage", | |
530 compression_pct); | |
531 } | |
532 // Reset state for the next frame. | |
533 promised_stream_id_ = kInvalidStreamId; | |
534 stream_id_ = kInvalidStreamId; | |
535 fin_ = false; | |
536 frame_len_ = 0; | |
537 uncompressed_frame_len_ = 0; | |
538 } else { | |
539 uncompressed_frame_len_ += len; | |
540 if (promised_stream_id_ == kInvalidStreamId) { | |
541 spdy_session_->OnStreamHeaders(stream_id_, StringPiece(header_data, len)); | |
542 } else { | |
543 spdy_session_->OnPromiseHeaders(stream_id_, | |
544 StringPiece(header_data, len)); | |
545 } | |
546 } | |
547 } | |
548 | |
549 void QuicHeadersStream::OnHeaderList(const QuicHeaderList& header_list) { | |
550 DVLOG(1) << "Received header list for stream " << stream_id_ << ": " | |
551 << header_list.DebugString(); | |
552 if (measure_headers_hol_blocking_time_) { | |
553 if (prev_max_timestamp_ > cur_max_timestamp_) { | |
554 // prev_max_timestamp_ > cur_max_timestamp_ implies that | |
555 // headers from lower numbered streams actually came off the | |
556 // wire after headers for the current stream, hence there was | |
557 // HOL blocking. | |
558 QuicTime::Delta delta = prev_max_timestamp_ - cur_max_timestamp_; | |
559 DVLOG(1) << "stream " << stream_id_ | |
560 << ": Net.QuicSession.HeadersHOLBlockedTime " | |
561 << delta.ToMilliseconds(); | |
562 spdy_session_->OnHeadersHeadOfLineBlocking(delta); | |
563 } | |
564 prev_max_timestamp_ = std::max(prev_max_timestamp_, cur_max_timestamp_); | |
565 cur_max_timestamp_ = QuicTime::Zero(); | |
566 } | |
567 if (promised_stream_id_ == kInvalidStreamId) { | |
568 spdy_session_->OnStreamHeaderList(stream_id_, fin_, frame_len_, | |
569 header_list); | |
570 } else { | |
571 spdy_session_->OnPromiseHeaderList(stream_id_, promised_stream_id_, | |
572 frame_len_, header_list); | |
573 } | |
574 // Reset state for the next frame. | |
575 promised_stream_id_ = kInvalidStreamId; | |
576 stream_id_ = kInvalidStreamId; | |
577 fin_ = false; | |
578 frame_len_ = 0; | |
579 uncompressed_frame_len_ = 0; | |
580 } | |
581 | |
582 void QuicHeadersStream::OnCompressedFrameSize(size_t frame_len) { | |
583 frame_len_ += frame_len; | |
584 } | |
585 | |
586 bool QuicHeadersStream::IsConnected() { | |
587 return session()->connection()->connected(); | |
588 } | |
589 | |
590 void QuicHeadersStream::DisableHpackDynamicTable() { | |
591 spdy_framer_.UpdateHeaderEncoderTableSize(0); | |
592 } | |
593 | |
594 void QuicHeadersStream::SetHpackEncoderDebugVisitor( | |
595 std::unique_ptr<HpackDebugVisitor> visitor) { | |
596 spdy_framer_.SetEncoderHeaderTableDebugVisitor( | |
597 std::unique_ptr<HeaderTableDebugVisitor>(new HeaderTableDebugVisitor( | |
598 session()->connection()->helper()->GetClock(), std::move(visitor)))); | |
599 } | |
600 | |
601 void QuicHeadersStream::SetHpackDecoderDebugVisitor( | |
602 std::unique_ptr<HpackDebugVisitor> visitor) { | |
603 spdy_framer_.SetDecoderHeaderTableDebugVisitor( | |
604 std::unique_ptr<HeaderTableDebugVisitor>(new HeaderTableDebugVisitor( | |
605 session()->connection()->helper()->GetClock(), std::move(visitor)))); | |
606 } | |
607 | |
608 void QuicHeadersStream::UpdateHeaderEncoderTableSize(uint32_t value) { | |
609 spdy_framer_.UpdateHeaderEncoderTableSize(value); | |
610 } | |
611 | |
612 bool QuicHeadersStream::OnDataFrameHeader(QuicStreamId stream_id, | |
613 size_t length, | |
614 bool fin) { | |
615 if (!spdy_session_->force_hol_blocking()) { | |
616 return false; | |
617 } | |
618 if (!IsConnected()) { | |
619 return true; | |
620 } | |
621 DVLOG(1) << "DATA frame header for stream " << stream_id << " length " | |
622 << length << " fin " << fin; | |
623 fin_ = fin; | |
624 frame_len_ = length; | |
625 if (fin && length == 0) { | |
626 OnStreamFrameData(stream_id, "", 0); | |
627 } | |
628 return true; | |
629 } | |
630 | |
631 bool QuicHeadersStream::OnStreamFrameData(QuicStreamId stream_id, | |
632 const char* data, | |
633 size_t len) { | |
634 if (!spdy_session_->force_hol_blocking()) { | |
635 return false; | |
636 } | |
637 if (!IsConnected()) { | |
638 return true; | |
639 } | |
640 frame_len_ -= len; | |
641 // Ignore fin_ while there is more data coming, if frame_len_ > 0. | |
642 spdy_session_->OnStreamFrameData(stream_id, data, len, | |
643 frame_len_ > 0 ? false : fin_); | |
644 return true; | |
645 } | |
646 | |
647 } // namespace net | |
OLD | NEW |