blob: 796edaf19dd6d0a20a8ad1f5845c8f245672714f [file] [log] [blame]
QUICHE teama6ef0a62019-03-07 20:34:33 -05001// Copyright (c) 2015 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/third_party/quiche/src/quic/core/http/quic_spdy_session.h"
6
7#include <algorithm>
8#include <cstdint>
vasilvv872e7a32019-03-12 16:42:44 -07009#include <string>
QUICHE teama6ef0a62019-03-07 20:34:33 -050010#include <utility>
11
12#include "net/third_party/quiche/src/quic/core/http/quic_headers_stream.h"
13#include "net/third_party/quiche/src/quic/core/quic_utils.h"
14#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
15#include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h"
16#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
17#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
18#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
19#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
20#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
QUICHE teama6ef0a62019-03-07 20:34:33 -050021#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
22#include "net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h"
23
24using http2::Http2DecoderAdapter;
25using spdy::HpackEntry;
26using spdy::HpackHeaderTable;
27using spdy::Http2WeightToSpdy3Priority;
28using spdy::SETTINGS_ENABLE_PUSH;
29using spdy::SETTINGS_HEADER_TABLE_SIZE;
30using spdy::SETTINGS_MAX_HEADER_LIST_SIZE;
31using spdy::Spdy3PriorityToHttp2Weight;
32using spdy::SpdyErrorCode;
33using spdy::SpdyFramer;
34using spdy::SpdyFramerDebugVisitorInterface;
35using spdy::SpdyFramerVisitorInterface;
36using spdy::SpdyFrameType;
37using spdy::SpdyHeaderBlock;
38using spdy::SpdyHeadersHandlerInterface;
39using spdy::SpdyHeadersIR;
40using spdy::SpdyKnownSettingsId;
41using spdy::SpdyPingId;
42using spdy::SpdyPriority;
43using spdy::SpdyPriorityIR;
44using spdy::SpdyPushPromiseIR;
45using spdy::SpdySerializedFrame;
46using spdy::SpdySettingsId;
47using spdy::SpdySettingsIR;
48using spdy::SpdyStreamId;
49
50namespace quic {
51
52namespace {
53
54class HeaderTableDebugVisitor : public HpackHeaderTable::DebugVisitorInterface {
55 public:
56 HeaderTableDebugVisitor(const QuicClock* clock,
57 std::unique_ptr<QuicHpackDebugVisitor> visitor)
58 : clock_(clock), headers_stream_hpack_visitor_(std::move(visitor)) {}
59 HeaderTableDebugVisitor(const HeaderTableDebugVisitor&) = delete;
60 HeaderTableDebugVisitor& operator=(const HeaderTableDebugVisitor&) = delete;
61
62 int64_t OnNewEntry(const HpackEntry& entry) override {
63 QUIC_DVLOG(1) << entry.GetDebugString();
64 return (clock_->ApproximateNow() - QuicTime::Zero()).ToMicroseconds();
65 }
66
67 void OnUseEntry(const HpackEntry& entry) override {
68 const QuicTime::Delta elapsed(
69 clock_->ApproximateNow() -
70 QuicTime::Delta::FromMicroseconds(entry.time_added()) -
71 QuicTime::Zero());
72 QUIC_DVLOG(1) << entry.GetDebugString() << " " << elapsed.ToMilliseconds()
73 << " ms";
74 headers_stream_hpack_visitor_->OnUseEntry(elapsed);
75 }
76
77 private:
78 const QuicClock* clock_;
79 std::unique_ptr<QuicHpackDebugVisitor> headers_stream_hpack_visitor_;
80};
81
82} // namespace
83
84// A SpdyFramerVisitor that passes HEADERS frames to the QuicSpdyStream, and
85// closes the connection if any unexpected frames are received.
86class QuicSpdySession::SpdyFramerVisitor
87 : public SpdyFramerVisitorInterface,
88 public SpdyFramerDebugVisitorInterface {
89 public:
90 explicit SpdyFramerVisitor(QuicSpdySession* session) : session_(session) {}
91 SpdyFramerVisitor(const SpdyFramerVisitor&) = delete;
92 SpdyFramerVisitor& operator=(const SpdyFramerVisitor&) = delete;
93
94 SpdyHeadersHandlerInterface* OnHeaderFrameStart(
95 SpdyStreamId /* stream_id */) override {
96 return &header_list_;
97 }
98
99 void OnHeaderFrameEnd(SpdyStreamId /* stream_id */) override {
100 if (session_->IsConnected()) {
101 session_->OnHeaderList(header_list_);
102 }
103 header_list_.Clear();
104 }
105
106 void OnStreamFrameData(SpdyStreamId stream_id,
107 const char* data,
108 size_t len) override {
109 CloseConnection("SPDY DATA frame received.",
110 QUIC_INVALID_HEADERS_STREAM_DATA);
111 }
112
113 void OnStreamEnd(SpdyStreamId stream_id) override {
114 // The framer invokes OnStreamEnd after processing a frame that had the fin
115 // bit set.
116 }
117
118 void OnStreamPadding(SpdyStreamId stream_id, size_t len) override {
119 CloseConnection("SPDY frame padding received.",
120 QUIC_INVALID_HEADERS_STREAM_DATA);
121 }
122
123 void OnError(Http2DecoderAdapter::SpdyFramerError error) override {
124 QuicErrorCode code = QUIC_INVALID_HEADERS_STREAM_DATA;
125 switch (error) {
126 case Http2DecoderAdapter::SpdyFramerError::SPDY_DECOMPRESS_FAILURE:
127 code = QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE;
128 break;
129 default:
130 break;
131 }
132 CloseConnection(
133 QuicStrCat("SPDY framing error: ",
134 Http2DecoderAdapter::SpdyFramerErrorToString(error)),
135 code);
136 }
137
138 void OnDataFrameHeader(SpdyStreamId stream_id,
139 size_t length,
140 bool fin) override {
141 CloseConnection("SPDY DATA frame received.",
142 QUIC_INVALID_HEADERS_STREAM_DATA);
143 }
144
145 void OnRstStream(SpdyStreamId stream_id, SpdyErrorCode error_code) override {
146 CloseConnection("SPDY RST_STREAM frame received.",
147 QUIC_INVALID_HEADERS_STREAM_DATA);
148 }
149
150 void OnSetting(SpdySettingsId id, uint32_t value) override {
151 switch (id) {
152 case SETTINGS_HEADER_TABLE_SIZE:
153 session_->UpdateHeaderEncoderTableSize(value);
154 break;
155 case SETTINGS_ENABLE_PUSH:
156 if (session_->perspective() == Perspective::IS_SERVER) {
157 // See rfc7540, Section 6.5.2.
158 if (value > 1) {
159 CloseConnection(
160 QuicStrCat("Invalid value for SETTINGS_ENABLE_PUSH: ", value),
161 QUIC_INVALID_HEADERS_STREAM_DATA);
162 return;
163 }
164 session_->UpdateEnableServerPush(value > 0);
165 break;
166 } else {
167 CloseConnection(
168 QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ", id),
169 QUIC_INVALID_HEADERS_STREAM_DATA);
170 }
171 break;
172 // TODO(fayang): Need to support SETTINGS_MAX_HEADER_LIST_SIZE when
173 // clients are actually sending it.
174 case SETTINGS_MAX_HEADER_LIST_SIZE:
175 break;
176 default:
177 CloseConnection(
178 QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ", id),
179 QUIC_INVALID_HEADERS_STREAM_DATA);
180 }
181 }
182
183 void OnSettingsEnd() override {}
184
185 void OnPing(SpdyPingId unique_id, bool is_ack) override {
186 CloseConnection("SPDY PING frame received.",
187 QUIC_INVALID_HEADERS_STREAM_DATA);
188 }
189
190 void OnGoAway(SpdyStreamId last_accepted_stream_id,
191 SpdyErrorCode error_code) override {
192 CloseConnection("SPDY GOAWAY frame received.",
193 QUIC_INVALID_HEADERS_STREAM_DATA);
194 }
195
196 void OnHeaders(SpdyStreamId stream_id,
197 bool has_priority,
198 int weight,
199 SpdyStreamId /*parent_stream_id*/,
200 bool /*exclusive*/,
201 bool fin,
202 bool end) override {
203 if (!session_->IsConnected()) {
204 return;
205 }
206
207 // TODO(mpw): avoid down-conversion and plumb SpdyStreamPrecedence through
208 // QuicHeadersStream.
209 SpdyPriority priority =
210 has_priority ? Http2WeightToSpdy3Priority(weight) : 0;
211 session_->OnHeaders(stream_id, has_priority, priority, fin);
212 }
213
214 void OnWindowUpdate(SpdyStreamId stream_id, int delta_window_size) override {
215 CloseConnection("SPDY WINDOW_UPDATE frame received.",
216 QUIC_INVALID_HEADERS_STREAM_DATA);
217 }
218
219 void OnPushPromise(SpdyStreamId stream_id,
220 SpdyStreamId promised_stream_id,
221 bool end) override {
222 if (!session_->supports_push_promise()) {
223 CloseConnection("PUSH_PROMISE not supported.",
224 QUIC_INVALID_HEADERS_STREAM_DATA);
225 return;
226 }
227 if (!session_->IsConnected()) {
228 return;
229 }
230 session_->OnPushPromise(stream_id, promised_stream_id, end);
231 }
232
233 void OnContinuation(SpdyStreamId stream_id, bool end) override {}
234
235 void OnPriority(SpdyStreamId stream_id,
236 SpdyStreamId parent_id,
237 int weight,
238 bool exclusive) override {
239 if (session_->connection()->transport_version() <= QUIC_VERSION_39) {
240 CloseConnection("SPDY PRIORITY frame received.",
241 QUIC_INVALID_HEADERS_STREAM_DATA);
242 return;
243 }
244 if (!session_->IsConnected()) {
245 return;
246 }
247 // TODO (wangyix): implement real HTTP/2 weights and dependencies instead of
248 // converting to SpdyPriority.
249 SpdyPriority priority = Http2WeightToSpdy3Priority(weight);
250 session_->OnPriority(stream_id, priority);
251 }
252
253 bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) override {
254 CloseConnection("Unknown frame type received.",
255 QUIC_INVALID_HEADERS_STREAM_DATA);
256 return false;
257 }
258
259 // SpdyFramerDebugVisitorInterface implementation
260 void OnSendCompressedFrame(SpdyStreamId stream_id,
261 SpdyFrameType type,
262 size_t payload_len,
263 size_t frame_len) override {
264 if (payload_len == 0) {
265 QUIC_BUG << "Zero payload length.";
266 return;
267 }
268 int compression_pct = 100 - (100 * frame_len) / payload_len;
269 QUIC_DVLOG(1) << "Net.QuicHpackCompressionPercentage: " << compression_pct;
270 }
271
272 void OnReceiveCompressedFrame(SpdyStreamId stream_id,
273 SpdyFrameType type,
274 size_t frame_len) override {
275 if (session_->IsConnected()) {
276 session_->OnCompressedFrameSize(frame_len);
277 }
278 }
279
280 void set_max_uncompressed_header_bytes(
281 size_t set_max_uncompressed_header_bytes) {
282 header_list_.set_max_header_list_size(set_max_uncompressed_header_bytes);
283 }
284
285 private:
vasilvvc48c8712019-03-11 13:38:16 -0700286 void CloseConnection(const std::string& details, QuicErrorCode code) {
QUICHE teama6ef0a62019-03-07 20:34:33 -0500287 if (session_->IsConnected()) {
288 session_->CloseConnectionWithDetails(code, details);
289 }
290 }
291
292 private:
293 QuicSpdySession* session_;
294 QuicHeaderList header_list_;
295};
296
297QuicHpackDebugVisitor::QuicHpackDebugVisitor() {}
298
299QuicHpackDebugVisitor::~QuicHpackDebugVisitor() {}
300
301QuicSpdySession::QuicSpdySession(
302 QuicConnection* connection,
303 QuicSession::Visitor* visitor,
304 const QuicConfig& config,
305 const ParsedQuicVersionVector& supported_versions)
306 : QuicSession(connection, visitor, config, supported_versions),
307 max_inbound_header_list_size_(kDefaultMaxUncompressedHeaderSize),
308 server_push_enabled_(true),
309 stream_id_(
310 QuicUtils::GetInvalidStreamId(connection->transport_version())),
311 promised_stream_id_(
312 QuicUtils::GetInvalidStreamId(connection->transport_version())),
313 fin_(false),
314 frame_len_(0),
315 uncompressed_frame_len_(0),
316 supports_push_promise_(perspective() == Perspective::IS_CLIENT),
317 spdy_framer_(SpdyFramer::ENABLE_COMPRESSION),
318 spdy_framer_visitor_(new SpdyFramerVisitor(this)) {
319 h2_deframer_.set_visitor(spdy_framer_visitor_.get());
320 h2_deframer_.set_debug_visitor(spdy_framer_visitor_.get());
321 spdy_framer_.set_debug_visitor(spdy_framer_visitor_.get());
322}
323
324QuicSpdySession::~QuicSpdySession() {
325 // Set the streams' session pointers in closed and dynamic stream lists
326 // to null to avoid subsequent use of this session.
327 for (auto& stream : *closed_streams()) {
328 static_cast<QuicSpdyStream*>(stream.get())->ClearSession();
329 }
330 for (auto const& kv : zombie_streams()) {
331 static_cast<QuicSpdyStream*>(kv.second.get())->ClearSession();
332 }
333 for (auto const& kv : dynamic_streams()) {
334 static_cast<QuicSpdyStream*>(kv.second.get())->ClearSession();
335 }
336}
337
338void QuicSpdySession::Initialize() {
339 QuicSession::Initialize();
340
341 if (perspective() == Perspective::IS_SERVER) {
342 set_largest_peer_created_stream_id(
343 QuicUtils::GetHeadersStreamId(connection()->transport_version()));
344 } else {
345 QuicStreamId headers_stream_id = GetNextOutgoingBidirectionalStreamId();
346 DCHECK_EQ(headers_stream_id,
347 QuicUtils::GetHeadersStreamId(connection()->transport_version()));
348 }
349
350 if (VersionUsesQpack(connection()->transport_version())) {
351 qpack_encoder_ = QuicMakeUnique<QpackEncoder>(this, this);
352 qpack_decoder_ = QuicMakeUnique<QpackDecoder>(this, this);
353 }
354
355 headers_stream_ = QuicMakeUnique<QuicHeadersStream>((this));
356 DCHECK_EQ(QuicUtils::GetHeadersStreamId(connection()->transport_version()),
357 headers_stream_->id());
renjietangfbeb5bf2019-04-19 15:06:20 -0700358 if (!GetQuicReloadableFlag(quic_eliminate_static_stream_map)) {
359 RegisterStaticStream(
360 QuicUtils::GetHeadersStreamId(connection()->transport_version()),
361 headers_stream_.get());
362 } else {
renjietang08a9cf72019-04-23 17:01:34 -0700363 QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map, 7, 15);
renjietangfbeb5bf2019-04-19 15:06:20 -0700364 unowned_headers_stream_ = headers_stream_.get();
365 RegisterStaticStreamNew(std::move(headers_stream_));
366 }
QUICHE teama6ef0a62019-03-07 20:34:33 -0500367
368 set_max_uncompressed_header_bytes(max_inbound_header_list_size_);
369
370 // Limit HPACK buffering to 2x header list size limit.
371 set_max_decode_buffer_size_bytes(2 * max_inbound_header_list_size_);
372}
373
374void QuicSpdySession::OnDecoderStreamError(QuicStringPiece error_message) {
375 DCHECK(VersionUsesQpack(connection()->transport_version()));
376
377 // TODO(112770235): Signal connection error on decoder stream errors.
378 QUIC_NOTREACHED();
379}
380
381void QuicSpdySession::WriteEncoderStreamData(QuicStringPiece data) {
382 DCHECK(VersionUsesQpack(connection()->transport_version()));
383
384 // TODO(112770235): Send encoder stream data on encoder stream.
385 QUIC_NOTREACHED();
386}
387
388void QuicSpdySession::OnEncoderStreamError(QuicStringPiece error_message) {
389 DCHECK(VersionUsesQpack(connection()->transport_version()));
390
391 // TODO(112770235): Signal connection error on encoder stream errors.
392 QUIC_NOTREACHED();
393}
394
395void QuicSpdySession::WriteDecoderStreamData(QuicStringPiece data) {
396 DCHECK(VersionUsesQpack(connection()->transport_version()));
397
398 // TODO(112770235): Send decoder stream data on decoder stream.
399 QUIC_NOTREACHED();
400}
401
402void QuicSpdySession::OnStreamHeadersPriority(QuicStreamId stream_id,
403 SpdyPriority priority) {
404 QuicSpdyStream* stream = GetSpdyDataStream(stream_id);
405 if (!stream) {
406 // It's quite possible to receive headers after a stream has been reset.
407 return;
408 }
409 stream->OnStreamHeadersPriority(priority);
410}
411
412void QuicSpdySession::OnStreamHeaderList(QuicStreamId stream_id,
413 bool fin,
414 size_t frame_len,
415 const QuicHeaderList& header_list) {
416 if (QuicContainsKey(static_streams(), stream_id)) {
417 connection()->CloseConnection(
418 QUIC_INVALID_HEADERS_STREAM_DATA, "stream is static",
419 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
420 return;
421 }
422 QuicSpdyStream* stream = GetSpdyDataStream(stream_id);
423 if (stream == nullptr) {
424 // The stream no longer exists, but trailing headers may contain the final
425 // byte offset necessary for flow control and open stream accounting.
426 size_t final_byte_offset = 0;
427 for (const auto& header : header_list) {
vasilvvc48c8712019-03-11 13:38:16 -0700428 const std::string& header_key = header.first;
429 const std::string& header_value = header.second;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500430 if (header_key == kFinalOffsetHeaderKey) {
431 if (!QuicTextUtils::StringToSizeT(header_value, &final_byte_offset)) {
432 connection()->CloseConnection(
433 QUIC_INVALID_HEADERS_STREAM_DATA,
434 "Trailers are malformed (no final offset)",
435 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
436 return;
437 }
438 DVLOG(1) << "Received final byte offset in trailers for stream "
439 << stream_id << ", which no longer exists.";
440 OnFinalByteOffsetReceived(stream_id, final_byte_offset);
441 }
442 }
443
444 // It's quite possible to receive headers after a stream has been reset.
445 return;
446 }
renjietangfbeb5bf2019-04-19 15:06:20 -0700447 if (GetQuicReloadableFlag(quic_eliminate_static_stream_map) &&
448 stream->is_static()) {
renjietang08a9cf72019-04-23 17:01:34 -0700449 QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map, 8, 15);
renjietangfbeb5bf2019-04-19 15:06:20 -0700450 connection()->CloseConnection(
451 QUIC_INVALID_HEADERS_STREAM_DATA, "stream is static",
452 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
453 return;
454 }
QUICHE teama6ef0a62019-03-07 20:34:33 -0500455 stream->OnStreamHeaderList(fin, frame_len, header_list);
456}
457
458void QuicSpdySession::OnPriorityFrame(QuicStreamId stream_id,
459 SpdyPriority priority) {
460 QuicSpdyStream* stream = GetSpdyDataStream(stream_id);
461 if (!stream) {
462 // It's quite possible to receive a PRIORITY frame after a stream has been
463 // reset.
464 return;
465 }
466 stream->OnPriorityFrame(priority);
467}
468
469size_t QuicSpdySession::ProcessHeaderData(const struct iovec& iov) {
470 return h2_deframer_.ProcessInput(static_cast<char*>(iov.iov_base),
471 iov.iov_len);
472}
473
474size_t QuicSpdySession::WriteHeadersOnHeadersStream(
475 QuicStreamId id,
476 SpdyHeaderBlock headers,
477 bool fin,
478 SpdyPriority priority,
479 QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
480 return WriteHeadersOnHeadersStreamImpl(
481 id, std::move(headers), fin,
482 /* parent_stream_id = */ 0, Spdy3PriorityToHttp2Weight(priority),
483 /* exclusive = */ false, std::move(ack_listener));
484}
485
486size_t QuicSpdySession::WritePriority(QuicStreamId id,
487 QuicStreamId parent_stream_id,
488 int weight,
489 bool exclusive) {
490 if (connection()->transport_version() <= QUIC_VERSION_39) {
491 return 0;
492 }
493 SpdyPriorityIR priority_frame(id, parent_stream_id, weight, exclusive);
494 SpdySerializedFrame frame(spdy_framer_.SerializeFrame(priority_frame));
renjietangfbeb5bf2019-04-19 15:06:20 -0700495 headers_stream()->WriteOrBufferData(
QUICHE teama6ef0a62019-03-07 20:34:33 -0500496 QuicStringPiece(frame.data(), frame.size()), false, nullptr);
497 return frame.size();
498}
499
500size_t QuicSpdySession::WritePushPromise(QuicStreamId original_stream_id,
501 QuicStreamId promised_stream_id,
502 SpdyHeaderBlock headers) {
503 if (perspective() == Perspective::IS_CLIENT) {
504 QUIC_BUG << "Client shouldn't send PUSH_PROMISE";
505 return 0;
506 }
507
508 SpdyPushPromiseIR push_promise(original_stream_id, promised_stream_id,
509 std::move(headers));
510 // PUSH_PROMISE must not be the last frame sent out, at least followed by
511 // response headers.
512 push_promise.set_fin(false);
513
514 SpdySerializedFrame frame(spdy_framer_.SerializeFrame(push_promise));
renjietangfbeb5bf2019-04-19 15:06:20 -0700515 headers_stream()->WriteOrBufferData(
QUICHE teama6ef0a62019-03-07 20:34:33 -0500516 QuicStringPiece(frame.data(), frame.size()), false, nullptr);
517 return frame.size();
518}
519
520size_t QuicSpdySession::SendMaxHeaderListSize(size_t value) {
521 SpdySettingsIR settings_frame;
522 settings_frame.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, value);
523
524 SpdySerializedFrame frame(spdy_framer_.SerializeFrame(settings_frame));
renjietangfbeb5bf2019-04-19 15:06:20 -0700525 headers_stream()->WriteOrBufferData(
QUICHE teama6ef0a62019-03-07 20:34:33 -0500526 QuicStringPiece(frame.data(), frame.size()), false, nullptr);
527 return frame.size();
528}
529
530QpackEncoder* QuicSpdySession::qpack_encoder() {
531 DCHECK(VersionUsesQpack(connection()->transport_version()));
532
533 return qpack_encoder_.get();
534}
535
536QpackDecoder* QuicSpdySession::qpack_decoder() {
537 DCHECK(VersionUsesQpack(connection()->transport_version()));
538
539 return qpack_decoder_.get();
540}
541
542QuicSpdyStream* QuicSpdySession::GetSpdyDataStream(
543 const QuicStreamId stream_id) {
544 return static_cast<QuicSpdyStream*>(GetOrCreateDynamicStream(stream_id));
545}
546
547void QuicSpdySession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
548 QuicSession::OnCryptoHandshakeEvent(event);
549 if (event == HANDSHAKE_CONFIRMED && config()->SupportMaxHeaderListSize()) {
550 SendMaxHeaderListSize(max_inbound_header_list_size_);
551 }
552}
553
554// True if there are open HTTP requests.
555bool QuicSpdySession::ShouldKeepConnectionAlive() const {
556 // Change to check if there are open HTTP requests.
557 // When IETF QUIC control and QPACK streams are used, those will need to be
558 // subtracted from this count to ensure only request streams are counted.
559 return GetNumOpenDynamicStreams() > 0;
560}
561
562bool QuicSpdySession::ShouldBufferIncomingStream(QuicStreamId id) const {
563 DCHECK_EQ(QUIC_VERSION_99, connection()->transport_version());
564 return !QuicUtils::IsBidirectionalStreamId(id);
565}
566
567size_t QuicSpdySession::WriteHeadersOnHeadersStreamImpl(
568 QuicStreamId id,
569 spdy::SpdyHeaderBlock headers,
570 bool fin,
571 QuicStreamId parent_stream_id,
572 int weight,
573 bool exclusive,
574 QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
575 SpdyHeadersIR headers_frame(id, std::move(headers));
576 headers_frame.set_fin(fin);
577 if (perspective() == Perspective::IS_CLIENT) {
578 headers_frame.set_has_priority(true);
579 headers_frame.set_parent_stream_id(parent_stream_id);
580 headers_frame.set_weight(weight);
581 headers_frame.set_exclusive(exclusive);
582 }
583 SpdySerializedFrame frame(spdy_framer_.SerializeFrame(headers_frame));
renjietangfbeb5bf2019-04-19 15:06:20 -0700584 headers_stream()->WriteOrBufferData(
QUICHE teama6ef0a62019-03-07 20:34:33 -0500585 QuicStringPiece(frame.data(), frame.size()), false,
586 std::move(ack_listener));
587 return frame.size();
588}
589
590void QuicSpdySession::OnPromiseHeaderList(QuicStreamId stream_id,
591 QuicStreamId promised_stream_id,
592 size_t frame_len,
593 const QuicHeaderList& header_list) {
vasilvvc48c8712019-03-11 13:38:16 -0700594 std::string error =
595 "OnPromiseHeaderList should be overridden in client code.";
QUICHE teama6ef0a62019-03-07 20:34:33 -0500596 QUIC_BUG << error;
597 connection()->CloseConnection(QUIC_INTERNAL_ERROR, error,
598 ConnectionCloseBehavior::SILENT_CLOSE);
599}
600
601bool QuicSpdySession::ShouldReleaseHeadersStreamSequencerBuffer() {
602 return false;
603}
604
605void QuicSpdySession::OnHeaders(SpdyStreamId stream_id,
606 bool has_priority,
607 SpdyPriority priority,
608 bool fin) {
609 if (has_priority) {
610 if (perspective() == Perspective::IS_CLIENT) {
611 CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
612 "Server must not send priorities.");
613 return;
614 }
615 OnStreamHeadersPriority(stream_id, priority);
616 } else {
617 if (perspective() == Perspective::IS_SERVER) {
618 CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
619 "Client must send priorities.");
620 return;
621 }
622 }
623 DCHECK_EQ(QuicUtils::GetInvalidStreamId(connection()->transport_version()),
624 stream_id_);
625 DCHECK_EQ(QuicUtils::GetInvalidStreamId(connection()->transport_version()),
626 promised_stream_id_);
627 stream_id_ = stream_id;
628 fin_ = fin;
629}
630
631void QuicSpdySession::OnPushPromise(SpdyStreamId stream_id,
632 SpdyStreamId promised_stream_id,
633 bool end) {
634 DCHECK_EQ(QuicUtils::GetInvalidStreamId(connection()->transport_version()),
635 stream_id_);
636 DCHECK_EQ(QuicUtils::GetInvalidStreamId(connection()->transport_version()),
637 promised_stream_id_);
638 stream_id_ = stream_id;
639 promised_stream_id_ = promised_stream_id;
640}
641
642// TODO (wangyix): Why is SpdyStreamId used instead of QuicStreamId?
643// This occurs in many places in this file.
644void QuicSpdySession::OnPriority(SpdyStreamId stream_id,
645 SpdyPriority priority) {
646 if (perspective() == Perspective::IS_CLIENT) {
647 CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
648 "Server must not send PRIORITY frames.");
649 return;
650 }
651 OnPriorityFrame(stream_id, priority);
652}
653
654void QuicSpdySession::OnHeaderList(const QuicHeaderList& header_list) {
655 QUIC_DVLOG(1) << "Received header list for stream " << stream_id_ << ": "
656 << header_list.DebugString();
657 if (promised_stream_id_ ==
658 QuicUtils::GetInvalidStreamId(connection()->transport_version())) {
659 OnStreamHeaderList(stream_id_, fin_, frame_len_, header_list);
660 } else {
661 OnPromiseHeaderList(stream_id_, promised_stream_id_, frame_len_,
662 header_list);
663 }
664 // Reset state for the next frame.
665 promised_stream_id_ =
666 QuicUtils::GetInvalidStreamId(connection()->transport_version());
667 stream_id_ = QuicUtils::GetInvalidStreamId(connection()->transport_version());
668 fin_ = false;
669 frame_len_ = 0;
670 uncompressed_frame_len_ = 0;
671}
672
673void QuicSpdySession::OnCompressedFrameSize(size_t frame_len) {
674 frame_len_ += frame_len;
675}
676
677void QuicSpdySession::SetHpackEncoderDebugVisitor(
678 std::unique_ptr<QuicHpackDebugVisitor> visitor) {
679 spdy_framer_.SetEncoderHeaderTableDebugVisitor(
680 std::unique_ptr<HeaderTableDebugVisitor>(new HeaderTableDebugVisitor(
681 connection()->helper()->GetClock(), std::move(visitor))));
682}
683
684void QuicSpdySession::SetHpackDecoderDebugVisitor(
685 std::unique_ptr<QuicHpackDebugVisitor> visitor) {
686 h2_deframer_.SetDecoderHeaderTableDebugVisitor(
687 QuicMakeUnique<HeaderTableDebugVisitor>(
688 connection()->helper()->GetClock(), std::move(visitor)));
689}
690
691void QuicSpdySession::UpdateHeaderEncoderTableSize(uint32_t value) {
692 spdy_framer_.UpdateHeaderEncoderTableSize(value);
693}
694
695void QuicSpdySession::UpdateEnableServerPush(bool value) {
696 set_server_push_enabled(value);
697}
698
699void QuicSpdySession::set_max_uncompressed_header_bytes(
700 size_t set_max_uncompressed_header_bytes) {
701 spdy_framer_visitor_->set_max_uncompressed_header_bytes(
702 set_max_uncompressed_header_bytes);
703}
704
705void QuicSpdySession::CloseConnectionWithDetails(QuicErrorCode error,
vasilvvc48c8712019-03-11 13:38:16 -0700706 const std::string& details) {
QUICHE teama6ef0a62019-03-07 20:34:33 -0500707 connection()->CloseConnection(
708 error, details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
709}
710
QUICHE teamc2653c42019-03-08 13:30:06 -0800711bool QuicSpdySession::HasActiveRequestStreams() const {
renjietangfbeb5bf2019-04-19 15:06:20 -0700712 if (!GetQuicReloadableFlag(quic_eliminate_static_stream_map)) {
713 return !dynamic_streams().empty();
714 }
715 // In the case where session is destructed by calling
716 // dynamic_streams().clear(), we will have incorrect accounting here.
717 // TODO(renjietang): Modify destructors and make this a DCHECK.
renjietang08a9cf72019-04-23 17:01:34 -0700718 QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map, 9, 15);
renjietangfbeb5bf2019-04-19 15:06:20 -0700719 if (static_cast<size_t>(dynamic_streams().size()) >
720 num_incoming_static_streams() + num_outgoing_static_streams()) {
721 return dynamic_streams().size() - num_incoming_static_streams() -
722 num_outgoing_static_streams() >
723 0;
724 }
725 return false;
QUICHE teamc2653c42019-03-08 13:30:06 -0800726}
727
QUICHE teama6ef0a62019-03-07 20:34:33 -0500728} // namespace quic