| OLD | NEW |
| (Empty) |
| 1 // 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 | |
| 7 #include "base/stl_util.h" | |
| 8 #include "base/strings/string_number_conversions.h" | |
| 9 #include "base/strings/stringprintf.h" | |
| 10 #include "net/quic/crypto/proof_verifier.h" | |
| 11 #include "net/quic/quic_bug_tracker.h" | |
| 12 #include "net/quic/quic_connection.h" | |
| 13 #include "net/quic/quic_flags.h" | |
| 14 #include "net/quic/quic_flow_controller.h" | |
| 15 #include "net/ssl/ssl_info.h" | |
| 16 | |
| 17 using base::IntToString; | |
| 18 using base::StringPiece; | |
| 19 using std::make_pair; | |
| 20 using std::map; | |
| 21 using std::max; | |
| 22 using std::string; | |
| 23 using std::vector; | |
| 24 using net::SpdyPriority; | |
| 25 | |
| 26 namespace net { | |
| 27 | |
| 28 #define ENDPOINT \ | |
| 29 (perspective() == Perspective::IS_SERVER ? "Server: " : " Client: ") | |
| 30 | |
| 31 QuicSession::QuicSession(QuicConnection* connection, const QuicConfig& config) | |
| 32 : connection_(connection), | |
| 33 config_(config), | |
| 34 max_open_outgoing_streams_(kDefaultMaxStreamsPerConnection), | |
| 35 max_open_incoming_streams_(config_.GetMaxIncomingDynamicStreamsToSend()), | |
| 36 next_outgoing_stream_id_(perspective() == Perspective::IS_SERVER ? 2 : 3), | |
| 37 largest_peer_created_stream_id_( | |
| 38 perspective() == Perspective::IS_SERVER ? 1 : 0), | |
| 39 num_dynamic_incoming_streams_(0), | |
| 40 num_draining_incoming_streams_(0), | |
| 41 num_locally_closed_incoming_streams_highest_offset_(0), | |
| 42 error_(QUIC_NO_ERROR), | |
| 43 flow_controller_(connection_.get(), | |
| 44 0, | |
| 45 perspective(), | |
| 46 kMinimumFlowControlSendWindow, | |
| 47 config_.GetInitialSessionFlowControlWindowToSend(), | |
| 48 perspective() == Perspective::IS_SERVER), | |
| 49 currently_writing_stream_id_(0) {} | |
| 50 | |
| 51 void QuicSession::Initialize() { | |
| 52 connection_->set_visitor(this); | |
| 53 connection_->SetFromConfig(config_); | |
| 54 | |
| 55 DCHECK_EQ(kCryptoStreamId, GetCryptoStream()->id()); | |
| 56 static_stream_map_[kCryptoStreamId] = GetCryptoStream(); | |
| 57 } | |
| 58 | |
| 59 QuicSession::~QuicSession() { | |
| 60 STLDeleteElements(&closed_streams_); | |
| 61 STLDeleteValues(&dynamic_stream_map_); | |
| 62 | |
| 63 DLOG_IF(WARNING, num_locally_closed_incoming_streams_highest_offset() > | |
| 64 max_open_incoming_streams_) | |
| 65 << "Surprisingly high number of locally closed peer initiated streams" | |
| 66 "still waiting for final byte offset: " | |
| 67 << num_locally_closed_incoming_streams_highest_offset(); | |
| 68 DLOG_IF(WARNING, GetNumLocallyClosedOutgoingStreamsHighestOffset() > | |
| 69 max_open_outgoing_streams_) | |
| 70 << "Surprisingly high number of locally closed self initiated streams" | |
| 71 "still waiting for final byte offset: " | |
| 72 << GetNumLocallyClosedOutgoingStreamsHighestOffset(); | |
| 73 } | |
| 74 | |
| 75 void QuicSession::OnStreamFrame(const QuicStreamFrame& frame) { | |
| 76 // TODO(rch) deal with the error case of stream id 0. | |
| 77 QuicStreamId stream_id = frame.stream_id; | |
| 78 ReliableQuicStream* stream = GetOrCreateStream(stream_id); | |
| 79 if (!stream) { | |
| 80 // The stream no longer exists, but we may still be interested in the | |
| 81 // final stream byte offset sent by the peer. A frame with a FIN can give | |
| 82 // us this offset. | |
| 83 if (frame.fin) { | |
| 84 QuicStreamOffset final_byte_offset = frame.offset + frame.data_length; | |
| 85 UpdateFlowControlOnFinalReceivedByteOffset(stream_id, final_byte_offset); | |
| 86 } | |
| 87 return; | |
| 88 } | |
| 89 stream->OnStreamFrame(frame); | |
| 90 } | |
| 91 | |
| 92 void QuicSession::OnRstStream(const QuicRstStreamFrame& frame) { | |
| 93 if (ContainsKey(static_stream_map_, frame.stream_id)) { | |
| 94 connection()->CloseConnection( | |
| 95 QUIC_INVALID_STREAM_ID, "Attempt to reset a static stream", | |
| 96 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | |
| 97 return; | |
| 98 } | |
| 99 | |
| 100 ReliableQuicStream* stream = GetOrCreateDynamicStream(frame.stream_id); | |
| 101 if (!stream) { | |
| 102 HandleRstOnValidNonexistentStream(frame); | |
| 103 return; // Errors are handled by GetOrCreateStream. | |
| 104 } | |
| 105 | |
| 106 stream->OnStreamReset(frame); | |
| 107 } | |
| 108 | |
| 109 void QuicSession::OnGoAway(const QuicGoAwayFrame& frame) { | |
| 110 DCHECK(frame.last_good_stream_id < next_outgoing_stream_id_); | |
| 111 } | |
| 112 | |
| 113 void QuicSession::OnConnectionClosed(QuicErrorCode error, | |
| 114 const string& /*error_details*/, | |
| 115 ConnectionCloseSource source) { | |
| 116 DCHECK(!connection_->connected()); | |
| 117 if (error_ == QUIC_NO_ERROR) { | |
| 118 error_ = error; | |
| 119 } | |
| 120 | |
| 121 while (!dynamic_stream_map_.empty()) { | |
| 122 DynamicStreamMap::iterator it = dynamic_stream_map_.begin(); | |
| 123 QuicStreamId id = it->first; | |
| 124 it->second->OnConnectionClosed(error, source); | |
| 125 // The stream should call CloseStream as part of OnConnectionClosed. | |
| 126 if (dynamic_stream_map_.find(id) != dynamic_stream_map_.end()) { | |
| 127 QUIC_BUG << ENDPOINT << "Stream failed to close under OnConnectionClosed"; | |
| 128 CloseStream(id); | |
| 129 } | |
| 130 } | |
| 131 } | |
| 132 | |
| 133 void QuicSession::OnSuccessfulVersionNegotiation( | |
| 134 const QuicVersion& /*version*/) {} | |
| 135 | |
| 136 void QuicSession::OnPathDegrading() {} | |
| 137 | |
| 138 void QuicSession::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) { | |
| 139 // Stream may be closed by the time we receive a WINDOW_UPDATE, so we can't | |
| 140 // assume that it still exists. | |
| 141 QuicStreamId stream_id = frame.stream_id; | |
| 142 if (stream_id == kConnectionLevelId) { | |
| 143 // This is a window update that applies to the connection, rather than an | |
| 144 // individual stream. | |
| 145 DVLOG(1) << ENDPOINT << "Received connection level flow control window " | |
| 146 "update with byte offset: " | |
| 147 << frame.byte_offset; | |
| 148 flow_controller_.UpdateSendWindowOffset(frame.byte_offset); | |
| 149 return; | |
| 150 } | |
| 151 ReliableQuicStream* stream = GetOrCreateStream(stream_id); | |
| 152 if (stream) { | |
| 153 stream->OnWindowUpdateFrame(frame); | |
| 154 } | |
| 155 } | |
| 156 | |
| 157 void QuicSession::OnBlockedFrame(const QuicBlockedFrame& frame) { | |
| 158 // TODO(rjshade): Compare our flow control receive windows for specified | |
| 159 // streams: if we have a large window then maybe something | |
| 160 // had gone wrong with the flow control accounting. | |
| 161 DVLOG(1) << ENDPOINT | |
| 162 << "Received BLOCKED frame with stream id: " << frame.stream_id; | |
| 163 } | |
| 164 | |
| 165 void QuicSession::OnCanWrite() { | |
| 166 // We limit the number of writes to the number of pending streams. If more | |
| 167 // streams become pending, WillingAndAbleToWrite will be true, which will | |
| 168 // cause the connection to request resumption before yielding to other | |
| 169 // connections. | |
| 170 size_t num_writes = write_blocked_streams_.NumBlockedStreams(); | |
| 171 if (flow_controller_.IsBlocked()) { | |
| 172 // If we are connection level flow control blocked, then only allow the | |
| 173 // crypto and headers streams to try writing as all other streams will be | |
| 174 // blocked. | |
| 175 num_writes = 0; | |
| 176 if (write_blocked_streams_.crypto_stream_blocked()) { | |
| 177 num_writes += 1; | |
| 178 } | |
| 179 if (write_blocked_streams_.headers_stream_blocked()) { | |
| 180 num_writes += 1; | |
| 181 } | |
| 182 } | |
| 183 if (num_writes == 0) { | |
| 184 return; | |
| 185 } | |
| 186 | |
| 187 QuicConnection::ScopedPacketBundler ack_bundler( | |
| 188 connection_.get(), QuicConnection::SEND_ACK_IF_QUEUED); | |
| 189 for (size_t i = 0; i < num_writes; ++i) { | |
| 190 if (!(write_blocked_streams_.HasWriteBlockedCryptoOrHeadersStream() || | |
| 191 write_blocked_streams_.HasWriteBlockedDataStreams())) { | |
| 192 // Writing one stream removed another!? Something's broken. | |
| 193 QUIC_BUG << "WriteBlockedStream is missing"; | |
| 194 connection_->CloseConnection(QUIC_INTERNAL_ERROR, | |
| 195 "WriteBlockedStream is missing", | |
| 196 ConnectionCloseBehavior::SILENT_CLOSE); | |
| 197 return; | |
| 198 } | |
| 199 if (!connection_->CanWriteStreamData()) { | |
| 200 return; | |
| 201 } | |
| 202 currently_writing_stream_id_ = write_blocked_streams_.PopFront(); | |
| 203 ReliableQuicStream* stream = | |
| 204 GetOrCreateStream(currently_writing_stream_id_); | |
| 205 if (stream != nullptr && !stream->flow_controller()->IsBlocked()) { | |
| 206 // If the stream can't write all bytes it'll re-add itself to the blocked | |
| 207 // list. | |
| 208 stream->OnCanWrite(); | |
| 209 } | |
| 210 currently_writing_stream_id_ = 0; | |
| 211 } | |
| 212 } | |
| 213 | |
| 214 bool QuicSession::WillingAndAbleToWrite() const { | |
| 215 // If the crypto or headers streams are blocked, we want to schedule a write - | |
| 216 // they don't get blocked by connection level flow control. Otherwise only | |
| 217 // schedule a write if we are not flow control blocked at the connection | |
| 218 // level. | |
| 219 return write_blocked_streams_.HasWriteBlockedCryptoOrHeadersStream() || | |
| 220 (!flow_controller_.IsBlocked() && | |
| 221 write_blocked_streams_.HasWriteBlockedDataStreams()); | |
| 222 } | |
| 223 | |
| 224 bool QuicSession::HasPendingHandshake() const { | |
| 225 return write_blocked_streams_.crypto_stream_blocked(); | |
| 226 } | |
| 227 | |
| 228 bool QuicSession::HasOpenDynamicStreams() const { | |
| 229 return (dynamic_stream_map_.size() - draining_streams_.size() + | |
| 230 locally_closed_streams_highest_offset_.size()) > 0; | |
| 231 } | |
| 232 | |
| 233 void QuicSession::ProcessUdpPacket(const IPEndPoint& self_address, | |
| 234 const IPEndPoint& peer_address, | |
| 235 const QuicReceivedPacket& packet) { | |
| 236 connection_->ProcessUdpPacket(self_address, peer_address, packet); | |
| 237 } | |
| 238 | |
| 239 QuicConsumedData QuicSession::WritevData( | |
| 240 ReliableQuicStream* stream, | |
| 241 QuicStreamId id, | |
| 242 QuicIOVector iov, | |
| 243 QuicStreamOffset offset, | |
| 244 bool fin, | |
| 245 QuicAckListenerInterface* ack_notifier_delegate) { | |
| 246 // This check is an attempt to deal with potential memory corruption | |
| 247 // in which |id| ends up set to 1 (the crypto stream id). If this happen | |
| 248 // it might end up resulting in unencrypted stream data being sent. | |
| 249 // While this is impossible to avoid given sufficient corruption, this | |
| 250 // seems like a reasonable mitigation. | |
| 251 if (id == kCryptoStreamId && stream != GetCryptoStream()) { | |
| 252 QUIC_BUG << "Stream id mismatch"; | |
| 253 connection_->CloseConnection( | |
| 254 QUIC_INTERNAL_ERROR, | |
| 255 "Non-crypto stream attempted to write data as crypto stream.", | |
| 256 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | |
| 257 return QuicConsumedData(0, false); | |
| 258 } | |
| 259 if (!IsEncryptionEstablished() && id != kCryptoStreamId) { | |
| 260 // Do not let streams write without encryption. The calling stream will end | |
| 261 // up write blocked until OnCanWrite is next called. | |
| 262 return QuicConsumedData(0, false); | |
| 263 } | |
| 264 QuicConsumedData data = | |
| 265 connection_->SendStreamData(id, iov, offset, fin, ack_notifier_delegate); | |
| 266 write_blocked_streams_.UpdateBytesForStream(id, data.bytes_consumed); | |
| 267 return data; | |
| 268 } | |
| 269 | |
| 270 void QuicSession::SendRstStream(QuicStreamId id, | |
| 271 QuicRstStreamErrorCode error, | |
| 272 QuicStreamOffset bytes_written) { | |
| 273 if (ContainsKey(static_stream_map_, id)) { | |
| 274 QUIC_BUG << "Cannot send RST for a static stream with ID " << id; | |
| 275 return; | |
| 276 } | |
| 277 | |
| 278 if (connection()->connected()) { | |
| 279 // Only send a RST_STREAM frame if still connected. | |
| 280 connection_->SendRstStream(id, error, bytes_written); | |
| 281 } | |
| 282 CloseStreamInner(id, true); | |
| 283 } | |
| 284 | |
| 285 void QuicSession::SendGoAway(QuicErrorCode error_code, const string& reason) { | |
| 286 if (goaway_sent()) { | |
| 287 return; | |
| 288 } | |
| 289 | |
| 290 connection_->SendGoAway(error_code, largest_peer_created_stream_id_, reason); | |
| 291 } | |
| 292 | |
| 293 void QuicSession::CloseStream(QuicStreamId stream_id) { | |
| 294 CloseStreamInner(stream_id, false); | |
| 295 } | |
| 296 | |
| 297 void QuicSession::InsertLocallyClosedStreamsHighestOffset( | |
| 298 const QuicStreamId id, | |
| 299 QuicStreamOffset offset) { | |
| 300 locally_closed_streams_highest_offset_[id] = offset; | |
| 301 if (IsIncomingStream(id)) { | |
| 302 ++num_locally_closed_incoming_streams_highest_offset_; | |
| 303 } | |
| 304 } | |
| 305 | |
| 306 void QuicSession::CloseStreamInner(QuicStreamId stream_id, bool locally_reset) { | |
| 307 DVLOG(1) << ENDPOINT << "Closing stream " << stream_id; | |
| 308 | |
| 309 DynamicStreamMap::iterator it = dynamic_stream_map_.find(stream_id); | |
| 310 if (it == dynamic_stream_map_.end()) { | |
| 311 // When CloseStreamInner has been called recursively (via | |
| 312 // ReliableQuicStream::OnClose), the stream will already have been deleted | |
| 313 // from stream_map_, so return immediately. | |
| 314 DVLOG(1) << ENDPOINT << "Stream is already closed: " << stream_id; | |
| 315 return; | |
| 316 } | |
| 317 ReliableQuicStream* stream = it->second; | |
| 318 | |
| 319 // Tell the stream that a RST has been sent. | |
| 320 if (locally_reset) { | |
| 321 stream->set_rst_sent(true); | |
| 322 } | |
| 323 | |
| 324 closed_streams_.push_back(it->second); | |
| 325 | |
| 326 // If we haven't received a FIN or RST for this stream, we need to keep track | |
| 327 // of the how many bytes the stream's flow controller believes it has | |
| 328 // received, for accurate connection level flow control accounting. | |
| 329 if (!stream->HasFinalReceivedByteOffset()) { | |
| 330 InsertLocallyClosedStreamsHighestOffset( | |
| 331 stream_id, stream->flow_controller()->highest_received_byte_offset()); | |
| 332 } | |
| 333 | |
| 334 dynamic_stream_map_.erase(it); | |
| 335 if (IsIncomingStream(stream_id)) { | |
| 336 --num_dynamic_incoming_streams_; | |
| 337 } | |
| 338 | |
| 339 if (draining_streams_.find(stream_id) != draining_streams_.end() && | |
| 340 IsIncomingStream(stream_id)) { | |
| 341 --num_draining_incoming_streams_; | |
| 342 } | |
| 343 draining_streams_.erase(stream_id); | |
| 344 | |
| 345 stream->OnClose(); | |
| 346 // Decrease the number of streams being emulated when a new one is opened. | |
| 347 connection_->SetNumOpenStreams(dynamic_stream_map_.size()); | |
| 348 } | |
| 349 | |
| 350 void QuicSession::UpdateFlowControlOnFinalReceivedByteOffset( | |
| 351 QuicStreamId stream_id, | |
| 352 QuicStreamOffset final_byte_offset) { | |
| 353 map<QuicStreamId, QuicStreamOffset>::iterator it = | |
| 354 locally_closed_streams_highest_offset_.find(stream_id); | |
| 355 if (it == locally_closed_streams_highest_offset_.end()) { | |
| 356 return; | |
| 357 } | |
| 358 | |
| 359 DVLOG(1) << ENDPOINT << "Received final byte offset " << final_byte_offset | |
| 360 << " for stream " << stream_id; | |
| 361 QuicByteCount offset_diff = final_byte_offset - it->second; | |
| 362 if (flow_controller_.UpdateHighestReceivedOffset( | |
| 363 flow_controller_.highest_received_byte_offset() + offset_diff)) { | |
| 364 // If the final offset violates flow control, close the connection now. | |
| 365 if (flow_controller_.FlowControlViolation()) { | |
| 366 connection_->CloseConnection( | |
| 367 QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, | |
| 368 "Connection level flow control violation", | |
| 369 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | |
| 370 return; | |
| 371 } | |
| 372 } | |
| 373 | |
| 374 flow_controller_.AddBytesConsumed(offset_diff); | |
| 375 locally_closed_streams_highest_offset_.erase(it); | |
| 376 if (IsIncomingStream(stream_id)) { | |
| 377 --num_locally_closed_incoming_streams_highest_offset_; | |
| 378 } | |
| 379 } | |
| 380 | |
| 381 bool QuicSession::IsEncryptionEstablished() { | |
| 382 return GetCryptoStream()->encryption_established(); | |
| 383 } | |
| 384 | |
| 385 bool QuicSession::IsCryptoHandshakeConfirmed() { | |
| 386 return GetCryptoStream()->handshake_confirmed(); | |
| 387 } | |
| 388 | |
| 389 void QuicSession::OnConfigNegotiated() { | |
| 390 connection_->SetFromConfig(config_); | |
| 391 | |
| 392 const QuicVersion version = connection()->version(); | |
| 393 uint32_t max_streams = 0; | |
| 394 if (version > QUIC_VERSION_34 && | |
| 395 config_.HasReceivedMaxIncomingDynamicStreams()) { | |
| 396 max_streams = config_.ReceivedMaxIncomingDynamicStreams(); | |
| 397 } else { | |
| 398 max_streams = config_.MaxStreamsPerConnection(); | |
| 399 } | |
| 400 set_max_open_outgoing_streams(max_streams); | |
| 401 | |
| 402 if (version <= QUIC_VERSION_34) { | |
| 403 // A small number of additional incoming streams beyond the limit should be | |
| 404 // allowed. This helps avoid early connection termination when FIN/RSTs for | |
| 405 // old streams are lost or arrive out of order. | |
| 406 // Use a minimum number of additional streams, or a percentage increase, | |
| 407 // whichever is larger. | |
| 408 uint32_t max_incoming_streams = | |
| 409 max(max_streams + kMaxStreamsMinimumIncrement, | |
| 410 static_cast<uint32_t>(max_streams * kMaxStreamsMultiplier)); | |
| 411 set_max_open_incoming_streams(max_incoming_streams); | |
| 412 } else { | |
| 413 uint32_t max_incoming_streams_to_send = | |
| 414 config_.GetMaxIncomingDynamicStreamsToSend(); | |
| 415 uint32_t max_incoming_streams = | |
| 416 max(max_incoming_streams_to_send + kMaxStreamsMinimumIncrement, | |
| 417 static_cast<uint32_t>(max_incoming_streams_to_send * | |
| 418 kMaxStreamsMultiplier)); | |
| 419 set_max_open_incoming_streams(max_incoming_streams); | |
| 420 } | |
| 421 | |
| 422 if (config_.HasReceivedInitialStreamFlowControlWindowBytes()) { | |
| 423 // Streams which were created before the SHLO was received (0-RTT | |
| 424 // requests) are now informed of the peer's initial flow control window. | |
| 425 OnNewStreamFlowControlWindow( | |
| 426 config_.ReceivedInitialStreamFlowControlWindowBytes()); | |
| 427 } | |
| 428 if (config_.HasReceivedInitialSessionFlowControlWindowBytes()) { | |
| 429 OnNewSessionFlowControlWindow( | |
| 430 config_.ReceivedInitialSessionFlowControlWindowBytes()); | |
| 431 } | |
| 432 } | |
| 433 | |
| 434 void QuicSession::HandleFrameOnNonexistentOutgoingStream( | |
| 435 QuicStreamId stream_id) { | |
| 436 DCHECK(!IsClosedStream(stream_id)); | |
| 437 // Received a frame for a locally-created stream that is not currently | |
| 438 // active. This is an error. | |
| 439 connection()->CloseConnection( | |
| 440 QUIC_INVALID_STREAM_ID, "Data for nonexistent stream", | |
| 441 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | |
| 442 } | |
| 443 | |
| 444 void QuicSession::HandleRstOnValidNonexistentStream( | |
| 445 const QuicRstStreamFrame& frame) { | |
| 446 // If the stream is neither originally in active streams nor created in | |
| 447 // GetOrCreateDynamicStream(), it could be a closed stream in which case its | |
| 448 // final received byte offset need to be updated. | |
| 449 if (IsClosedStream(frame.stream_id)) { | |
| 450 // The RST frame contains the final byte offset for the stream: we can now | |
| 451 // update the connection level flow controller if needed. | |
| 452 UpdateFlowControlOnFinalReceivedByteOffset(frame.stream_id, | |
| 453 frame.byte_offset); | |
| 454 } | |
| 455 } | |
| 456 | |
| 457 void QuicSession::OnNewStreamFlowControlWindow(QuicStreamOffset new_window) { | |
| 458 if (new_window < kMinimumFlowControlSendWindow) { | |
| 459 LOG(ERROR) << "Peer sent us an invalid stream flow control send window: " | |
| 460 << new_window | |
| 461 << ", below default: " << kMinimumFlowControlSendWindow; | |
| 462 if (connection_->connected()) { | |
| 463 connection_->CloseConnection( | |
| 464 QUIC_FLOW_CONTROL_INVALID_WINDOW, "New stream window too low", | |
| 465 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | |
| 466 } | |
| 467 return; | |
| 468 } | |
| 469 | |
| 470 // Inform all existing streams about the new window. | |
| 471 for (auto const& kv : static_stream_map_) { | |
| 472 kv.second->UpdateSendWindowOffset(new_window); | |
| 473 } | |
| 474 for (auto const& kv : dynamic_stream_map_) { | |
| 475 kv.second->UpdateSendWindowOffset(new_window); | |
| 476 } | |
| 477 } | |
| 478 | |
| 479 void QuicSession::OnNewSessionFlowControlWindow(QuicStreamOffset new_window) { | |
| 480 if (new_window < kMinimumFlowControlSendWindow) { | |
| 481 LOG(ERROR) << "Peer sent us an invalid session flow control send window: " | |
| 482 << new_window | |
| 483 << ", below default: " << kMinimumFlowControlSendWindow; | |
| 484 if (connection_->connected()) { | |
| 485 connection_->CloseConnection( | |
| 486 QUIC_FLOW_CONTROL_INVALID_WINDOW, "New connection window too low", | |
| 487 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | |
| 488 } | |
| 489 return; | |
| 490 } | |
| 491 | |
| 492 flow_controller_.UpdateSendWindowOffset(new_window); | |
| 493 } | |
| 494 | |
| 495 void QuicSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { | |
| 496 switch (event) { | |
| 497 // TODO(satyamshekhar): Move the logic of setting the encrypter/decrypter | |
| 498 // to QuicSession since it is the glue. | |
| 499 case ENCRYPTION_FIRST_ESTABLISHED: | |
| 500 // Given any streams blocked by encryption a chance to write. | |
| 501 OnCanWrite(); | |
| 502 break; | |
| 503 | |
| 504 case ENCRYPTION_REESTABLISHED: | |
| 505 // Retransmit originally packets that were sent, since they can't be | |
| 506 // decrypted by the peer. | |
| 507 connection_->RetransmitUnackedPackets(ALL_INITIAL_RETRANSMISSION); | |
| 508 // Given any streams blocked by encryption a chance to write. | |
| 509 OnCanWrite(); | |
| 510 break; | |
| 511 | |
| 512 case HANDSHAKE_CONFIRMED: | |
| 513 QUIC_BUG_IF(!config_.negotiated()) | |
| 514 << ENDPOINT << "Handshake confirmed without parameter negotiation."; | |
| 515 // Discard originally encrypted packets, since they can't be decrypted by | |
| 516 // the peer. | |
| 517 connection_->NeuterUnencryptedPackets(); | |
| 518 break; | |
| 519 | |
| 520 default: | |
| 521 LOG(ERROR) << ENDPOINT << "Got unknown handshake event: " << event; | |
| 522 } | |
| 523 } | |
| 524 | |
| 525 void QuicSession::OnCryptoHandshakeMessageSent( | |
| 526 const CryptoHandshakeMessage& /*message*/) {} | |
| 527 | |
| 528 void QuicSession::OnCryptoHandshakeMessageReceived( | |
| 529 const CryptoHandshakeMessage& /*message*/) {} | |
| 530 | |
| 531 QuicConfig* QuicSession::config() { | |
| 532 return &config_; | |
| 533 } | |
| 534 | |
| 535 void QuicSession::ActivateStream(ReliableQuicStream* stream) { | |
| 536 DVLOG(1) << ENDPOINT << "num_streams: " << dynamic_stream_map_.size() | |
| 537 << ". activating " << stream->id(); | |
| 538 DCHECK(!ContainsKey(dynamic_stream_map_, stream->id())); | |
| 539 DCHECK(!ContainsKey(static_stream_map_, stream->id())); | |
| 540 dynamic_stream_map_[stream->id()] = stream; | |
| 541 if (IsIncomingStream(stream->id())) { | |
| 542 ++num_dynamic_incoming_streams_; | |
| 543 } | |
| 544 // Increase the number of streams being emulated when a new one is opened. | |
| 545 connection_->SetNumOpenStreams(dynamic_stream_map_.size()); | |
| 546 } | |
| 547 | |
| 548 QuicStreamId QuicSession::GetNextOutgoingStreamId() { | |
| 549 QuicStreamId id = next_outgoing_stream_id_; | |
| 550 next_outgoing_stream_id_ += 2; | |
| 551 return id; | |
| 552 } | |
| 553 | |
| 554 ReliableQuicStream* QuicSession::GetOrCreateStream( | |
| 555 const QuicStreamId stream_id) { | |
| 556 StaticStreamMap::iterator it = static_stream_map_.find(stream_id); | |
| 557 if (it != static_stream_map_.end()) { | |
| 558 return it->second; | |
| 559 } | |
| 560 return GetOrCreateDynamicStream(stream_id); | |
| 561 } | |
| 562 | |
| 563 void QuicSession::StreamDraining(QuicStreamId stream_id) { | |
| 564 DCHECK(ContainsKey(dynamic_stream_map_, stream_id)); | |
| 565 if (!ContainsKey(draining_streams_, stream_id)) { | |
| 566 draining_streams_.insert(stream_id); | |
| 567 if (IsIncomingStream(stream_id)) { | |
| 568 ++num_draining_incoming_streams_; | |
| 569 } | |
| 570 } | |
| 571 } | |
| 572 | |
| 573 bool QuicSession::MaybeIncreaseLargestPeerStreamId( | |
| 574 const QuicStreamId stream_id) { | |
| 575 if (stream_id <= largest_peer_created_stream_id_) { | |
| 576 return true; | |
| 577 } | |
| 578 | |
| 579 // Check if the new number of available streams would cause the number of | |
| 580 // available streams to exceed the limit. Note that the peer can create | |
| 581 // only alternately-numbered streams. | |
| 582 size_t additional_available_streams = | |
| 583 (stream_id - largest_peer_created_stream_id_) / 2 - 1; | |
| 584 size_t new_num_available_streams = | |
| 585 GetNumAvailableStreams() + additional_available_streams; | |
| 586 if (new_num_available_streams > MaxAvailableStreams()) { | |
| 587 DVLOG(1) << "Failed to create a new incoming stream with id:" << stream_id | |
| 588 << ". There are already " << GetNumAvailableStreams() | |
| 589 << " streams available, which would become " | |
| 590 << new_num_available_streams << ", which exceeds the limit " | |
| 591 << MaxAvailableStreams() << "."; | |
| 592 string details = IntToString(new_num_available_streams) + " above " + | |
| 593 IntToString(MaxAvailableStreams()); | |
| 594 connection()->CloseConnection( | |
| 595 QUIC_TOO_MANY_AVAILABLE_STREAMS, details.c_str(), | |
| 596 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | |
| 597 return false; | |
| 598 } | |
| 599 for (QuicStreamId id = largest_peer_created_stream_id_ + 2; id < stream_id; | |
| 600 id += 2) { | |
| 601 available_streams_.insert(id); | |
| 602 } | |
| 603 largest_peer_created_stream_id_ = stream_id; | |
| 604 | |
| 605 return true; | |
| 606 } | |
| 607 | |
| 608 bool QuicSession::ShouldYield(QuicStreamId stream_id) { | |
| 609 if (stream_id == currently_writing_stream_id_) { | |
| 610 return false; | |
| 611 } | |
| 612 return write_blocked_streams()->ShouldYield(stream_id); | |
| 613 } | |
| 614 | |
| 615 ReliableQuicStream* QuicSession::GetOrCreateDynamicStream( | |
| 616 const QuicStreamId stream_id) { | |
| 617 DCHECK(!ContainsKey(static_stream_map_, stream_id)) | |
| 618 << "Attempt to call GetOrCreateDynamicStream for a static stream"; | |
| 619 | |
| 620 DynamicStreamMap::iterator it = dynamic_stream_map_.find(stream_id); | |
| 621 if (it != dynamic_stream_map_.end()) { | |
| 622 return it->second; | |
| 623 } | |
| 624 | |
| 625 if (IsClosedStream(stream_id)) { | |
| 626 return nullptr; | |
| 627 } | |
| 628 | |
| 629 if (!IsIncomingStream(stream_id)) { | |
| 630 HandleFrameOnNonexistentOutgoingStream(stream_id); | |
| 631 return nullptr; | |
| 632 } | |
| 633 | |
| 634 available_streams_.erase(stream_id); | |
| 635 | |
| 636 if (!MaybeIncreaseLargestPeerStreamId(stream_id)) { | |
| 637 return nullptr; | |
| 638 } | |
| 639 // Check if the new number of open streams would cause the number of | |
| 640 // open streams to exceed the limit. | |
| 641 if (GetNumOpenIncomingStreams() >= max_open_incoming_streams()) { | |
| 642 // Refuse to open the stream. | |
| 643 SendRstStream(stream_id, QUIC_REFUSED_STREAM, 0); | |
| 644 return nullptr; | |
| 645 } | |
| 646 | |
| 647 return CreateIncomingDynamicStream(stream_id); | |
| 648 } | |
| 649 | |
| 650 void QuicSession::set_max_open_incoming_streams( | |
| 651 size_t max_open_incoming_streams) { | |
| 652 DVLOG(1) << "Setting max_open_incoming_streams_ to " | |
| 653 << max_open_incoming_streams; | |
| 654 max_open_incoming_streams_ = max_open_incoming_streams; | |
| 655 DVLOG(1) << "MaxAvailableStreams() became " << MaxAvailableStreams(); | |
| 656 } | |
| 657 | |
| 658 void QuicSession::set_max_open_outgoing_streams( | |
| 659 size_t max_open_outgoing_streams) { | |
| 660 DVLOG(1) << "Setting max_open_outgoing_streams_ to " | |
| 661 << max_open_outgoing_streams; | |
| 662 max_open_outgoing_streams_ = max_open_outgoing_streams; | |
| 663 } | |
| 664 | |
| 665 bool QuicSession::goaway_sent() const { | |
| 666 return connection_->goaway_sent(); | |
| 667 } | |
| 668 | |
| 669 bool QuicSession::goaway_received() const { | |
| 670 return connection_->goaway_received(); | |
| 671 } | |
| 672 | |
| 673 bool QuicSession::IsClosedStream(QuicStreamId id) { | |
| 674 DCHECK_NE(0u, id); | |
| 675 if (IsOpenStream(id)) { | |
| 676 // Stream is active | |
| 677 return false; | |
| 678 } | |
| 679 if (!IsIncomingStream(id)) { | |
| 680 // Locally created streams are strictly in-order. If the id is in the | |
| 681 // range of created streams and it's not active, it must have been closed. | |
| 682 return id < next_outgoing_stream_id_; | |
| 683 } | |
| 684 // For peer created streams, we also need to consider available streams. | |
| 685 return id <= largest_peer_created_stream_id_ && | |
| 686 !ContainsKey(available_streams_, id); | |
| 687 } | |
| 688 | |
| 689 bool QuicSession::IsOpenStream(QuicStreamId id) { | |
| 690 DCHECK_NE(0u, id); | |
| 691 if (ContainsKey(static_stream_map_, id) || | |
| 692 ContainsKey(dynamic_stream_map_, id)) { | |
| 693 // Stream is active | |
| 694 return true; | |
| 695 } | |
| 696 return false; | |
| 697 } | |
| 698 | |
| 699 size_t QuicSession::GetNumOpenIncomingStreams() const { | |
| 700 return num_dynamic_incoming_streams_ - num_draining_incoming_streams_ + | |
| 701 num_locally_closed_incoming_streams_highest_offset_; | |
| 702 } | |
| 703 | |
| 704 size_t QuicSession::GetNumOpenOutgoingStreams() const { | |
| 705 return GetNumDynamicOutgoingStreams() - GetNumDrainingOutgoingStreams() + | |
| 706 GetNumLocallyClosedOutgoingStreamsHighestOffset(); | |
| 707 } | |
| 708 | |
| 709 size_t QuicSession::GetNumActiveStreams() const { | |
| 710 return dynamic_stream_map_.size() - draining_streams_.size(); | |
| 711 } | |
| 712 | |
| 713 size_t QuicSession::GetNumAvailableStreams() const { | |
| 714 return available_streams_.size(); | |
| 715 } | |
| 716 | |
| 717 void QuicSession::MarkConnectionLevelWriteBlocked(QuicStreamId id) { | |
| 718 QUIC_BUG_IF(GetOrCreateStream(id) == nullptr) << "Marking unknown stream " | |
| 719 << id << " blocked."; | |
| 720 | |
| 721 write_blocked_streams_.AddStream(id); | |
| 722 } | |
| 723 | |
| 724 bool QuicSession::HasDataToWrite() const { | |
| 725 return write_blocked_streams_.HasWriteBlockedCryptoOrHeadersStream() || | |
| 726 write_blocked_streams_.HasWriteBlockedDataStreams() || | |
| 727 connection_->HasQueuedData(); | |
| 728 } | |
| 729 | |
| 730 void QuicSession::PostProcessAfterData() { | |
| 731 STLDeleteElements(&closed_streams_); | |
| 732 closed_streams_.clear(); | |
| 733 } | |
| 734 | |
| 735 size_t QuicSession::GetNumDynamicOutgoingStreams() const { | |
| 736 return dynamic_stream_map_.size() - num_dynamic_incoming_streams_; | |
| 737 } | |
| 738 | |
| 739 size_t QuicSession::GetNumDrainingOutgoingStreams() const { | |
| 740 return draining_streams_.size() - num_draining_incoming_streams_; | |
| 741 } | |
| 742 | |
| 743 size_t QuicSession::GetNumLocallyClosedOutgoingStreamsHighestOffset() const { | |
| 744 return locally_closed_streams_highest_offset_.size() - | |
| 745 num_locally_closed_incoming_streams_highest_offset_; | |
| 746 } | |
| 747 | |
| 748 bool QuicSession::IsConnectionFlowControlBlocked() const { | |
| 749 return flow_controller_.IsBlocked(); | |
| 750 } | |
| 751 | |
| 752 bool QuicSession::IsStreamFlowControlBlocked() { | |
| 753 for (auto const& kv : static_stream_map_) { | |
| 754 if (kv.second->flow_controller()->IsBlocked()) { | |
| 755 return true; | |
| 756 } | |
| 757 } | |
| 758 for (auto const& kv : dynamic_stream_map_) { | |
| 759 if (kv.second->flow_controller()->IsBlocked()) { | |
| 760 return true; | |
| 761 } | |
| 762 } | |
| 763 return false; | |
| 764 } | |
| 765 | |
| 766 size_t QuicSession::MaxAvailableStreams() const { | |
| 767 return max_open_incoming_streams_ * kMaxAvailableStreamsMultiplier; | |
| 768 } | |
| 769 | |
| 770 bool QuicSession::IsIncomingStream(QuicStreamId id) const { | |
| 771 return id % 2 != next_outgoing_stream_id_ % 2; | |
| 772 } | |
| 773 | |
| 774 } // namespace net | |
| OLD | NEW |