QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 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/third_party/quiche/src/quic/core/http/quic_server_session_base.h" |
| 6 | |
vasilvv | 872e7a3 | 2019-03-12 16:42:44 -0700 | [diff] [blame^] | 7 | #include <string> |
| 8 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 9 | #include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h" |
| 10 | #include "net/third_party/quiche/src/quic/core/quic_connection.h" |
| 11 | #include "net/third_party/quiche/src/quic/core/quic_stream.h" |
| 12 | #include "net/third_party/quiche/src/quic/core/quic_utils.h" |
| 13 | #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" |
| 14 | #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" |
| 15 | #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" |
| 16 | #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 17 | |
| 18 | namespace quic { |
| 19 | |
| 20 | QuicServerSessionBase::QuicServerSessionBase( |
| 21 | const QuicConfig& config, |
| 22 | const ParsedQuicVersionVector& supported_versions, |
| 23 | QuicConnection* connection, |
| 24 | Visitor* visitor, |
| 25 | QuicCryptoServerStream::Helper* helper, |
| 26 | const QuicCryptoServerConfig* crypto_config, |
| 27 | QuicCompressedCertsCache* compressed_certs_cache) |
| 28 | : QuicSpdySession(connection, visitor, config, supported_versions), |
| 29 | crypto_config_(crypto_config), |
| 30 | compressed_certs_cache_(compressed_certs_cache), |
| 31 | helper_(helper), |
| 32 | bandwidth_resumption_enabled_(false), |
| 33 | bandwidth_estimate_sent_to_client_(QuicBandwidth::Zero()), |
| 34 | last_scup_time_(QuicTime::Zero()) {} |
| 35 | |
| 36 | QuicServerSessionBase::~QuicServerSessionBase() {} |
| 37 | |
| 38 | void QuicServerSessionBase::Initialize() { |
| 39 | crypto_stream_.reset( |
| 40 | CreateQuicCryptoServerStream(crypto_config_, compressed_certs_cache_)); |
| 41 | QuicSpdySession::Initialize(); |
| 42 | } |
| 43 | |
| 44 | void QuicServerSessionBase::OnConfigNegotiated() { |
| 45 | QuicSpdySession::OnConfigNegotiated(); |
| 46 | |
| 47 | if (!config()->HasReceivedConnectionOptions()) { |
| 48 | return; |
| 49 | } |
| 50 | |
| 51 | // Enable bandwidth resumption if peer sent correct connection options. |
| 52 | const bool last_bandwidth_resumption = |
| 53 | ContainsQuicTag(config()->ReceivedConnectionOptions(), kBWRE); |
| 54 | const bool max_bandwidth_resumption = |
| 55 | ContainsQuicTag(config()->ReceivedConnectionOptions(), kBWMX); |
| 56 | bandwidth_resumption_enabled_ = |
| 57 | last_bandwidth_resumption || max_bandwidth_resumption; |
| 58 | |
| 59 | // If the client has provided a bandwidth estimate from the same serving |
| 60 | // region as this server, then decide whether to use the data for bandwidth |
| 61 | // resumption. |
| 62 | const CachedNetworkParameters* cached_network_params = |
| 63 | crypto_stream_->PreviousCachedNetworkParams(); |
| 64 | if (cached_network_params != nullptr && |
| 65 | cached_network_params->serving_region() == serving_region_) { |
| 66 | // Log the received connection parameters, regardless of how they |
| 67 | // get used for bandwidth resumption. |
| 68 | connection()->OnReceiveConnectionState(*cached_network_params); |
| 69 | |
| 70 | if (bandwidth_resumption_enabled_) { |
| 71 | // Only do bandwidth resumption if estimate is recent enough. |
| 72 | const uint64_t seconds_since_estimate = |
| 73 | connection()->clock()->WallNow().ToUNIXSeconds() - |
| 74 | cached_network_params->timestamp(); |
| 75 | if (seconds_since_estimate <= kNumSecondsPerHour) { |
| 76 | connection()->ResumeConnectionState(*cached_network_params, |
| 77 | max_bandwidth_resumption); |
| 78 | } |
| 79 | } |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | void QuicServerSessionBase::OnConnectionClosed(QuicErrorCode error, |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 84 | const std::string& error_details, |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 85 | ConnectionCloseSource source) { |
| 86 | QuicSession::OnConnectionClosed(error, error_details, source); |
| 87 | // In the unlikely event we get a connection close while doing an asynchronous |
| 88 | // crypto event, make sure we cancel the callback. |
| 89 | if (crypto_stream_ != nullptr) { |
| 90 | crypto_stream_->CancelOutstandingCallbacks(); |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | void QuicServerSessionBase::OnCongestionWindowChange(QuicTime now) { |
| 95 | if (!bandwidth_resumption_enabled_) { |
| 96 | return; |
| 97 | } |
| 98 | // Only send updates when the application has no data to write. |
| 99 | if (HasDataToWrite()) { |
| 100 | return; |
| 101 | } |
| 102 | |
| 103 | // If not enough time has passed since the last time we sent an update to the |
| 104 | // client, or not enough packets have been sent, then return early. |
| 105 | const QuicSentPacketManager& sent_packet_manager = |
| 106 | connection()->sent_packet_manager(); |
| 107 | int64_t srtt_ms = |
| 108 | sent_packet_manager.GetRttStats()->smoothed_rtt().ToMilliseconds(); |
| 109 | int64_t now_ms = (now - last_scup_time_).ToMilliseconds(); |
| 110 | int64_t packets_since_last_scup = 0; |
| 111 | const QuicPacketNumber largest_sent_packet = |
| 112 | connection()->sent_packet_manager().GetLargestSentPacket(); |
| 113 | if (largest_sent_packet.IsInitialized()) { |
| 114 | packets_since_last_scup = |
| 115 | last_scup_packet_number_.IsInitialized() |
| 116 | ? largest_sent_packet - last_scup_packet_number_ |
| 117 | : largest_sent_packet.ToUint64(); |
| 118 | } |
| 119 | if (now_ms < (kMinIntervalBetweenServerConfigUpdatesRTTs * srtt_ms) || |
| 120 | now_ms < kMinIntervalBetweenServerConfigUpdatesMs || |
| 121 | packets_since_last_scup < kMinPacketsBetweenServerConfigUpdates) { |
| 122 | return; |
| 123 | } |
| 124 | |
| 125 | // If the bandwidth recorder does not have a valid estimate, return early. |
| 126 | const QuicSustainedBandwidthRecorder* bandwidth_recorder = |
| 127 | sent_packet_manager.SustainedBandwidthRecorder(); |
| 128 | if (bandwidth_recorder == nullptr || !bandwidth_recorder->HasEstimate()) { |
| 129 | return; |
| 130 | } |
| 131 | |
| 132 | // The bandwidth recorder has recorded at least one sustained bandwidth |
| 133 | // estimate. Check that it's substantially different from the last one that |
| 134 | // we sent to the client, and if so, send the new one. |
| 135 | QuicBandwidth new_bandwidth_estimate = |
| 136 | bandwidth_recorder->BandwidthEstimate(); |
| 137 | |
| 138 | int64_t bandwidth_delta = |
| 139 | std::abs(new_bandwidth_estimate.ToBitsPerSecond() - |
| 140 | bandwidth_estimate_sent_to_client_.ToBitsPerSecond()); |
| 141 | |
| 142 | // Define "substantial" difference as a 50% increase or decrease from the |
| 143 | // last estimate. |
| 144 | bool substantial_difference = |
| 145 | bandwidth_delta > |
| 146 | 0.5 * bandwidth_estimate_sent_to_client_.ToBitsPerSecond(); |
| 147 | if (!substantial_difference) { |
| 148 | return; |
| 149 | } |
| 150 | |
| 151 | bandwidth_estimate_sent_to_client_ = new_bandwidth_estimate; |
| 152 | QUIC_DVLOG(1) << "Server: sending new bandwidth estimate (KBytes/s): " |
| 153 | << bandwidth_estimate_sent_to_client_.ToKBytesPerSecond(); |
| 154 | |
| 155 | // Include max bandwidth in the update. |
| 156 | QuicBandwidth max_bandwidth_estimate = |
| 157 | bandwidth_recorder->MaxBandwidthEstimate(); |
| 158 | int32_t max_bandwidth_timestamp = bandwidth_recorder->MaxBandwidthTimestamp(); |
| 159 | |
| 160 | // Fill the proto before passing it to the crypto stream to send. |
| 161 | const int32_t bw_estimate_bytes_per_second = |
| 162 | BandwidthToCachedParameterBytesPerSecond( |
| 163 | bandwidth_estimate_sent_to_client_); |
| 164 | const int32_t max_bw_estimate_bytes_per_second = |
| 165 | BandwidthToCachedParameterBytesPerSecond(max_bandwidth_estimate); |
| 166 | QUIC_BUG_IF(max_bw_estimate_bytes_per_second < 0) |
| 167 | << max_bw_estimate_bytes_per_second; |
| 168 | QUIC_BUG_IF(bw_estimate_bytes_per_second < 0) << bw_estimate_bytes_per_second; |
| 169 | |
| 170 | CachedNetworkParameters cached_network_params; |
| 171 | cached_network_params.set_bandwidth_estimate_bytes_per_second( |
| 172 | bw_estimate_bytes_per_second); |
| 173 | cached_network_params.set_max_bandwidth_estimate_bytes_per_second( |
| 174 | max_bw_estimate_bytes_per_second); |
| 175 | cached_network_params.set_max_bandwidth_timestamp_seconds( |
| 176 | max_bandwidth_timestamp); |
| 177 | cached_network_params.set_min_rtt_ms( |
| 178 | sent_packet_manager.GetRttStats()->min_rtt().ToMilliseconds()); |
| 179 | cached_network_params.set_previous_connection_state( |
| 180 | bandwidth_recorder->EstimateRecordedDuringSlowStart() |
| 181 | ? CachedNetworkParameters::SLOW_START |
| 182 | : CachedNetworkParameters::CONGESTION_AVOIDANCE); |
| 183 | cached_network_params.set_timestamp( |
| 184 | connection()->clock()->WallNow().ToUNIXSeconds()); |
| 185 | if (!serving_region_.empty()) { |
| 186 | cached_network_params.set_serving_region(serving_region_); |
| 187 | } |
| 188 | |
| 189 | crypto_stream_->SendServerConfigUpdate(&cached_network_params); |
| 190 | |
| 191 | connection()->OnSendConnectionState(cached_network_params); |
| 192 | |
| 193 | last_scup_time_ = now; |
| 194 | last_scup_packet_number_ = |
| 195 | connection()->sent_packet_manager().GetLargestSentPacket(); |
| 196 | } |
| 197 | |
| 198 | bool QuicServerSessionBase::ShouldCreateIncomingStream(QuicStreamId id) { |
| 199 | if (!connection()->connected()) { |
| 200 | QUIC_BUG << "ShouldCreateIncomingStream called when disconnected"; |
| 201 | return false; |
| 202 | } |
| 203 | |
| 204 | if (QuicUtils::IsServerInitiatedStreamId(connection()->transport_version(), |
| 205 | id)) { |
| 206 | QUIC_DLOG(INFO) << "Invalid incoming even stream_id:" << id; |
| 207 | connection()->CloseConnection( |
| 208 | QUIC_INVALID_STREAM_ID, "Client created even numbered stream", |
| 209 | ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| 210 | return false; |
| 211 | } |
| 212 | return true; |
| 213 | } |
| 214 | |
| 215 | bool QuicServerSessionBase::ShouldCreateOutgoingBidirectionalStream() { |
| 216 | if (!connection()->connected()) { |
| 217 | QUIC_BUG |
| 218 | << "ShouldCreateOutgoingBidirectionalStream called when disconnected"; |
| 219 | return false; |
| 220 | } |
| 221 | if (!crypto_stream_->encryption_established()) { |
| 222 | QUIC_BUG << "Encryption not established so no outgoing stream created."; |
| 223 | return false; |
| 224 | } |
| 225 | |
| 226 | if (!GetQuicReloadableFlag(quic_use_common_stream_check) && |
| 227 | connection()->transport_version() != QUIC_VERSION_99) { |
| 228 | if (GetNumOpenOutgoingStreams() >= |
| 229 | stream_id_manager().max_open_outgoing_streams()) { |
| 230 | VLOG(1) << "No more streams should be created. " |
| 231 | << "Already " << GetNumOpenOutgoingStreams() << " open."; |
| 232 | return false; |
| 233 | } |
| 234 | } |
| 235 | QUIC_RELOADABLE_FLAG_COUNT_N(quic_use_common_stream_check, 2, 2); |
| 236 | return CanOpenNextOutgoingBidirectionalStream(); |
| 237 | } |
| 238 | |
| 239 | bool QuicServerSessionBase::ShouldCreateOutgoingUnidirectionalStream() { |
| 240 | if (!connection()->connected()) { |
| 241 | QUIC_BUG |
| 242 | << "ShouldCreateOutgoingUnidirectionalStream called when disconnected"; |
| 243 | return false; |
| 244 | } |
| 245 | if (!crypto_stream_->encryption_established()) { |
| 246 | QUIC_BUG << "Encryption not established so no outgoing stream created."; |
| 247 | return false; |
| 248 | } |
| 249 | |
| 250 | if (!GetQuicReloadableFlag(quic_use_common_stream_check) && |
| 251 | connection()->transport_version() != QUIC_VERSION_99) { |
| 252 | if (GetNumOpenOutgoingStreams() >= |
| 253 | stream_id_manager().max_open_outgoing_streams()) { |
| 254 | VLOG(1) << "No more streams should be created. " |
| 255 | << "Already " << GetNumOpenOutgoingStreams() << " open."; |
| 256 | return false; |
| 257 | } |
| 258 | } |
| 259 | |
| 260 | return CanOpenNextOutgoingUnidirectionalStream(); |
| 261 | } |
| 262 | |
| 263 | QuicCryptoServerStreamBase* QuicServerSessionBase::GetMutableCryptoStream() { |
| 264 | return crypto_stream_.get(); |
| 265 | } |
| 266 | |
| 267 | const QuicCryptoServerStreamBase* QuicServerSessionBase::GetCryptoStream() |
| 268 | const { |
| 269 | return crypto_stream_.get(); |
| 270 | } |
| 271 | |
| 272 | int32_t QuicServerSessionBase::BandwidthToCachedParameterBytesPerSecond( |
| 273 | const QuicBandwidth& bandwidth) { |
| 274 | return static_cast<int32_t>(std::min<int64_t>( |
| 275 | bandwidth.ToBytesPerSecond(), std::numeric_limits<uint32_t>::max())); |
| 276 | } |
| 277 | |
| 278 | } // namespace quic |