blob: 9389af403cac1d5f16c23f1e5f412d5450e440bc [file] [log] [blame]
[email protected]9c0b1352012-11-04 00:03:271// Copyright (c) 2012 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_session.h"
6
[email protected]5ffa5622012-12-23 03:07:257#include "base/stl_util.h"
[email protected]2662ed562013-07-03 10:27:468#include "net/quic/crypto/proof_verifier.h"
[email protected]9c0b1352012-11-04 00:03:279#include "net/quic/quic_connection.h"
[email protected]a69af0522013-07-12 19:23:4710#include "net/ssl/ssl_info.h"
[email protected]9c0b1352012-11-04 00:03:2711
12using base::StringPiece;
13using base::hash_map;
14using base::hash_set;
[email protected]3e60db82013-08-05 19:43:0615using std::make_pair;
[email protected]9c0b1352012-11-04 00:03:2716using std::vector;
17
18namespace net {
19
[email protected]3e60db82013-08-05 19:43:0620const size_t kMaxPrematurelyClosedStreamsTracked = 20;
[email protected]06ff5152013-08-29 01:03:0521const size_t kMaxZombieStreams = 20;
[email protected]3e60db82013-08-05 19:43:0622
[email protected]25c31dc2013-06-05 17:56:0423#define ENDPOINT (is_server_ ? "Server: " : " Client: ")
24
[email protected]5ffa5622012-12-23 03:07:2525// We want to make sure we delete any closed streams in a safe manner.
26// To avoid deleting a stream in mid-operation, we have a simple shim between
27// us and the stream, so we can delete any streams when we return from
28// processing.
29//
30// We could just override the base methods, but this makes it easier to make
31// sure we don't miss any.
32class VisitorShim : public QuicConnectionVisitorInterface {
33 public:
34 explicit VisitorShim(QuicSession* session) : session_(session) {}
35
[email protected]4d1789c32013-09-18 23:54:3636 virtual bool OnStreamFrames(const vector<QuicStreamFrame>& frames) OVERRIDE {
37 bool accepted = session_->OnStreamFrames(frames);
[email protected]5ffa5622012-12-23 03:07:2538 session_->PostProcessAfterData();
39 return accepted;
40 }
[email protected]46fadfd2013-02-06 09:40:1641 virtual void OnRstStream(const QuicRstStreamFrame& frame) OVERRIDE {
[email protected]5ffa5622012-12-23 03:07:2542 session_->OnRstStream(frame);
43 session_->PostProcessAfterData();
44 }
45
[email protected]9db443912013-02-25 05:27:0346 virtual void OnGoAway(const QuicGoAwayFrame& frame) OVERRIDE {
47 session_->OnGoAway(frame);
48 session_->PostProcessAfterData();
49 }
50
[email protected]46fadfd2013-02-06 09:40:1651 virtual bool OnCanWrite() OVERRIDE {
[email protected]5ffa5622012-12-23 03:07:2552 bool rc = session_->OnCanWrite();
53 session_->PostProcessAfterData();
54 return rc;
55 }
56
[email protected]24e5bc52013-09-18 15:36:5857 virtual void OnSuccessfulVersionNegotiation(
58 const QuicVersion& version) OVERRIDE {
59 session_->OnSuccessfulVersionNegotiation(version);
60 }
61
[email protected]46fadfd2013-02-06 09:40:1662 virtual void ConnectionClose(QuicErrorCode error, bool from_peer) OVERRIDE {
[email protected]5ffa5622012-12-23 03:07:2563 session_->ConnectionClose(error, from_peer);
64 // The session will go away, so don't bother with cleanup.
65 }
66
[email protected]4d1789c32013-09-18 23:54:3667 virtual bool HasPendingHandshake() const OVERRIDE {
68 return session_->HasPendingHandshake();
69 }
70
[email protected]5ffa5622012-12-23 03:07:2571 private:
72 QuicSession* session_;
73};
74
[email protected]899951652013-05-16 12:52:3975QuicSession::QuicSession(QuicConnection* connection,
76 const QuicConfig& config,
77 bool is_server)
[email protected]9c0b1352012-11-04 00:03:2778 : connection_(connection),
[email protected]5ffa5622012-12-23 03:07:2579 visitor_shim_(new VisitorShim(this)),
[email protected]899951652013-05-16 12:52:3980 config_(config),
[email protected]ec640112013-08-09 03:56:1881 max_open_streams_(config_.max_streams_per_connection()),
[email protected]9c0b1352012-11-04 00:03:2782 next_stream_id_(is_server ? 2 : 3),
83 is_server_(is_server),
[email protected]9db443912013-02-25 05:27:0384 largest_peer_created_stream_id_(0),
[email protected]25c31dc2013-06-05 17:56:0485 error_(QUIC_NO_ERROR),
[email protected]9db443912013-02-25 05:27:0386 goaway_received_(false),
[email protected]4d1789c32013-09-18 23:54:3687 goaway_sent_(false),
88 has_pending_handshake_(false) {
[email protected]b064310782013-05-30 21:12:1789
90 connection_->set_visitor(visitor_shim_.get());
91 connection_->SetIdleNetworkTimeout(config_.idle_connection_state_lifetime());
[email protected]ec640112013-08-09 03:56:1892 if (connection_->connected()) {
93 connection_->SetOverallConnectionTimeout(
94 config_.max_time_before_crypto_handshake());
95 }
[email protected]b064310782013-05-30 21:12:1796 // TODO(satyamshekhar): Set congestion control and ICSL also.
[email protected]9c0b1352012-11-04 00:03:2797}
98
99QuicSession::~QuicSession() {
[email protected]63534512012-12-23 18:49:00100 STLDeleteElements(&closed_streams_);
101 STLDeleteValues(&stream_map_);
[email protected]9c0b1352012-11-04 00:03:27102}
103
[email protected]4d1789c32013-09-18 23:54:36104bool QuicSession::OnStreamFrames(const vector<QuicStreamFrame>& frames) {
[email protected]9c0b1352012-11-04 00:03:27105 for (size_t i = 0; i < frames.size(); ++i) {
106 // TODO(rch) deal with the error case of stream id 0
[email protected]3e60db82013-08-05 19:43:06107 if (IsClosedStream(frames[i].stream_id)) {
108 // If we get additional frames for a stream where we didn't process
109 // headers, it's highly likely our compression context will end up
110 // permanently out of sync with the peer's, so we give up and close the
111 // connection.
112 if (ContainsKey(prematurely_closed_streams_, frames[i].stream_id)) {
113 connection()->SendConnectionClose(
114 QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED);
115 return false;
116 }
117 continue;
118 }
[email protected]9c0b1352012-11-04 00:03:27119
120 ReliableQuicStream* stream = GetStream(frames[i].stream_id);
121 if (stream == NULL) return false;
122 if (!stream->WillAcceptStreamFrame(frames[i])) return false;
123
124 // TODO(alyssar) check against existing connection address: if changed, make
125 // sure we update the connection.
126 }
127
128 for (size_t i = 0; i < frames.size(); ++i) {
[email protected]06ff5152013-08-29 01:03:05129 QuicStreamId stream_id = frames[i].stream_id;
130 ReliableQuicStream* stream = GetStream(stream_id);
131 if (!stream) {
132 continue;
133 }
134 stream->OnStreamFrame(frames[i]);
135
136 // If the stream had been prematurely closed, and the
137 // headers are now decompressed, then we are finally finished
138 // with this stream.
139 if (ContainsKey(zombie_streams_, stream_id) &&
140 stream->headers_decompressed()) {
141 CloseZombieStream(stream_id);
[email protected]9c0b1352012-11-04 00:03:27142 }
143 }
[email protected]c244c5a12013-05-07 20:55:04144
145 while (!decompression_blocked_streams_.empty()) {
146 QuicHeaderId header_id = decompression_blocked_streams_.begin()->first;
[email protected]b064310782013-05-30 21:12:17147 if (header_id != decompressor_.current_header_id()) {
148 break;
[email protected]c244c5a12013-05-07 20:55:04149 }
[email protected]b064310782013-05-30 21:12:17150 QuicStreamId stream_id = decompression_blocked_streams_.begin()->second;
151 decompression_blocked_streams_.erase(header_id);
152 ReliableQuicStream* stream = GetStream(stream_id);
153 if (!stream) {
154 connection()->SendConnectionClose(
155 QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED);
[email protected]ec640112013-08-09 03:56:18156 return false;
[email protected]b064310782013-05-30 21:12:17157 }
158 stream->OnDecompressorAvailable();
[email protected]c244c5a12013-05-07 20:55:04159 }
[email protected]9c0b1352012-11-04 00:03:27160 return true;
161}
162
163void QuicSession::OnRstStream(const QuicRstStreamFrame& frame) {
164 ReliableQuicStream* stream = GetStream(frame.stream_id);
165 if (!stream) {
166 return; // Errors are handled by GetStream.
167 }
[email protected]06ff5152013-08-29 01:03:05168 if (ContainsKey(zombie_streams_, stream->id())) {
169 // If this was a zombie stream then we close it out now.
170 CloseZombieStream(stream->id());
171 // However, since the headers still have not been decompressed, we want to
172 // mark it a prematurely closed so that if we ever receive frames
173 // for this stream we can close the connection.
174 DCHECK(!stream->headers_decompressed());
175 AddPrematurelyClosedStream(frame.stream_id);
176 return;
177 }
[email protected]9db443912013-02-25 05:27:03178 stream->OnStreamReset(frame.error_code);
179}
180
181void QuicSession::OnGoAway(const QuicGoAwayFrame& frame) {
182 DCHECK(frame.last_good_stream_id < next_stream_id_);
183 goaway_received_ = true;
[email protected]9c0b1352012-11-04 00:03:27184}
185
186void QuicSession::ConnectionClose(QuicErrorCode error, bool from_peer) {
[email protected]06ff5152013-08-29 01:03:05187 DCHECK(!connection_->connected());
[email protected]25c31dc2013-06-05 17:56:04188 if (error_ == QUIC_NO_ERROR) {
189 error_ = error;
190 }
191
[email protected]9c0b1352012-11-04 00:03:27192 while (stream_map_.size() != 0) {
193 ReliableStreamMap::iterator it = stream_map_.begin();
194 QuicStreamId id = it->first;
195 it->second->ConnectionClose(error, from_peer);
196 // The stream should call CloseStream as part of ConnectionClose.
197 if (stream_map_.find(id) != stream_map_.end()) {
[email protected]25c31dc2013-06-05 17:56:04198 LOG(DFATAL) << ENDPOINT << "Stream failed to close under ConnectionClose";
[email protected]9c0b1352012-11-04 00:03:27199 CloseStream(id);
200 }
201 }
202}
203
[email protected]a5d4eee22012-12-13 09:09:01204bool QuicSession::OnCanWrite() {
205 // We latch this here rather than doing a traditional loop, because streams
206 // may be modifying the list as we loop.
[email protected]e4696aaa2013-08-20 16:03:55207 int remaining_writes = write_blocked_streams_.NumBlockedStreams();
[email protected]a5d4eee22012-12-13 09:09:01208
[email protected]dda3e9b2012-12-22 21:49:25209 while (!connection_->HasQueuedData() &&
[email protected]a5d4eee22012-12-13 09:09:01210 remaining_writes > 0) {
[email protected]e4696aaa2013-08-20 16:03:55211 DCHECK(write_blocked_streams_.HasWriteBlockedStreams());
[email protected]e4696aaa2013-08-20 16:03:55212 int index = write_blocked_streams_.GetHighestPriorityWriteBlockedList();
[email protected]4d1789c32013-09-18 23:54:36213 if (index == -1) {
214 LOG(DFATAL) << "WriteBlockedStream is missing";
215 connection_->CloseConnection(QUIC_INTERNAL_ERROR, false);
216 return true; // We have no write blocked streams.
[email protected]e4696aaa2013-08-20 16:03:55217 }
[email protected]4d1789c32013-09-18 23:54:36218 QuicStreamId stream_id = write_blocked_streams_.PopFront(index);
219 if (stream_id == kCryptoStreamId) {
220 has_pending_handshake_ = false; // We just popped it.
221 }
222 ReliableQuicStream* stream = GetStream(stream_id);
[email protected]a5d4eee22012-12-13 09:09:01223 if (stream != NULL) {
224 // If the stream can't write all bytes, it'll re-add itself to the blocked
225 // list.
226 stream->OnCanWrite();
227 }
228 --remaining_writes;
229 }
230
[email protected]e4696aaa2013-08-20 16:03:55231 return !write_blocked_streams_.HasWriteBlockedStreams();
[email protected]a5d4eee22012-12-13 09:09:01232}
233
[email protected]4d1789c32013-09-18 23:54:36234bool QuicSession::HasPendingHandshake() const {
235 return has_pending_handshake_;
236}
237
[email protected]24e5bc52013-09-18 15:36:58238QuicConsumedData QuicSession::WritevData(QuicStreamId id,
239 const struct iovec* iov,
[email protected]4d1789c32013-09-18 23:54:36240 int iov_count,
[email protected]24e5bc52013-09-18 15:36:58241 QuicStreamOffset offset,
242 bool fin) {
[email protected]4d1789c32013-09-18 23:54:36243 return connection_->SendvStreamData(id, iov, iov_count, offset, fin);
[email protected]9c0b1352012-11-04 00:03:27244}
245
246void QuicSession::SendRstStream(QuicStreamId id,
[email protected]74bda142013-03-31 02:49:11247 QuicRstStreamErrorCode error) {
[email protected]9db443912013-02-25 05:27:03248 connection_->SendRstStream(id, error);
[email protected]06ff5152013-08-29 01:03:05249 CloseStreamInner(id, true);
[email protected]9c0b1352012-11-04 00:03:27250}
251
[email protected]9db443912013-02-25 05:27:03252void QuicSession::SendGoAway(QuicErrorCode error_code, const string& reason) {
253 goaway_sent_ = true;
254 connection_->SendGoAway(error_code, largest_peer_created_stream_id_, reason);
255}
256
[email protected]9c0b1352012-11-04 00:03:27257void QuicSession::CloseStream(QuicStreamId stream_id) {
[email protected]06ff5152013-08-29 01:03:05258 CloseStreamInner(stream_id, false);
259}
260
261void QuicSession::CloseStreamInner(QuicStreamId stream_id,
262 bool locally_reset) {
[email protected]25c31dc2013-06-05 17:56:04263 DLOG(INFO) << ENDPOINT << "Closing stream " << stream_id;
[email protected]9c0b1352012-11-04 00:03:27264
265 ReliableStreamMap::iterator it = stream_map_.find(stream_id);
266 if (it == stream_map_.end()) {
[email protected]25c31dc2013-06-05 17:56:04267 DLOG(INFO) << ENDPOINT << "Stream is already closed: " << stream_id;
[email protected]9c0b1352012-11-04 00:03:27268 return;
269 }
[email protected]a57e0272013-04-26 07:31:47270 ReliableQuicStream* stream = it->second;
[email protected]06ff5152013-08-29 01:03:05271 if (connection_->connected() && !stream->headers_decompressed()) {
272 // If the stream is being closed locally (for example a client cancelling
273 // a request before receiving the response) then we need to make sure that
274 // we keep the stream alive long enough to process any response or
275 // RST_STREAM frames.
276 if (locally_reset && !is_server_) {
277 AddZombieStream(stream_id);
278 return;
[email protected]3e60db82013-08-05 19:43:06279 }
[email protected]06ff5152013-08-29 01:03:05280
281 // This stream has been closed before the headers were decompressed.
282 // This might cause problems with head of line blocking of headers.
283 // If the peer sent headers which were lost but we now close the stream
284 // we will never be able to decompress headers for other streams.
285 // To deal with this, we keep track of streams which have been closed
286 // prematurely. If we ever receive data frames for this steam, then we
287 // know there actually has been a problem and we close the connection.
288 AddPrematurelyClosedStream(stream->id());
[email protected]3e60db82013-08-05 19:43:06289 }
[email protected]63534512012-12-23 18:49:00290 closed_streams_.push_back(it->second);
[email protected]4ec72432013-09-01 23:17:01291 if (ContainsKey(zombie_streams_, stream->id())) {
292 zombie_streams_.erase(stream->id());
293 }
[email protected]9c0b1352012-11-04 00:03:27294 stream_map_.erase(it);
[email protected]a57e0272013-04-26 07:31:47295 stream->OnClose();
[email protected]9c0b1352012-11-04 00:03:27296}
297
[email protected]06ff5152013-08-29 01:03:05298void QuicSession::AddZombieStream(QuicStreamId stream_id) {
299 if (zombie_streams_.size() == kMaxZombieStreams) {
300 QuicStreamId oldest_zombie_stream_id = zombie_streams_.begin()->first;
301 CloseZombieStream(oldest_zombie_stream_id);
302 // However, since the headers still have not been decompressed, we want to
303 // mark it a prematurely closed so that if we ever receive frames
304 // for this stream we can close the connection.
305 AddPrematurelyClosedStream(oldest_zombie_stream_id);
306 }
307 zombie_streams_.insert(make_pair(stream_id, true));
308}
309
310void QuicSession::CloseZombieStream(QuicStreamId stream_id) {
311 DCHECK(ContainsKey(zombie_streams_, stream_id));
312 zombie_streams_.erase(stream_id);
313 ReliableQuicStream* stream = GetStream(stream_id);
314 if (!stream) {
315 return;
316 }
317 stream_map_.erase(stream_id);
318 stream->OnClose();
319 closed_streams_.push_back(stream);
320}
321
322void QuicSession::AddPrematurelyClosedStream(QuicStreamId stream_id) {
323 if (prematurely_closed_streams_.size() ==
324 kMaxPrematurelyClosedStreamsTracked) {
325 prematurely_closed_streams_.erase(prematurely_closed_streams_.begin());
326 }
327 prematurely_closed_streams_.insert(make_pair(stream_id, true));
328}
329
[email protected]8ba81212013-05-03 13:11:48330bool QuicSession::IsEncryptionEstablished() {
331 return GetCryptoStream()->encryption_established();
[email protected]9c0b1352012-11-04 00:03:27332}
333
[email protected]8ba81212013-05-03 13:11:48334bool QuicSession::IsCryptoHandshakeConfirmed() {
335 return GetCryptoStream()->handshake_confirmed();
336}
337
338void QuicSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
[email protected]1354bf62013-05-23 23:17:18339 switch (event) {
340 // TODO(satyamshekhar): Move the logic of setting the encrypter/decrypter
341 // to QuicSession since it is the glue.
342 case ENCRYPTION_FIRST_ESTABLISHED:
343 break;
344
345 case ENCRYPTION_REESTABLISHED:
346 // Retransmit originally packets that were sent, since they can't be
347 // decrypted by the peer.
348 connection_->RetransmitUnackedPackets(
349 QuicConnection::INITIAL_ENCRYPTION_ONLY);
350 break;
351
352 case HANDSHAKE_CONFIRMED:
[email protected]25c31dc2013-06-05 17:56:04353 LOG_IF(DFATAL, !config_.negotiated()) << ENDPOINT
[email protected]1354bf62013-05-23 23:17:18354 << "Handshake confirmed without parameter negotiation.";
[email protected]b064310782013-05-30 21:12:17355 connection_->SetIdleNetworkTimeout(
[email protected]1354bf62013-05-23 23:17:18356 config_.idle_connection_state_lifetime());
[email protected]b064310782013-05-30 21:12:17357 connection_->SetOverallConnectionTimeout(QuicTime::Delta::Infinite());
[email protected]1354bf62013-05-23 23:17:18358 max_open_streams_ = config_.max_streams_per_connection();
359 break;
360
361 default:
[email protected]25c31dc2013-06-05 17:56:04362 LOG(ERROR) << ENDPOINT << "Got unknown handshake event: " << event;
[email protected]2532de12013-05-09 12:29:33363 }
[email protected]8ee611b2012-11-20 01:48:12364}
365
[email protected]54f5f932013-08-14 19:47:20366void QuicSession::OnCryptoHandshakeMessageSent(
367 const CryptoHandshakeMessage& message) {
368}
369
370void QuicSession::OnCryptoHandshakeMessageReceived(
371 const CryptoHandshakeMessage& message) {
372}
373
[email protected]899951652013-05-16 12:52:39374QuicConfig* QuicSession::config() {
375 return &config_;
376}
377
[email protected]9c0b1352012-11-04 00:03:27378void QuicSession::ActivateStream(ReliableQuicStream* stream) {
[email protected]25c31dc2013-06-05 17:56:04379 DLOG(INFO) << ENDPOINT << "num_streams: " << stream_map_.size()
380 << ". activating " << stream->id();
[email protected]4d1789c32013-09-18 23:54:36381 DCHECK_EQ(stream_map_.count(stream->id()), 0u);
[email protected]9c0b1352012-11-04 00:03:27382 stream_map_[stream->id()] = stream;
383}
384
385QuicStreamId QuicSession::GetNextStreamId() {
386 QuicStreamId id = next_stream_id_;
387 next_stream_id_ += 2;
388 return id;
389}
390
391ReliableQuicStream* QuicSession::GetStream(const QuicStreamId stream_id) {
392 if (stream_id == kCryptoStreamId) {
393 return GetCryptoStream();
394 }
395
396 ReliableStreamMap::iterator it = stream_map_.find(stream_id);
397 if (it != stream_map_.end()) {
398 return it->second;
399 }
400
[email protected]610a7e942012-12-18 00:21:39401 if (IsClosedStream(stream_id)) {
402 return NULL;
403 }
404
[email protected]9c0b1352012-11-04 00:03:27405 if (stream_id % 2 == next_stream_id_ % 2) {
406 // We've received a frame for a locally-created stream that is not
407 // currently active. This is an error.
408 connection()->SendConnectionClose(QUIC_PACKET_FOR_NONEXISTENT_STREAM);
409 return NULL;
410 }
411
412 return GetIncomingReliableStream(stream_id);
413}
414
415ReliableQuicStream* QuicSession::GetIncomingReliableStream(
416 QuicStreamId stream_id) {
417 if (IsClosedStream(stream_id)) {
418 return NULL;
419 }
420
[email protected]9db443912013-02-25 05:27:03421 if (goaway_sent_) {
422 // We've already sent a GoAway
[email protected]74bda142013-03-31 02:49:11423 SendRstStream(stream_id, QUIC_STREAM_PEER_GOING_AWAY);
[email protected]9db443912013-02-25 05:27:03424 return NULL;
425 }
426
[email protected]9c0b1352012-11-04 00:03:27427 implicitly_created_streams_.erase(stream_id);
428 if (stream_id > largest_peer_created_stream_id_) {
429 // TODO(rch) add unit test for this
430 if (stream_id - largest_peer_created_stream_id_ > kMaxStreamIdDelta) {
431 connection()->SendConnectionClose(QUIC_INVALID_STREAM_ID);
432 return NULL;
433 }
[email protected]97cf3022013-09-05 14:30:16434 if (largest_peer_created_stream_id_ == 0) {
435 largest_peer_created_stream_id_= 1;
436 }
437 for (QuicStreamId id = largest_peer_created_stream_id_ + 2;
438 id < stream_id;
439 id += 2) {
440 implicitly_created_streams_.insert(id);
[email protected]9c0b1352012-11-04 00:03:27441 }
442 largest_peer_created_stream_id_ = stream_id;
443 }
444 ReliableQuicStream* stream = CreateIncomingReliableStream(stream_id);
445 if (stream == NULL) {
[email protected]9c0b1352012-11-04 00:03:27446 return NULL;
447 }
448 ActivateStream(stream);
449 return stream;
450}
451
452bool QuicSession::IsClosedStream(QuicStreamId id) {
453 DCHECK_NE(0u, id);
454 if (id == kCryptoStreamId) {
455 return false;
456 }
[email protected]06ff5152013-08-29 01:03:05457 if (ContainsKey(zombie_streams_, id)) {
458 return true;
459 }
460 if (ContainsKey(stream_map_, id)) {
[email protected]9c0b1352012-11-04 00:03:27461 // Stream is active
462 return false;
463 }
464 if (id % 2 == next_stream_id_ % 2) {
[email protected]2ff600a2012-11-11 19:22:19465 // Locally created streams are strictly in-order. If the id is in the
466 // range of created streams and it's not active, it must have been closed.
[email protected]9c0b1352012-11-04 00:03:27467 return id < next_stream_id_;
[email protected]2ff600a2012-11-11 19:22:19468 }
469 // For peer created streams, we also need to consider implicitly created
470 // streams.
[email protected]044ac2b2012-11-13 21:41:06471 return id <= largest_peer_created_stream_id_ &&
472 implicitly_created_streams_.count(id) == 0;
473}
[email protected]9c0b1352012-11-04 00:03:27474
[email protected]c5b061b2013-01-05 00:31:34475size_t QuicSession::GetNumOpenStreams() const {
[email protected]06ff5152013-08-29 01:03:05476 return stream_map_.size() + implicitly_created_streams_.size() -
477 zombie_streams_.size();
[email protected]9c0b1352012-11-04 00:03:27478}
479
[email protected]24e5bc52013-09-18 15:36:58480void QuicSession::MarkWriteBlocked(QuicStreamId id, QuicPriority priority) {
[email protected]4d1789c32013-09-18 23:54:36481 if (id == kCryptoStreamId) {
482 DCHECK(!has_pending_handshake_);
483 has_pending_handshake_ = true;
484 // TODO(jar): Be sure to use the highest priority for the crypto stream,
485 // perhaps by adding a "special" priority for it that is higher than
486 // kHighestPriority.
487 priority = kHighestPriority;
488 }
[email protected]24e5bc52013-09-18 15:36:58489 write_blocked_streams_.PushBack(id, priority);
[email protected]a5d4eee22012-12-13 09:09:01490}
491
[email protected]c244c5a12013-05-07 20:55:04492void QuicSession::MarkDecompressionBlocked(QuicHeaderId header_id,
493 QuicStreamId stream_id) {
494 decompression_blocked_streams_[header_id] = stream_id;
495}
496
[email protected]a69af0522013-07-12 19:23:47497bool QuicSession::GetSSLInfo(SSLInfo* ssl_info) {
498 NOTIMPLEMENTED();
499 return false;
500}
501
[email protected]5ffa5622012-12-23 03:07:25502void QuicSession::PostProcessAfterData() {
503 STLDeleteElements(&closed_streams_);
504 closed_streams_.clear();
505}
506
[email protected]9c0b1352012-11-04 00:03:27507} // namespace net