| 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/spdy/spdy_session.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <limits> | |
| 9 #include <map> | |
| 10 #include <utility> | |
| 11 | |
| 12 #include "base/bind.h" | |
| 13 #include "base/compiler_specific.h" | |
| 14 #include "base/feature_list.h" | |
| 15 #include "base/location.h" | |
| 16 #include "base/logging.h" | |
| 17 #include "base/memory/ptr_util.h" | |
| 18 #include "base/metrics/histogram_macros.h" | |
| 19 #include "base/metrics/sparse_histogram.h" | |
| 20 #include "base/profiler/scoped_tracker.h" | |
| 21 #include "base/single_thread_task_runner.h" | |
| 22 #include "base/stl_util.h" | |
| 23 #include "base/strings/string_number_conversions.h" | |
| 24 #include "base/strings/string_util.h" | |
| 25 #include "base/strings/utf_string_conversions.h" | |
| 26 #include "base/threading/thread_task_runner_handle.h" | |
| 27 #include "base/time/time.h" | |
| 28 #include "base/trace_event/trace_event.h" | |
| 29 #include "base/values.h" | |
| 30 #include "crypto/ec_private_key.h" | |
| 31 #include "crypto/ec_signature_creator.h" | |
| 32 #include "net/base/proxy_delegate.h" | |
| 33 #include "net/cert/asn1_util.h" | |
| 34 #include "net/cert/cert_verify_result.h" | |
| 35 #include "net/cert/ct_policy_status.h" | |
| 36 #include "net/http/http_network_session.h" | |
| 37 #include "net/http/http_server_properties.h" | |
| 38 #include "net/http/http_util.h" | |
| 39 #include "net/http/transport_security_state.h" | |
| 40 #include "net/log/net_log.h" | |
| 41 #include "net/log/net_log_capture_mode.h" | |
| 42 #include "net/log/net_log_event_type.h" | |
| 43 #include "net/log/net_log_source_type.h" | |
| 44 #include "net/log/net_log_with_source.h" | |
| 45 #include "net/proxy/proxy_server.h" | |
| 46 #include "net/socket/socket.h" | |
| 47 #include "net/socket/ssl_client_socket.h" | |
| 48 #include "net/spdy/platform/api/spdy_estimate_memory_usage.h" | |
| 49 #include "net/spdy/platform/api/spdy_string_utils.h" | |
| 50 #include "net/spdy/spdy_buffer_producer.h" | |
| 51 #include "net/spdy/spdy_frame_builder.h" | |
| 52 #include "net/spdy/spdy_http_utils.h" | |
| 53 #include "net/spdy/spdy_log_util.h" | |
| 54 #include "net/spdy/spdy_protocol.h" | |
| 55 #include "net/spdy/spdy_session_pool.h" | |
| 56 #include "net/spdy/spdy_stream.h" | |
| 57 #include "net/ssl/channel_id_service.h" | |
| 58 #include "net/ssl/ssl_cipher_suite_names.h" | |
| 59 #include "net/ssl/ssl_connection_status_flags.h" | |
| 60 | |
| 61 namespace net { | |
| 62 | |
| 63 namespace { | |
| 64 | |
| 65 const int kReadBufferSize = 8 * 1024; | |
| 66 const int kDefaultConnectionAtRiskOfLossSeconds = 10; | |
| 67 const int kHungIntervalSeconds = 10; | |
| 68 | |
| 69 // Minimum seconds that unclaimed pushed streams will be kept in memory. | |
| 70 const int kMinPushedStreamLifetimeSeconds = 300; | |
| 71 | |
| 72 // Default initial value for HTTP/2 SETTINGS. | |
| 73 const uint32_t kDefaultInitialHeaderTableSize = 4096; | |
| 74 const uint32_t kDefaultInitialEnablePush = 1; | |
| 75 const uint32_t kDefaultInitialInitialWindowSize = 65535; | |
| 76 const uint32_t kDefaultInitialMaxFrameSize = 16384; | |
| 77 | |
| 78 bool IsSpdySettingAtDefaultInitialValue(SpdySettingsIds setting_id, | |
| 79 uint32_t value) { | |
| 80 switch (setting_id) { | |
| 81 case SETTINGS_HEADER_TABLE_SIZE: | |
| 82 return value == kDefaultInitialHeaderTableSize; | |
| 83 case SETTINGS_ENABLE_PUSH: | |
| 84 return value == kDefaultInitialEnablePush; | |
| 85 case SETTINGS_MAX_CONCURRENT_STREAMS: | |
| 86 // There is no initial limit on the number of concurrent streams. | |
| 87 return false; | |
| 88 case SETTINGS_INITIAL_WINDOW_SIZE: | |
| 89 return value == kDefaultInitialInitialWindowSize; | |
| 90 case SETTINGS_MAX_FRAME_SIZE: | |
| 91 return value == kDefaultInitialMaxFrameSize; | |
| 92 case SETTINGS_MAX_HEADER_LIST_SIZE: | |
| 93 // There is no initial limit on the size of the header list. | |
| 94 return false; | |
| 95 default: | |
| 96 // Undefined parameters have no initial value. | |
| 97 return false; | |
| 98 } | |
| 99 } | |
| 100 | |
| 101 std::unique_ptr<base::Value> NetLogSpdyHeadersSentCallback( | |
| 102 const SpdyHeaderBlock* headers, | |
| 103 bool fin, | |
| 104 SpdyStreamId stream_id, | |
| 105 bool has_priority, | |
| 106 int weight, | |
| 107 SpdyStreamId parent_stream_id, | |
| 108 bool exclusive, | |
| 109 NetLogSource source_dependency, | |
| 110 NetLogCaptureMode capture_mode) { | |
| 111 auto dict = base::MakeUnique<base::DictionaryValue>(); | |
| 112 dict->Set("headers", ElideSpdyHeaderBlockForNetLog(*headers, capture_mode)); | |
| 113 dict->SetBoolean("fin", fin); | |
| 114 dict->SetInteger("stream_id", stream_id); | |
| 115 dict->SetBoolean("has_priority", has_priority); | |
| 116 if (has_priority) { | |
| 117 dict->SetInteger("parent_stream_id", parent_stream_id); | |
| 118 dict->SetInteger("weight", weight); | |
| 119 dict->SetBoolean("exclusive", exclusive); | |
| 120 } | |
| 121 if (source_dependency.IsValid()) { | |
| 122 source_dependency.AddToEventParameters(dict.get()); | |
| 123 } | |
| 124 return std::move(dict); | |
| 125 } | |
| 126 | |
| 127 std::unique_ptr<base::Value> NetLogSpdyHeadersReceivedCallback( | |
| 128 const SpdyHeaderBlock* headers, | |
| 129 bool fin, | |
| 130 SpdyStreamId stream_id, | |
| 131 NetLogCaptureMode capture_mode) { | |
| 132 auto dict = base::MakeUnique<base::DictionaryValue>(); | |
| 133 dict->Set("headers", ElideSpdyHeaderBlockForNetLog(*headers, capture_mode)); | |
| 134 dict->SetBoolean("fin", fin); | |
| 135 dict->SetInteger("stream_id", stream_id); | |
| 136 return std::move(dict); | |
| 137 } | |
| 138 | |
| 139 std::unique_ptr<base::Value> NetLogSpdySessionCloseCallback( | |
| 140 int net_error, | |
| 141 const SpdyString* description, | |
| 142 NetLogCaptureMode /* capture_mode */) { | |
| 143 auto dict = base::MakeUnique<base::DictionaryValue>(); | |
| 144 dict->SetInteger("net_error", net_error); | |
| 145 dict->SetString("description", *description); | |
| 146 return std::move(dict); | |
| 147 } | |
| 148 | |
| 149 std::unique_ptr<base::Value> NetLogSpdySessionCallback( | |
| 150 const HostPortProxyPair* host_pair, | |
| 151 NetLogCaptureMode /* capture_mode */) { | |
| 152 auto dict = base::MakeUnique<base::DictionaryValue>(); | |
| 153 dict->SetString("host", host_pair->first.ToString()); | |
| 154 dict->SetString("proxy", host_pair->second.ToPacString()); | |
| 155 return std::move(dict); | |
| 156 } | |
| 157 | |
| 158 std::unique_ptr<base::Value> NetLogSpdyInitializedCallback( | |
| 159 NetLogSource source, | |
| 160 NetLogCaptureMode /* capture_mode */) { | |
| 161 auto dict = base::MakeUnique<base::DictionaryValue>(); | |
| 162 if (source.IsValid()) { | |
| 163 source.AddToEventParameters(dict.get()); | |
| 164 } | |
| 165 dict->SetString("protocol", NextProtoToString(kProtoHTTP2)); | |
| 166 return std::move(dict); | |
| 167 } | |
| 168 | |
| 169 std::unique_ptr<base::Value> NetLogSpdySendSettingsCallback( | |
| 170 const SettingsMap* settings, | |
| 171 NetLogCaptureMode /* capture_mode */) { | |
| 172 auto dict = base::MakeUnique<base::DictionaryValue>(); | |
| 173 auto settings_list = base::MakeUnique<base::ListValue>(); | |
| 174 for (SettingsMap::const_iterator it = settings->begin(); | |
| 175 it != settings->end(); ++it) { | |
| 176 const SpdySettingsIds id = it->first; | |
| 177 const uint32_t value = it->second; | |
| 178 const char* settings_string; | |
| 179 SettingsIdToString(id, &settings_string); | |
| 180 settings_list->AppendString( | |
| 181 SpdyStringPrintf("[id:%u (%s) value:%u]", id, settings_string, value)); | |
| 182 } | |
| 183 dict->Set("settings", std::move(settings_list)); | |
| 184 return std::move(dict); | |
| 185 } | |
| 186 | |
| 187 std::unique_ptr<base::Value> NetLogSpdyRecvSettingsCallback( | |
| 188 const HostPortPair& host_port_pair, | |
| 189 NetLogCaptureMode /* capture_mode */) { | |
| 190 auto dict = base::MakeUnique<base::DictionaryValue>(); | |
| 191 dict->SetString("host", host_port_pair.ToString()); | |
| 192 return std::move(dict); | |
| 193 } | |
| 194 | |
| 195 std::unique_ptr<base::Value> NetLogSpdyRecvSettingCallback( | |
| 196 SpdySettingsIds id, | |
| 197 uint32_t value, | |
| 198 NetLogCaptureMode /* capture_mode */) { | |
| 199 auto dict = base::MakeUnique<base::DictionaryValue>(); | |
| 200 const char* settings_string; | |
| 201 SettingsIdToString(id, &settings_string); | |
| 202 dict->SetString("id", SpdyStringPrintf("%u (%s)", id, settings_string)); | |
| 203 dict->SetInteger("value", value); | |
| 204 return std::move(dict); | |
| 205 } | |
| 206 | |
| 207 std::unique_ptr<base::Value> NetLogSpdyWindowUpdateFrameCallback( | |
| 208 SpdyStreamId stream_id, | |
| 209 uint32_t delta, | |
| 210 NetLogCaptureMode /* capture_mode */) { | |
| 211 auto dict = base::MakeUnique<base::DictionaryValue>(); | |
| 212 dict->SetInteger("stream_id", static_cast<int>(stream_id)); | |
| 213 dict->SetInteger("delta", delta); | |
| 214 return std::move(dict); | |
| 215 } | |
| 216 | |
| 217 std::unique_ptr<base::Value> NetLogSpdySessionWindowUpdateCallback( | |
| 218 int32_t delta, | |
| 219 int32_t window_size, | |
| 220 NetLogCaptureMode /* capture_mode */) { | |
| 221 auto dict = base::MakeUnique<base::DictionaryValue>(); | |
| 222 dict->SetInteger("delta", delta); | |
| 223 dict->SetInteger("window_size", window_size); | |
| 224 return std::move(dict); | |
| 225 } | |
| 226 | |
| 227 std::unique_ptr<base::Value> NetLogSpdyDataCallback( | |
| 228 SpdyStreamId stream_id, | |
| 229 int size, | |
| 230 bool fin, | |
| 231 NetLogCaptureMode /* capture_mode */) { | |
| 232 auto dict = base::MakeUnique<base::DictionaryValue>(); | |
| 233 dict->SetInteger("stream_id", static_cast<int>(stream_id)); | |
| 234 dict->SetInteger("size", size); | |
| 235 dict->SetBoolean("fin", fin); | |
| 236 return std::move(dict); | |
| 237 } | |
| 238 | |
| 239 std::unique_ptr<base::Value> NetLogSpdyRecvRstStreamCallback( | |
| 240 SpdyStreamId stream_id, | |
| 241 SpdyErrorCode error_code, | |
| 242 NetLogCaptureMode /* capture_mode */) { | |
| 243 auto dict = base::MakeUnique<base::DictionaryValue>(); | |
| 244 dict->SetInteger("stream_id", static_cast<int>(stream_id)); | |
| 245 dict->SetString( | |
| 246 "error_code", | |
| 247 SpdyStringPrintf("%u (%s)", error_code, ErrorCodeToString(error_code))); | |
| 248 return std::move(dict); | |
| 249 } | |
| 250 | |
| 251 std::unique_ptr<base::Value> NetLogSpdySendRstStreamCallback( | |
| 252 SpdyStreamId stream_id, | |
| 253 SpdyErrorCode error_code, | |
| 254 const SpdyString* description, | |
| 255 NetLogCaptureMode /* capture_mode */) { | |
| 256 auto dict = base::MakeUnique<base::DictionaryValue>(); | |
| 257 dict->SetInteger("stream_id", static_cast<int>(stream_id)); | |
| 258 dict->SetString( | |
| 259 "error_code", | |
| 260 SpdyStringPrintf("%u (%s)", error_code, ErrorCodeToString(error_code))); | |
| 261 dict->SetString("description", *description); | |
| 262 return std::move(dict); | |
| 263 } | |
| 264 | |
| 265 std::unique_ptr<base::Value> NetLogSpdyPingCallback( | |
| 266 SpdyPingId unique_id, | |
| 267 bool is_ack, | |
| 268 const char* type, | |
| 269 NetLogCaptureMode /* capture_mode */) { | |
| 270 auto dict = base::MakeUnique<base::DictionaryValue>(); | |
| 271 dict->SetInteger("unique_id", static_cast<int>(unique_id)); | |
| 272 dict->SetString("type", type); | |
| 273 dict->SetBoolean("is_ack", is_ack); | |
| 274 return std::move(dict); | |
| 275 } | |
| 276 | |
| 277 std::unique_ptr<base::Value> NetLogSpdyRecvGoAwayCallback( | |
| 278 SpdyStreamId last_stream_id, | |
| 279 int active_streams, | |
| 280 int unclaimed_streams, | |
| 281 SpdyErrorCode error_code, | |
| 282 SpdyStringPiece debug_data, | |
| 283 NetLogCaptureMode capture_mode) { | |
| 284 auto dict = base::MakeUnique<base::DictionaryValue>(); | |
| 285 dict->SetInteger("last_accepted_stream_id", static_cast<int>(last_stream_id)); | |
| 286 dict->SetInteger("active_streams", active_streams); | |
| 287 dict->SetInteger("unclaimed_streams", unclaimed_streams); | |
| 288 dict->SetString( | |
| 289 "error_code", | |
| 290 SpdyStringPrintf("%u (%s)", error_code, ErrorCodeToString(error_code))); | |
| 291 dict->SetString("debug_data", | |
| 292 ElideGoAwayDebugDataForNetLog(capture_mode, debug_data)); | |
| 293 return std::move(dict); | |
| 294 } | |
| 295 | |
| 296 std::unique_ptr<base::Value> NetLogSpdyPushPromiseReceivedCallback( | |
| 297 const SpdyHeaderBlock* headers, | |
| 298 SpdyStreamId stream_id, | |
| 299 SpdyStreamId promised_stream_id, | |
| 300 NetLogCaptureMode capture_mode) { | |
| 301 auto dict = base::MakeUnique<base::DictionaryValue>(); | |
| 302 dict->Set("headers", ElideSpdyHeaderBlockForNetLog(*headers, capture_mode)); | |
| 303 dict->SetInteger("id", stream_id); | |
| 304 dict->SetInteger("promised_stream_id", promised_stream_id); | |
| 305 return std::move(dict); | |
| 306 } | |
| 307 | |
| 308 std::unique_ptr<base::Value> NetLogSpdyAdoptedPushStreamCallback( | |
| 309 SpdyStreamId stream_id, | |
| 310 const GURL* url, | |
| 311 NetLogCaptureMode capture_mode) { | |
| 312 auto dict = base::MakeUnique<base::DictionaryValue>(); | |
| 313 dict->SetInteger("stream_id", stream_id); | |
| 314 dict->SetString("url", url->spec()); | |
| 315 return std::move(dict); | |
| 316 } | |
| 317 | |
| 318 std::unique_ptr<base::Value> NetLogSpdySessionStalledCallback( | |
| 319 size_t num_active_streams, | |
| 320 size_t num_created_streams, | |
| 321 size_t num_pushed_streams, | |
| 322 size_t max_concurrent_streams, | |
| 323 const SpdyString& url, | |
| 324 NetLogCaptureMode capture_mode) { | |
| 325 auto dict = base::MakeUnique<base::DictionaryValue>(); | |
| 326 dict->SetInteger("num_active_streams", num_active_streams); | |
| 327 dict->SetInteger("num_created_streams", num_created_streams); | |
| 328 dict->SetInteger("num_pushed_streams", num_pushed_streams); | |
| 329 dict->SetInteger("max_concurrent_streams", max_concurrent_streams); | |
| 330 dict->SetString("url", url); | |
| 331 return std::move(dict); | |
| 332 } | |
| 333 | |
| 334 std::unique_ptr<base::Value> NetLogSpdyPriorityCallback( | |
| 335 SpdyStreamId stream_id, | |
| 336 SpdyStreamId parent_stream_id, | |
| 337 int weight, | |
| 338 bool exclusive, | |
| 339 NetLogCaptureMode capture_mode) { | |
| 340 auto dict = base::MakeUnique<base::DictionaryValue>(); | |
| 341 dict->SetInteger("stream_id", stream_id); | |
| 342 dict->SetInteger("parent_stream_id", parent_stream_id); | |
| 343 dict->SetInteger("weight", weight); | |
| 344 dict->SetBoolean("exclusive", exclusive); | |
| 345 return std::move(dict); | |
| 346 } | |
| 347 | |
| 348 // Helper function to return the total size of an array of objects | |
| 349 // with .size() member functions. | |
| 350 template <typename T, size_t N> | |
| 351 size_t GetTotalSize(const T (&arr)[N]) { | |
| 352 size_t total_size = 0; | |
| 353 for (size_t i = 0; i < N; ++i) { | |
| 354 total_size += arr[i].size(); | |
| 355 } | |
| 356 return total_size; | |
| 357 } | |
| 358 | |
| 359 // Helper class for std:find_if on STL container containing | |
| 360 // SpdyStreamRequest weak pointers. | |
| 361 class RequestEquals { | |
| 362 public: | |
| 363 explicit RequestEquals(const base::WeakPtr<SpdyStreamRequest>& request) | |
| 364 : request_(request) {} | |
| 365 | |
| 366 bool operator()(const base::WeakPtr<SpdyStreamRequest>& request) const { | |
| 367 return request_.get() == request.get(); | |
| 368 } | |
| 369 | |
| 370 private: | |
| 371 const base::WeakPtr<SpdyStreamRequest> request_; | |
| 372 }; | |
| 373 | |
| 374 // The maximum number of concurrent streams we will ever create. Even if | |
| 375 // the server permits more, we will never exceed this limit. | |
| 376 const size_t kMaxConcurrentStreamLimit = 256; | |
| 377 | |
| 378 class SpdyServerPushHelper : public ServerPushDelegate::ServerPushHelper { | |
| 379 public: | |
| 380 explicit SpdyServerPushHelper(base::WeakPtr<SpdySession> session, | |
| 381 const GURL& url) | |
| 382 : session_(session), request_url_(url) {} | |
| 383 | |
| 384 void Cancel() override { | |
| 385 if (session_) | |
| 386 session_->CancelPush(request_url_); | |
| 387 } | |
| 388 | |
| 389 const GURL& GetURL() const override { return request_url_; } | |
| 390 | |
| 391 private: | |
| 392 base::WeakPtr<SpdySession> session_; | |
| 393 const GURL request_url_; | |
| 394 }; | |
| 395 | |
| 396 } // namespace | |
| 397 | |
| 398 SpdyProtocolErrorDetails MapFramerErrorToProtocolError( | |
| 399 SpdyFramer::SpdyFramerError err) { | |
| 400 switch (err) { | |
| 401 case SpdyFramer::SPDY_NO_ERROR: | |
| 402 return SPDY_ERROR_NO_ERROR; | |
| 403 case SpdyFramer::SPDY_INVALID_STREAM_ID: | |
| 404 return SPDY_ERROR_INVALID_STREAM_ID; | |
| 405 case SpdyFramer::SPDY_INVALID_CONTROL_FRAME: | |
| 406 return SPDY_ERROR_INVALID_CONTROL_FRAME; | |
| 407 case SpdyFramer::SPDY_CONTROL_PAYLOAD_TOO_LARGE: | |
| 408 return SPDY_ERROR_CONTROL_PAYLOAD_TOO_LARGE; | |
| 409 case SpdyFramer::SPDY_ZLIB_INIT_FAILURE: | |
| 410 return SPDY_ERROR_ZLIB_INIT_FAILURE; | |
| 411 case SpdyFramer::SPDY_UNSUPPORTED_VERSION: | |
| 412 return SPDY_ERROR_UNSUPPORTED_VERSION; | |
| 413 case SpdyFramer::SPDY_DECOMPRESS_FAILURE: | |
| 414 return SPDY_ERROR_DECOMPRESS_FAILURE; | |
| 415 case SpdyFramer::SPDY_COMPRESS_FAILURE: | |
| 416 return SPDY_ERROR_COMPRESS_FAILURE; | |
| 417 case SpdyFramer::SPDY_GOAWAY_FRAME_CORRUPT: | |
| 418 return SPDY_ERROR_GOAWAY_FRAME_CORRUPT; | |
| 419 case SpdyFramer::SPDY_RST_STREAM_FRAME_CORRUPT: | |
| 420 return SPDY_ERROR_RST_STREAM_FRAME_CORRUPT; | |
| 421 case SpdyFramer::SPDY_INVALID_PADDING: | |
| 422 return SPDY_ERROR_INVALID_PADDING; | |
| 423 case SpdyFramer::SPDY_INVALID_DATA_FRAME_FLAGS: | |
| 424 return SPDY_ERROR_INVALID_DATA_FRAME_FLAGS; | |
| 425 case SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS: | |
| 426 return SPDY_ERROR_INVALID_CONTROL_FRAME_FLAGS; | |
| 427 case SpdyFramer::SPDY_UNEXPECTED_FRAME: | |
| 428 return SPDY_ERROR_UNEXPECTED_FRAME; | |
| 429 case SpdyFramer::SPDY_INTERNAL_FRAMER_ERROR: | |
| 430 return SPDY_ERROR_INTERNAL_FRAMER_ERROR; | |
| 431 case SpdyFramer::SPDY_INVALID_CONTROL_FRAME_SIZE: | |
| 432 return SPDY_ERROR_INVALID_CONTROL_FRAME_SIZE; | |
| 433 case SpdyFramer::SPDY_OVERSIZED_PAYLOAD: | |
| 434 return SPDY_ERROR_OVERSIZED_PAYLOAD; | |
| 435 case SpdyFramer::LAST_ERROR: | |
| 436 NOTREACHED(); | |
| 437 } | |
| 438 NOTREACHED(); | |
| 439 return static_cast<SpdyProtocolErrorDetails>(-1); | |
| 440 } | |
| 441 | |
| 442 Error MapFramerErrorToNetError(SpdyFramer::SpdyFramerError err) { | |
| 443 switch (err) { | |
| 444 case SpdyFramer::SPDY_NO_ERROR: | |
| 445 return OK; | |
| 446 case SpdyFramer::SPDY_INVALID_CONTROL_FRAME: | |
| 447 return ERR_SPDY_PROTOCOL_ERROR; | |
| 448 case SpdyFramer::SPDY_CONTROL_PAYLOAD_TOO_LARGE: | |
| 449 return ERR_SPDY_FRAME_SIZE_ERROR; | |
| 450 case SpdyFramer::SPDY_ZLIB_INIT_FAILURE: | |
| 451 return ERR_SPDY_COMPRESSION_ERROR; | |
| 452 case SpdyFramer::SPDY_UNSUPPORTED_VERSION: | |
| 453 return ERR_SPDY_PROTOCOL_ERROR; | |
| 454 case SpdyFramer::SPDY_DECOMPRESS_FAILURE: | |
| 455 return ERR_SPDY_COMPRESSION_ERROR; | |
| 456 case SpdyFramer::SPDY_COMPRESS_FAILURE: | |
| 457 return ERR_SPDY_COMPRESSION_ERROR; | |
| 458 case SpdyFramer::SPDY_GOAWAY_FRAME_CORRUPT: | |
| 459 return ERR_SPDY_PROTOCOL_ERROR; | |
| 460 case SpdyFramer::SPDY_RST_STREAM_FRAME_CORRUPT: | |
| 461 return ERR_SPDY_PROTOCOL_ERROR; | |
| 462 case SpdyFramer::SPDY_INVALID_PADDING: | |
| 463 return ERR_SPDY_PROTOCOL_ERROR; | |
| 464 case SpdyFramer::SPDY_INVALID_DATA_FRAME_FLAGS: | |
| 465 return ERR_SPDY_PROTOCOL_ERROR; | |
| 466 case SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS: | |
| 467 return ERR_SPDY_PROTOCOL_ERROR; | |
| 468 case SpdyFramer::SPDY_UNEXPECTED_FRAME: | |
| 469 return ERR_SPDY_PROTOCOL_ERROR; | |
| 470 case SpdyFramer::SPDY_INTERNAL_FRAMER_ERROR: | |
| 471 return ERR_SPDY_PROTOCOL_ERROR; | |
| 472 case SpdyFramer::SPDY_INVALID_CONTROL_FRAME_SIZE: | |
| 473 return ERR_SPDY_FRAME_SIZE_ERROR; | |
| 474 case SpdyFramer::SPDY_INVALID_STREAM_ID: | |
| 475 return ERR_SPDY_PROTOCOL_ERROR; | |
| 476 case SpdyFramer::SPDY_OVERSIZED_PAYLOAD: | |
| 477 return ERR_SPDY_FRAME_SIZE_ERROR; | |
| 478 case SpdyFramer::LAST_ERROR: | |
| 479 NOTREACHED(); | |
| 480 } | |
| 481 NOTREACHED(); | |
| 482 return ERR_SPDY_PROTOCOL_ERROR; | |
| 483 } | |
| 484 | |
| 485 SpdyProtocolErrorDetails MapRstStreamStatusToProtocolError( | |
| 486 SpdyErrorCode error_code) { | |
| 487 switch (error_code) { | |
| 488 case ERROR_CODE_NO_ERROR: | |
| 489 return STATUS_CODE_NO_ERROR; | |
| 490 case ERROR_CODE_PROTOCOL_ERROR: | |
| 491 return STATUS_CODE_PROTOCOL_ERROR; | |
| 492 case ERROR_CODE_INTERNAL_ERROR: | |
| 493 return STATUS_CODE_INTERNAL_ERROR; | |
| 494 case ERROR_CODE_FLOW_CONTROL_ERROR: | |
| 495 return STATUS_CODE_FLOW_CONTROL_ERROR; | |
| 496 case ERROR_CODE_SETTINGS_TIMEOUT: | |
| 497 return STATUS_CODE_SETTINGS_TIMEOUT; | |
| 498 case ERROR_CODE_STREAM_CLOSED: | |
| 499 return STATUS_CODE_STREAM_CLOSED; | |
| 500 case ERROR_CODE_FRAME_SIZE_ERROR: | |
| 501 return STATUS_CODE_FRAME_SIZE_ERROR; | |
| 502 case ERROR_CODE_REFUSED_STREAM: | |
| 503 return STATUS_CODE_REFUSED_STREAM; | |
| 504 case ERROR_CODE_CANCEL: | |
| 505 return STATUS_CODE_CANCEL; | |
| 506 case ERROR_CODE_COMPRESSION_ERROR: | |
| 507 return STATUS_CODE_COMPRESSION_ERROR; | |
| 508 case ERROR_CODE_CONNECT_ERROR: | |
| 509 return STATUS_CODE_CONNECT_ERROR; | |
| 510 case ERROR_CODE_ENHANCE_YOUR_CALM: | |
| 511 return STATUS_CODE_ENHANCE_YOUR_CALM; | |
| 512 case ERROR_CODE_INADEQUATE_SECURITY: | |
| 513 return STATUS_CODE_INADEQUATE_SECURITY; | |
| 514 case ERROR_CODE_HTTP_1_1_REQUIRED: | |
| 515 return STATUS_CODE_HTTP_1_1_REQUIRED; | |
| 516 } | |
| 517 NOTREACHED(); | |
| 518 return static_cast<SpdyProtocolErrorDetails>(-1); | |
| 519 } | |
| 520 | |
| 521 SpdyErrorCode MapNetErrorToGoAwayStatus(Error err) { | |
| 522 switch (err) { | |
| 523 case OK: | |
| 524 return ERROR_CODE_NO_ERROR; | |
| 525 case ERR_SPDY_PROTOCOL_ERROR: | |
| 526 return ERROR_CODE_PROTOCOL_ERROR; | |
| 527 case ERR_SPDY_FLOW_CONTROL_ERROR: | |
| 528 return ERROR_CODE_FLOW_CONTROL_ERROR; | |
| 529 case ERR_SPDY_FRAME_SIZE_ERROR: | |
| 530 return ERROR_CODE_FRAME_SIZE_ERROR; | |
| 531 case ERR_SPDY_COMPRESSION_ERROR: | |
| 532 return ERROR_CODE_COMPRESSION_ERROR; | |
| 533 case ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY: | |
| 534 return ERROR_CODE_INADEQUATE_SECURITY; | |
| 535 default: | |
| 536 return ERROR_CODE_PROTOCOL_ERROR; | |
| 537 } | |
| 538 } | |
| 539 | |
| 540 SpdyStreamRequest::SpdyStreamRequest() : weak_ptr_factory_(this) { | |
| 541 Reset(); | |
| 542 } | |
| 543 | |
| 544 SpdyStreamRequest::~SpdyStreamRequest() { | |
| 545 CancelRequest(); | |
| 546 } | |
| 547 | |
| 548 int SpdyStreamRequest::StartRequest(SpdyStreamType type, | |
| 549 const base::WeakPtr<SpdySession>& session, | |
| 550 const GURL& url, | |
| 551 RequestPriority priority, | |
| 552 const NetLogWithSource& net_log, | |
| 553 const CompletionCallback& callback) { | |
| 554 DCHECK(session); | |
| 555 DCHECK(!session_); | |
| 556 DCHECK(!stream_); | |
| 557 DCHECK(callback_.is_null()); | |
| 558 | |
| 559 type_ = type; | |
| 560 session_ = session; | |
| 561 url_ = url; | |
| 562 priority_ = priority; | |
| 563 net_log_ = net_log; | |
| 564 callback_ = callback; | |
| 565 | |
| 566 base::WeakPtr<SpdyStream> stream; | |
| 567 int rv = session->TryCreateStream(weak_ptr_factory_.GetWeakPtr(), &stream); | |
| 568 if (rv == OK) { | |
| 569 Reset(); | |
| 570 stream_ = stream; | |
| 571 } | |
| 572 return rv; | |
| 573 } | |
| 574 | |
| 575 void SpdyStreamRequest::CancelRequest() { | |
| 576 if (session_) | |
| 577 session_->CancelStreamRequest(weak_ptr_factory_.GetWeakPtr()); | |
| 578 Reset(); | |
| 579 // Do this to cancel any pending CompleteStreamRequest() tasks. | |
| 580 weak_ptr_factory_.InvalidateWeakPtrs(); | |
| 581 } | |
| 582 | |
| 583 base::WeakPtr<SpdyStream> SpdyStreamRequest::ReleaseStream() { | |
| 584 DCHECK(!session_); | |
| 585 base::WeakPtr<SpdyStream> stream = stream_; | |
| 586 DCHECK(stream); | |
| 587 Reset(); | |
| 588 return stream; | |
| 589 } | |
| 590 | |
| 591 size_t SpdyStreamRequest::EstimateMemoryUsage() const { | |
| 592 return base::trace_event::EstimateItemMemoryUsage(url_); | |
| 593 } | |
| 594 | |
| 595 void SpdyStreamRequest::OnRequestCompleteSuccess( | |
| 596 const base::WeakPtr<SpdyStream>& stream) { | |
| 597 DCHECK(session_); | |
| 598 DCHECK(!stream_); | |
| 599 DCHECK(!callback_.is_null()); | |
| 600 CompletionCallback callback = callback_; | |
| 601 Reset(); | |
| 602 DCHECK(stream); | |
| 603 stream_ = stream; | |
| 604 callback.Run(OK); | |
| 605 } | |
| 606 | |
| 607 void SpdyStreamRequest::OnRequestCompleteFailure(int rv) { | |
| 608 DCHECK(session_); | |
| 609 DCHECK(!stream_); | |
| 610 DCHECK(!callback_.is_null()); | |
| 611 CompletionCallback callback = callback_; | |
| 612 Reset(); | |
| 613 DCHECK_NE(rv, OK); | |
| 614 callback.Run(rv); | |
| 615 } | |
| 616 | |
| 617 void SpdyStreamRequest::Reset() { | |
| 618 type_ = SPDY_BIDIRECTIONAL_STREAM; | |
| 619 session_.reset(); | |
| 620 stream_.reset(); | |
| 621 url_ = GURL(); | |
| 622 priority_ = MINIMUM_PRIORITY; | |
| 623 net_log_ = NetLogWithSource(); | |
| 624 callback_.Reset(); | |
| 625 } | |
| 626 | |
| 627 SpdySession::UnclaimedPushedStreamContainer::UnclaimedPushedStreamContainer( | |
| 628 SpdySession* spdy_session) | |
| 629 : spdy_session_(spdy_session) {} | |
| 630 SpdySession::UnclaimedPushedStreamContainer::~UnclaimedPushedStreamContainer() { | |
| 631 } | |
| 632 | |
| 633 size_t SpdySession::UnclaimedPushedStreamContainer::erase(const GURL& url) { | |
| 634 const_iterator it = find(url); | |
| 635 if (it != end()) { | |
| 636 streams_.erase(it); | |
| 637 return 1; | |
| 638 } | |
| 639 return 0; | |
| 640 } | |
| 641 | |
| 642 SpdySession::UnclaimedPushedStreamContainer::iterator | |
| 643 SpdySession::UnclaimedPushedStreamContainer::erase(const_iterator it) { | |
| 644 DCHECK(spdy_session_->pool_); | |
| 645 DCHECK(it != end()); | |
| 646 // Only allow cross-origin push for secure resources. | |
| 647 if (it->first.SchemeIsCryptographic()) { | |
| 648 spdy_session_->pool_->UnregisterUnclaimedPushedStream(it->first, | |
| 649 spdy_session_); | |
| 650 } | |
| 651 return streams_.erase(it); | |
| 652 } | |
| 653 | |
| 654 SpdySession::UnclaimedPushedStreamContainer::iterator | |
| 655 SpdySession::UnclaimedPushedStreamContainer::insert( | |
| 656 const_iterator position, | |
| 657 const GURL& url, | |
| 658 SpdyStreamId stream_id, | |
| 659 const base::TimeTicks& creation_time) { | |
| 660 DCHECK(spdy_session_->pool_); | |
| 661 // Only allow cross-origin push for https resources. | |
| 662 if (url.SchemeIsCryptographic()) { | |
| 663 spdy_session_->pool_->RegisterUnclaimedPushedStream( | |
| 664 url, spdy_session_->GetWeakPtr()); | |
| 665 } | |
| 666 return streams_.insert( | |
| 667 position, | |
| 668 std::make_pair( | |
| 669 url, SpdySession::UnclaimedPushedStreamContainer::PushedStreamInfo( | |
| 670 stream_id, creation_time))); | |
| 671 } | |
| 672 | |
| 673 size_t SpdySession::UnclaimedPushedStreamContainer::EstimateMemoryUsage() | |
| 674 const { | |
| 675 return SpdyEstimateMemoryUsage(streams_); | |
| 676 } | |
| 677 | |
| 678 // static | |
| 679 bool SpdySession::CanPool(TransportSecurityState* transport_security_state, | |
| 680 const SSLInfo& ssl_info, | |
| 681 const SpdyString& old_hostname, | |
| 682 const SpdyString& new_hostname) { | |
| 683 // Pooling is prohibited if the server cert is not valid for the new domain, | |
| 684 // and for connections on which client certs were sent. It is also prohibited | |
| 685 // when channel ID was sent if the hosts are from different eTLDs+1. | |
| 686 if (IsCertStatusError(ssl_info.cert_status)) | |
| 687 return false; | |
| 688 | |
| 689 if (ssl_info.client_cert_sent) | |
| 690 return false; | |
| 691 | |
| 692 if (ssl_info.channel_id_sent && | |
| 693 ChannelIDService::GetDomainForHost(new_hostname) != | |
| 694 ChannelIDService::GetDomainForHost(old_hostname)) { | |
| 695 return false; | |
| 696 } | |
| 697 | |
| 698 if (!ssl_info.cert->VerifyNameMatch(new_hostname, false)) | |
| 699 return false; | |
| 700 | |
| 701 SpdyString pinning_failure_log; | |
| 702 // DISABLE_PIN_REPORTS is set here because this check can fail in | |
| 703 // normal operation without being indicative of a misconfiguration or | |
| 704 // attack. Port is left at 0 as it is never used. | |
| 705 if (transport_security_state->CheckPublicKeyPins( | |
| 706 HostPortPair(new_hostname, 0), ssl_info.is_issued_by_known_root, | |
| 707 ssl_info.public_key_hashes, ssl_info.unverified_cert.get(), | |
| 708 ssl_info.cert.get(), TransportSecurityState::DISABLE_PIN_REPORTS, | |
| 709 &pinning_failure_log) == | |
| 710 TransportSecurityState::PKPStatus::VIOLATED) { | |
| 711 return false; | |
| 712 } | |
| 713 | |
| 714 if (ssl_info.ct_cert_policy_compliance != | |
| 715 ct::CertPolicyCompliance::CERT_POLICY_COMPLIES_VIA_SCTS && | |
| 716 ssl_info.ct_cert_policy_compliance != | |
| 717 ct::CertPolicyCompliance::CERT_POLICY_BUILD_NOT_TIMELY && | |
| 718 transport_security_state->ShouldRequireCT( | |
| 719 new_hostname, ssl_info.cert.get(), ssl_info.public_key_hashes)) { | |
| 720 return false; | |
| 721 } | |
| 722 | |
| 723 return true; | |
| 724 } | |
| 725 | |
| 726 SpdySession::SpdySession(const SpdySessionKey& spdy_session_key, | |
| 727 HttpServerProperties* http_server_properties, | |
| 728 TransportSecurityState* transport_security_state, | |
| 729 bool enable_sending_initial_data, | |
| 730 bool enable_ping_based_connection_checking, | |
| 731 size_t session_max_recv_window_size, | |
| 732 const SettingsMap& initial_settings, | |
| 733 TimeFunc time_func, | |
| 734 ServerPushDelegate* push_delegate, | |
| 735 ProxyDelegate* proxy_delegate, | |
| 736 NetLog* net_log) | |
| 737 : in_io_loop_(false), | |
| 738 spdy_session_key_(spdy_session_key), | |
| 739 pool_(NULL), | |
| 740 http_server_properties_(http_server_properties), | |
| 741 transport_security_state_(transport_security_state), | |
| 742 stream_hi_water_mark_(kFirstStreamId), | |
| 743 last_accepted_push_stream_id_(0), | |
| 744 unclaimed_pushed_streams_(this), | |
| 745 push_delegate_(push_delegate), | |
| 746 num_pushed_streams_(0u), | |
| 747 num_active_pushed_streams_(0u), | |
| 748 bytes_pushed_count_(0u), | |
| 749 bytes_pushed_and_unclaimed_count_(0u), | |
| 750 in_flight_write_frame_type_(SpdyFrameType::DATA), | |
| 751 in_flight_write_frame_size_(0), | |
| 752 is_secure_(false), | |
| 753 availability_state_(STATE_AVAILABLE), | |
| 754 read_state_(READ_STATE_DO_READ), | |
| 755 write_state_(WRITE_STATE_IDLE), | |
| 756 error_on_close_(OK), | |
| 757 initial_settings_(initial_settings), | |
| 758 max_concurrent_streams_(kInitialMaxConcurrentStreams), | |
| 759 max_concurrent_pushed_streams_( | |
| 760 initial_settings.at(SETTINGS_MAX_CONCURRENT_STREAMS)), | |
| 761 streams_initiated_count_(0), | |
| 762 streams_pushed_count_(0), | |
| 763 streams_pushed_and_claimed_count_(0), | |
| 764 streams_abandoned_count_(0), | |
| 765 pings_in_flight_(0), | |
| 766 next_ping_id_(1), | |
| 767 last_activity_time_(time_func()), | |
| 768 last_compressed_frame_len_(0), | |
| 769 check_ping_status_pending_(false), | |
| 770 session_send_window_size_(0), | |
| 771 session_max_recv_window_size_(session_max_recv_window_size), | |
| 772 session_recv_window_size_(0), | |
| 773 session_unacked_recv_window_bytes_(0), | |
| 774 stream_initial_send_window_size_(kDefaultInitialWindowSize), | |
| 775 max_header_table_size_(initial_settings.at(SETTINGS_HEADER_TABLE_SIZE)), | |
| 776 stream_max_recv_window_size_( | |
| 777 initial_settings.at(SETTINGS_INITIAL_WINDOW_SIZE)), | |
| 778 net_log_( | |
| 779 NetLogWithSource::Make(net_log, NetLogSourceType::HTTP2_SESSION)), | |
| 780 enable_sending_initial_data_(enable_sending_initial_data), | |
| 781 enable_ping_based_connection_checking_( | |
| 782 enable_ping_based_connection_checking), | |
| 783 connection_at_risk_of_loss_time_( | |
| 784 base::TimeDelta::FromSeconds(kDefaultConnectionAtRiskOfLossSeconds)), | |
| 785 hung_interval_(base::TimeDelta::FromSeconds(kHungIntervalSeconds)), | |
| 786 proxy_delegate_(proxy_delegate), | |
| 787 time_func_(time_func), | |
| 788 weak_factory_(this) { | |
| 789 net_log_.BeginEvent( | |
| 790 NetLogEventType::HTTP2_SESSION, | |
| 791 base::Bind(&NetLogSpdySessionCallback, &host_port_proxy_pair())); | |
| 792 next_unclaimed_push_stream_sweep_time_ = | |
| 793 time_func_() + | |
| 794 base::TimeDelta::FromSeconds(kMinPushedStreamLifetimeSeconds); | |
| 795 | |
| 796 DCHECK(base::ContainsKey(initial_settings_, SETTINGS_HEADER_TABLE_SIZE)); | |
| 797 DCHECK(base::ContainsKey(initial_settings_, SETTINGS_MAX_CONCURRENT_STREAMS)); | |
| 798 DCHECK(base::ContainsKey(initial_settings_, SETTINGS_INITIAL_WINDOW_SIZE)); | |
| 799 | |
| 800 // TODO(mbelshe): consider randomization of the stream_hi_water_mark. | |
| 801 } | |
| 802 | |
| 803 SpdySession::~SpdySession() { | |
| 804 CHECK(!in_io_loop_); | |
| 805 DcheckDraining(); | |
| 806 | |
| 807 // TODO(akalin): Check connection->is_initialized() instead. This | |
| 808 // requires re-working CreateFakeSpdySession(), though. | |
| 809 DCHECK(connection_->socket()); | |
| 810 // With SPDY we can't recycle sockets. | |
| 811 connection_->socket()->Disconnect(); | |
| 812 | |
| 813 RecordHistograms(); | |
| 814 | |
| 815 net_log_.EndEvent(NetLogEventType::HTTP2_SESSION); | |
| 816 } | |
| 817 | |
| 818 int SpdySession::GetPushStream(const GURL& url, | |
| 819 RequestPriority priority, | |
| 820 SpdyStream** stream, | |
| 821 const NetLogWithSource& stream_net_log) { | |
| 822 CHECK(!in_io_loop_); | |
| 823 | |
| 824 if (availability_state_ == STATE_DRAINING) { | |
| 825 *stream = nullptr; | |
| 826 return ERR_CONNECTION_CLOSED; | |
| 827 } | |
| 828 | |
| 829 *stream = GetActivePushStream(url); | |
| 830 if (*stream) { | |
| 831 DCHECK_LT(streams_pushed_and_claimed_count_, streams_pushed_count_); | |
| 832 streams_pushed_and_claimed_count_++; | |
| 833 | |
| 834 // If the stream is still open, update its priority to match | |
| 835 // the priority of the matching request. | |
| 836 if (!(*stream)->IsClosed() && (*stream)->priority() != priority) { | |
| 837 (*stream)->set_priority(priority); | |
| 838 | |
| 839 // Send PRIORITY updates. | |
| 840 auto updates = priority_dependency_state_.OnStreamUpdate( | |
| 841 (*stream)->stream_id(), | |
| 842 ConvertRequestPriorityToSpdyPriority(priority)); | |
| 843 for (auto u : updates) { | |
| 844 ActiveStreamMap::iterator it = active_streams_.find(u.id); | |
| 845 DCHECK(it != active_streams_.end()); | |
| 846 int weight = Spdy3PriorityToHttp2Weight( | |
| 847 ConvertRequestPriorityToSpdyPriority(it->second->priority())); | |
| 848 EnqueuePriorityFrame(u.id, u.dependent_stream_id, weight, u.exclusive); | |
| 849 } | |
| 850 } | |
| 851 } | |
| 852 | |
| 853 return OK; | |
| 854 } | |
| 855 | |
| 856 void SpdySession::CancelPush(const GURL& url) { | |
| 857 UnclaimedPushedStreamContainer::const_iterator unclaimed_it = | |
| 858 unclaimed_pushed_streams_.find(url); | |
| 859 if (unclaimed_it == unclaimed_pushed_streams_.end()) | |
| 860 return; | |
| 861 | |
| 862 SpdyStreamId stream_id = unclaimed_it->second.stream_id; | |
| 863 | |
| 864 if (active_streams_.find(stream_id) == active_streams_.end()) { | |
| 865 ResetStream(stream_id, ERROR_CODE_CANCEL, | |
| 866 "Cancelled push stream with url: " + url.spec()); | |
| 867 } | |
| 868 unclaimed_pushed_streams_.erase(unclaimed_it); | |
| 869 } | |
| 870 | |
| 871 void SpdySession::InitializeWithSocket( | |
| 872 std::unique_ptr<ClientSocketHandle> connection, | |
| 873 SpdySessionPool* pool, | |
| 874 bool is_secure) { | |
| 875 CHECK(!in_io_loop_); | |
| 876 DCHECK_EQ(availability_state_, STATE_AVAILABLE); | |
| 877 DCHECK_EQ(read_state_, READ_STATE_DO_READ); | |
| 878 DCHECK_EQ(write_state_, WRITE_STATE_IDLE); | |
| 879 DCHECK(!connection_); | |
| 880 | |
| 881 // TODO(akalin): Check connection->is_initialized() instead. This | |
| 882 // requires re-working CreateFakeSpdySession(), though. | |
| 883 DCHECK(connection->socket()); | |
| 884 | |
| 885 connection_ = std::move(connection); | |
| 886 is_secure_ = is_secure; | |
| 887 | |
| 888 session_send_window_size_ = kDefaultInitialWindowSize; | |
| 889 session_recv_window_size_ = kDefaultInitialWindowSize; | |
| 890 | |
| 891 buffered_spdy_framer_.reset(new BufferedSpdyFramer()); | |
| 892 buffered_spdy_framer_->set_visitor(this); | |
| 893 buffered_spdy_framer_->set_debug_visitor(this); | |
| 894 buffered_spdy_framer_->UpdateHeaderDecoderTableSize(max_header_table_size_); | |
| 895 | |
| 896 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_INITIALIZED, | |
| 897 base::Bind(&NetLogSpdyInitializedCallback, | |
| 898 connection_->socket()->NetLog().source())); | |
| 899 | |
| 900 DCHECK_EQ(availability_state_, STATE_AVAILABLE); | |
| 901 connection_->AddHigherLayeredPool(this); | |
| 902 if (enable_sending_initial_data_) | |
| 903 SendInitialData(); | |
| 904 pool_ = pool; | |
| 905 | |
| 906 // Bootstrap the read loop. | |
| 907 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 908 FROM_HERE, | |
| 909 base::Bind(&SpdySession::PumpReadLoop, weak_factory_.GetWeakPtr(), | |
| 910 READ_STATE_DO_READ, OK)); | |
| 911 } | |
| 912 | |
| 913 bool SpdySession::VerifyDomainAuthentication(const SpdyString& domain) { | |
| 914 if (availability_state_ == STATE_DRAINING) | |
| 915 return false; | |
| 916 | |
| 917 SSLInfo ssl_info; | |
| 918 if (!GetSSLInfo(&ssl_info)) | |
| 919 return true; // This is not a secure session, so all domains are okay. | |
| 920 | |
| 921 return CanPool(transport_security_state_, ssl_info, host_port_pair().host(), | |
| 922 domain); | |
| 923 } | |
| 924 | |
| 925 void SpdySession::EnqueueStreamWrite( | |
| 926 const base::WeakPtr<SpdyStream>& stream, | |
| 927 SpdyFrameType frame_type, | |
| 928 std::unique_ptr<SpdyBufferProducer> producer) { | |
| 929 DCHECK(frame_type == SpdyFrameType::HEADERS || | |
| 930 frame_type == SpdyFrameType::DATA); | |
| 931 EnqueueWrite(stream->priority(), frame_type, std::move(producer), stream); | |
| 932 } | |
| 933 | |
| 934 std::unique_ptr<SpdySerializedFrame> SpdySession::CreateHeaders( | |
| 935 SpdyStreamId stream_id, | |
| 936 RequestPriority priority, | |
| 937 SpdyControlFlags flags, | |
| 938 SpdyHeaderBlock block, | |
| 939 NetLogSource source_dependency) { | |
| 940 ActiveStreamMap::const_iterator it = active_streams_.find(stream_id); | |
| 941 CHECK(it != active_streams_.end()); | |
| 942 CHECK_EQ(it->second->stream_id(), stream_id); | |
| 943 | |
| 944 SendPrefacePingIfNoneInFlight(); | |
| 945 | |
| 946 DCHECK(buffered_spdy_framer_.get()); | |
| 947 SpdyPriority spdy_priority = ConvertRequestPriorityToSpdyPriority(priority); | |
| 948 | |
| 949 std::unique_ptr<SpdySerializedFrame> syn_frame; | |
| 950 bool has_priority = true; | |
| 951 int weight = Spdy3PriorityToHttp2Weight(spdy_priority); | |
| 952 SpdyStreamId dependent_stream_id = 0; | |
| 953 bool exclusive = false; | |
| 954 | |
| 955 priority_dependency_state_.OnStreamCreation(stream_id, spdy_priority, | |
| 956 &dependent_stream_id, &exclusive); | |
| 957 | |
| 958 if (net_log().IsCapturing()) { | |
| 959 net_log().AddEvent( | |
| 960 NetLogEventType::HTTP2_SESSION_SEND_HEADERS, | |
| 961 base::Bind(&NetLogSpdyHeadersSentCallback, &block, | |
| 962 (flags & CONTROL_FLAG_FIN) != 0, stream_id, has_priority, | |
| 963 weight, dependent_stream_id, exclusive, source_dependency)); | |
| 964 } | |
| 965 | |
| 966 SpdyHeadersIR headers(stream_id, std::move(block)); | |
| 967 headers.set_has_priority(has_priority); | |
| 968 headers.set_weight(weight); | |
| 969 headers.set_parent_stream_id(dependent_stream_id); | |
| 970 headers.set_exclusive(exclusive); | |
| 971 headers.set_fin((flags & CONTROL_FLAG_FIN) != 0); | |
| 972 syn_frame.reset( | |
| 973 new SpdySerializedFrame(buffered_spdy_framer_->SerializeFrame(headers))); | |
| 974 | |
| 975 streams_initiated_count_++; | |
| 976 | |
| 977 return syn_frame; | |
| 978 } | |
| 979 | |
| 980 std::unique_ptr<SpdyBuffer> SpdySession::CreateDataBuffer( | |
| 981 SpdyStreamId stream_id, | |
| 982 IOBuffer* data, | |
| 983 int len, | |
| 984 SpdyDataFlags flags) { | |
| 985 if (availability_state_ == STATE_DRAINING) { | |
| 986 return std::unique_ptr<SpdyBuffer>(); | |
| 987 } | |
| 988 | |
| 989 ActiveStreamMap::const_iterator it = active_streams_.find(stream_id); | |
| 990 CHECK(it != active_streams_.end()); | |
| 991 SpdyStream* stream = it->second; | |
| 992 CHECK_EQ(stream->stream_id(), stream_id); | |
| 993 | |
| 994 if (len < 0) { | |
| 995 NOTREACHED(); | |
| 996 return std::unique_ptr<SpdyBuffer>(); | |
| 997 } | |
| 998 | |
| 999 int effective_len = std::min(len, kMaxSpdyFrameChunkSize); | |
| 1000 | |
| 1001 bool send_stalled_by_stream = (stream->send_window_size() <= 0); | |
| 1002 bool send_stalled_by_session = IsSendStalled(); | |
| 1003 | |
| 1004 // NOTE: There's an enum of the same name in histograms.xml. | |
| 1005 enum SpdyFrameFlowControlState { | |
| 1006 SEND_NOT_STALLED, | |
| 1007 SEND_STALLED_BY_STREAM, | |
| 1008 SEND_STALLED_BY_SESSION, | |
| 1009 SEND_STALLED_BY_STREAM_AND_SESSION, | |
| 1010 }; | |
| 1011 | |
| 1012 SpdyFrameFlowControlState frame_flow_control_state = SEND_NOT_STALLED; | |
| 1013 if (send_stalled_by_stream) { | |
| 1014 if (send_stalled_by_session) { | |
| 1015 frame_flow_control_state = SEND_STALLED_BY_STREAM_AND_SESSION; | |
| 1016 } else { | |
| 1017 frame_flow_control_state = SEND_STALLED_BY_STREAM; | |
| 1018 } | |
| 1019 } else if (send_stalled_by_session) { | |
| 1020 frame_flow_control_state = SEND_STALLED_BY_SESSION; | |
| 1021 } | |
| 1022 | |
| 1023 UMA_HISTOGRAM_ENUMERATION("Net.SpdyFrameStreamAndSessionFlowControlState", | |
| 1024 frame_flow_control_state, | |
| 1025 SEND_STALLED_BY_STREAM_AND_SESSION + 1); | |
| 1026 | |
| 1027 // Obey send window size of the stream. | |
| 1028 if (send_stalled_by_stream) { | |
| 1029 stream->set_send_stalled_by_flow_control(true); | |
| 1030 // Even though we're currently stalled only by the stream, we | |
| 1031 // might end up being stalled by the session also. | |
| 1032 QueueSendStalledStream(*stream); | |
| 1033 net_log().AddEvent( | |
| 1034 NetLogEventType::HTTP2_SESSION_STREAM_STALLED_BY_STREAM_SEND_WINDOW, | |
| 1035 NetLog::IntCallback("stream_id", stream_id)); | |
| 1036 return std::unique_ptr<SpdyBuffer>(); | |
| 1037 } | |
| 1038 | |
| 1039 effective_len = std::min(effective_len, stream->send_window_size()); | |
| 1040 | |
| 1041 // Obey send window size of the session. | |
| 1042 if (send_stalled_by_session) { | |
| 1043 stream->set_send_stalled_by_flow_control(true); | |
| 1044 QueueSendStalledStream(*stream); | |
| 1045 net_log().AddEvent( | |
| 1046 NetLogEventType::HTTP2_SESSION_STREAM_STALLED_BY_SESSION_SEND_WINDOW, | |
| 1047 NetLog::IntCallback("stream_id", stream_id)); | |
| 1048 return std::unique_ptr<SpdyBuffer>(); | |
| 1049 } | |
| 1050 | |
| 1051 effective_len = std::min(effective_len, session_send_window_size_); | |
| 1052 | |
| 1053 DCHECK_GE(effective_len, 0); | |
| 1054 | |
| 1055 // Clear FIN flag if only some of the data will be in the data | |
| 1056 // frame. | |
| 1057 if (effective_len < len) | |
| 1058 flags = static_cast<SpdyDataFlags>(flags & ~DATA_FLAG_FIN); | |
| 1059 | |
| 1060 if (net_log().IsCapturing()) { | |
| 1061 net_log().AddEvent(NetLogEventType::HTTP2_SESSION_SEND_DATA, | |
| 1062 base::Bind(&NetLogSpdyDataCallback, stream_id, | |
| 1063 effective_len, (flags & DATA_FLAG_FIN) != 0)); | |
| 1064 } | |
| 1065 | |
| 1066 // Send PrefacePing for DATA_FRAMEs with nonzero payload size. | |
| 1067 if (effective_len > 0) | |
| 1068 SendPrefacePingIfNoneInFlight(); | |
| 1069 | |
| 1070 // TODO(mbelshe): reduce memory copies here. | |
| 1071 DCHECK(buffered_spdy_framer_.get()); | |
| 1072 std::unique_ptr<SpdySerializedFrame> frame( | |
| 1073 buffered_spdy_framer_->CreateDataFrame( | |
| 1074 stream_id, data->data(), static_cast<uint32_t>(effective_len), | |
| 1075 flags)); | |
| 1076 | |
| 1077 std::unique_ptr<SpdyBuffer> data_buffer(new SpdyBuffer(std::move(frame))); | |
| 1078 | |
| 1079 // Send window size is based on payload size, so nothing to do if this is | |
| 1080 // just a FIN with no payload. | |
| 1081 if (effective_len != 0) { | |
| 1082 DecreaseSendWindowSize(static_cast<int32_t>(effective_len)); | |
| 1083 data_buffer->AddConsumeCallback(base::Bind( | |
| 1084 &SpdySession::OnWriteBufferConsumed, weak_factory_.GetWeakPtr(), | |
| 1085 static_cast<size_t>(effective_len))); | |
| 1086 } | |
| 1087 | |
| 1088 return data_buffer; | |
| 1089 } | |
| 1090 | |
| 1091 void SpdySession::CloseActiveStream(SpdyStreamId stream_id, int status) { | |
| 1092 DCHECK_NE(stream_id, 0u); | |
| 1093 | |
| 1094 ActiveStreamMap::iterator it = active_streams_.find(stream_id); | |
| 1095 if (it == active_streams_.end()) { | |
| 1096 NOTREACHED(); | |
| 1097 return; | |
| 1098 } | |
| 1099 | |
| 1100 CloseActiveStreamIterator(it, status); | |
| 1101 } | |
| 1102 | |
| 1103 void SpdySession::CloseCreatedStream(const base::WeakPtr<SpdyStream>& stream, | |
| 1104 int status) { | |
| 1105 DCHECK_EQ(stream->stream_id(), 0u); | |
| 1106 | |
| 1107 CreatedStreamSet::iterator it = created_streams_.find(stream.get()); | |
| 1108 if (it == created_streams_.end()) { | |
| 1109 NOTREACHED(); | |
| 1110 return; | |
| 1111 } | |
| 1112 | |
| 1113 CloseCreatedStreamIterator(it, status); | |
| 1114 } | |
| 1115 | |
| 1116 void SpdySession::ResetStream(SpdyStreamId stream_id, | |
| 1117 SpdyErrorCode error_code, | |
| 1118 const SpdyString& description) { | |
| 1119 DCHECK_NE(stream_id, 0u); | |
| 1120 | |
| 1121 ActiveStreamMap::iterator it = active_streams_.find(stream_id); | |
| 1122 if (it == active_streams_.end()) { | |
| 1123 NOTREACHED(); | |
| 1124 return; | |
| 1125 } | |
| 1126 | |
| 1127 ResetStreamIterator(it, error_code, description); | |
| 1128 } | |
| 1129 | |
| 1130 bool SpdySession::IsStreamActive(SpdyStreamId stream_id) const { | |
| 1131 return base::ContainsKey(active_streams_, stream_id); | |
| 1132 } | |
| 1133 | |
| 1134 LoadState SpdySession::GetLoadState() const { | |
| 1135 // Just report that we're idle since the session could be doing | |
| 1136 // many things concurrently. | |
| 1137 return LOAD_STATE_IDLE; | |
| 1138 } | |
| 1139 | |
| 1140 url::SchemeHostPort SpdySession::GetServer() { | |
| 1141 return url::SchemeHostPort(is_secure_ ? "https" : "http", | |
| 1142 host_port_pair().host(), host_port_pair().port()); | |
| 1143 } | |
| 1144 | |
| 1145 bool SpdySession::GetRemoteEndpoint(IPEndPoint* endpoint) { | |
| 1146 return GetPeerAddress(endpoint) == OK; | |
| 1147 } | |
| 1148 | |
| 1149 bool SpdySession::GetSSLInfo(SSLInfo* ssl_info) const { | |
| 1150 return connection_->socket()->GetSSLInfo(ssl_info); | |
| 1151 } | |
| 1152 | |
| 1153 Error SpdySession::GetTokenBindingSignature(crypto::ECPrivateKey* key, | |
| 1154 TokenBindingType tb_type, | |
| 1155 std::vector<uint8_t>* out) { | |
| 1156 if (!is_secure_) { | |
| 1157 NOTREACHED(); | |
| 1158 return ERR_FAILED; | |
| 1159 } | |
| 1160 SSLClientSocket* ssl_socket = | |
| 1161 static_cast<SSLClientSocket*>(connection_->socket()); | |
| 1162 return ssl_socket->GetTokenBindingSignature(key, tb_type, out); | |
| 1163 } | |
| 1164 | |
| 1165 bool SpdySession::WasAlpnNegotiated() const { | |
| 1166 return connection_->socket()->WasAlpnNegotiated(); | |
| 1167 } | |
| 1168 | |
| 1169 NextProto SpdySession::GetNegotiatedProtocol() const { | |
| 1170 return connection_->socket()->GetNegotiatedProtocol(); | |
| 1171 } | |
| 1172 | |
| 1173 void SpdySession::SendStreamWindowUpdate(SpdyStreamId stream_id, | |
| 1174 uint32_t delta_window_size) { | |
| 1175 ActiveStreamMap::const_iterator it = active_streams_.find(stream_id); | |
| 1176 CHECK(it != active_streams_.end()); | |
| 1177 CHECK_EQ(it->second->stream_id(), stream_id); | |
| 1178 SendWindowUpdateFrame(stream_id, delta_window_size, it->second->priority()); | |
| 1179 } | |
| 1180 | |
| 1181 void SpdySession::CloseSessionOnError(Error err, | |
| 1182 const SpdyString& description) { | |
| 1183 DCHECK_LT(err, ERR_IO_PENDING); | |
| 1184 DoDrainSession(err, description); | |
| 1185 } | |
| 1186 | |
| 1187 void SpdySession::MakeUnavailable() { | |
| 1188 if (availability_state_ == STATE_AVAILABLE) { | |
| 1189 availability_state_ = STATE_GOING_AWAY; | |
| 1190 pool_->MakeSessionUnavailable(GetWeakPtr()); | |
| 1191 } | |
| 1192 } | |
| 1193 | |
| 1194 void SpdySession::StartGoingAway(SpdyStreamId last_good_stream_id, | |
| 1195 Error status) { | |
| 1196 DCHECK_GE(availability_state_, STATE_GOING_AWAY); | |
| 1197 | |
| 1198 // The loops below are carefully written to avoid reentrancy problems. | |
| 1199 | |
| 1200 while (true) { | |
| 1201 size_t old_size = GetTotalSize(pending_create_stream_queues_); | |
| 1202 base::WeakPtr<SpdyStreamRequest> pending_request = | |
| 1203 GetNextPendingStreamRequest(); | |
| 1204 if (!pending_request) | |
| 1205 break; | |
| 1206 // No new stream requests should be added while the session is | |
| 1207 // going away. | |
| 1208 DCHECK_GT(old_size, GetTotalSize(pending_create_stream_queues_)); | |
| 1209 pending_request->OnRequestCompleteFailure(ERR_ABORTED); | |
| 1210 } | |
| 1211 | |
| 1212 while (true) { | |
| 1213 size_t old_size = active_streams_.size(); | |
| 1214 ActiveStreamMap::iterator it = | |
| 1215 active_streams_.lower_bound(last_good_stream_id + 1); | |
| 1216 if (it == active_streams_.end()) | |
| 1217 break; | |
| 1218 LogAbandonedActiveStream(it, status); | |
| 1219 CloseActiveStreamIterator(it, status); | |
| 1220 // No new streams should be activated while the session is going | |
| 1221 // away. | |
| 1222 DCHECK_GT(old_size, active_streams_.size()); | |
| 1223 } | |
| 1224 | |
| 1225 while (!created_streams_.empty()) { | |
| 1226 size_t old_size = created_streams_.size(); | |
| 1227 CreatedStreamSet::iterator it = created_streams_.begin(); | |
| 1228 LogAbandonedStream(*it, status); | |
| 1229 CloseCreatedStreamIterator(it, status); | |
| 1230 // No new streams should be created while the session is going | |
| 1231 // away. | |
| 1232 DCHECK_GT(old_size, created_streams_.size()); | |
| 1233 } | |
| 1234 | |
| 1235 write_queue_.RemovePendingWritesForStreamsAfter(last_good_stream_id); | |
| 1236 | |
| 1237 DcheckGoingAway(); | |
| 1238 MaybeFinishGoingAway(); | |
| 1239 } | |
| 1240 | |
| 1241 void SpdySession::MaybeFinishGoingAway() { | |
| 1242 if (active_streams_.empty() && created_streams_.empty() && | |
| 1243 availability_state_ == STATE_GOING_AWAY) { | |
| 1244 DoDrainSession(OK, "Finished going away"); | |
| 1245 } | |
| 1246 } | |
| 1247 | |
| 1248 std::unique_ptr<base::Value> SpdySession::GetInfoAsValue() const { | |
| 1249 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); | |
| 1250 | |
| 1251 dict->SetInteger("source_id", net_log_.source().id); | |
| 1252 | |
| 1253 dict->SetString("host_port_pair", host_port_pair().ToString()); | |
| 1254 if (!pooled_aliases_.empty()) { | |
| 1255 std::unique_ptr<base::ListValue> alias_list(new base::ListValue()); | |
| 1256 for (const auto& alias : pooled_aliases_) { | |
| 1257 alias_list->AppendString(alias.host_port_pair().ToString()); | |
| 1258 } | |
| 1259 dict->Set("aliases", std::move(alias_list)); | |
| 1260 } | |
| 1261 dict->SetString("proxy", host_port_proxy_pair().second.ToURI()); | |
| 1262 | |
| 1263 dict->SetInteger("active_streams", active_streams_.size()); | |
| 1264 | |
| 1265 dict->SetInteger("unclaimed_pushed_streams", | |
| 1266 unclaimed_pushed_streams_.size()); | |
| 1267 | |
| 1268 dict->SetBoolean("is_secure", is_secure_); | |
| 1269 | |
| 1270 dict->SetString( | |
| 1271 "negotiated_protocol", | |
| 1272 NextProtoToString(connection_->socket()->GetNegotiatedProtocol())); | |
| 1273 | |
| 1274 dict->SetInteger("error", error_on_close_); | |
| 1275 dict->SetInteger("max_concurrent_streams", max_concurrent_streams_); | |
| 1276 | |
| 1277 dict->SetInteger("streams_initiated_count", streams_initiated_count_); | |
| 1278 dict->SetInteger("streams_pushed_count", streams_pushed_count_); | |
| 1279 dict->SetInteger("streams_pushed_and_claimed_count", | |
| 1280 streams_pushed_and_claimed_count_); | |
| 1281 dict->SetInteger("streams_abandoned_count", streams_abandoned_count_); | |
| 1282 DCHECK(buffered_spdy_framer_.get()); | |
| 1283 dict->SetInteger("frames_received", buffered_spdy_framer_->frames_received()); | |
| 1284 | |
| 1285 dict->SetInteger("send_window_size", session_send_window_size_); | |
| 1286 dict->SetInteger("recv_window_size", session_recv_window_size_); | |
| 1287 dict->SetInteger("unacked_recv_window_bytes", | |
| 1288 session_unacked_recv_window_bytes_); | |
| 1289 return std::move(dict); | |
| 1290 } | |
| 1291 | |
| 1292 bool SpdySession::IsReused() const { | |
| 1293 return buffered_spdy_framer_->frames_received() > 0 || | |
| 1294 connection_->reuse_type() == ClientSocketHandle::UNUSED_IDLE; | |
| 1295 } | |
| 1296 | |
| 1297 bool SpdySession::GetLoadTimingInfo(SpdyStreamId stream_id, | |
| 1298 LoadTimingInfo* load_timing_info) const { | |
| 1299 return connection_->GetLoadTimingInfo(stream_id != kFirstStreamId, | |
| 1300 load_timing_info); | |
| 1301 } | |
| 1302 | |
| 1303 size_t SpdySession::num_unclaimed_pushed_streams() const { | |
| 1304 return unclaimed_pushed_streams_.size(); | |
| 1305 } | |
| 1306 | |
| 1307 size_t SpdySession::count_unclaimed_pushed_streams_for_url( | |
| 1308 const GURL& url) const { | |
| 1309 return unclaimed_pushed_streams_.count(url); | |
| 1310 } | |
| 1311 | |
| 1312 int SpdySession::GetPeerAddress(IPEndPoint* address) const { | |
| 1313 if (connection_->socket()) | |
| 1314 return connection_->socket()->GetPeerAddress(address); | |
| 1315 | |
| 1316 return ERR_SOCKET_NOT_CONNECTED; | |
| 1317 } | |
| 1318 | |
| 1319 int SpdySession::GetLocalAddress(IPEndPoint* address) const { | |
| 1320 if (connection_->socket()) | |
| 1321 return connection_->socket()->GetLocalAddress(address); | |
| 1322 | |
| 1323 return ERR_SOCKET_NOT_CONNECTED; | |
| 1324 } | |
| 1325 | |
| 1326 void SpdySession::AddPooledAlias(const SpdySessionKey& alias_key) { | |
| 1327 pooled_aliases_.insert(alias_key); | |
| 1328 } | |
| 1329 | |
| 1330 void SpdySession::RemovePooledAlias(const SpdySessionKey& alias_key) { | |
| 1331 pooled_aliases_.erase(alias_key); | |
| 1332 } | |
| 1333 | |
| 1334 bool SpdySession::HasAcceptableTransportSecurity() const { | |
| 1335 // If we're not even using TLS, we have no standards to meet. | |
| 1336 if (!is_secure_) { | |
| 1337 return true; | |
| 1338 } | |
| 1339 | |
| 1340 SSLInfo ssl_info; | |
| 1341 CHECK(GetSSLInfo(&ssl_info)); | |
| 1342 | |
| 1343 // HTTP/2 requires TLS 1.2+ | |
| 1344 if (SSLConnectionStatusToVersion(ssl_info.connection_status) < | |
| 1345 SSL_CONNECTION_VERSION_TLS1_2) { | |
| 1346 return false; | |
| 1347 } | |
| 1348 | |
| 1349 if (!IsTLSCipherSuiteAllowedByHTTP2( | |
| 1350 SSLConnectionStatusToCipherSuite(ssl_info.connection_status))) { | |
| 1351 return false; | |
| 1352 } | |
| 1353 | |
| 1354 return true; | |
| 1355 } | |
| 1356 | |
| 1357 base::WeakPtr<SpdySession> SpdySession::GetWeakPtr() { | |
| 1358 return weak_factory_.GetWeakPtr(); | |
| 1359 } | |
| 1360 | |
| 1361 bool SpdySession::CloseOneIdleConnection() { | |
| 1362 CHECK(!in_io_loop_); | |
| 1363 DCHECK(pool_); | |
| 1364 if (active_streams_.empty()) { | |
| 1365 DoDrainSession(ERR_CONNECTION_CLOSED, "Closing idle connection."); | |
| 1366 } | |
| 1367 // Return false as the socket wasn't immediately closed. | |
| 1368 return false; | |
| 1369 } | |
| 1370 | |
| 1371 size_t SpdySession::DumpMemoryStats(StreamSocket::SocketMemoryStats* stats, | |
| 1372 bool* is_session_active) const { | |
| 1373 // TODO(xunjieli): Include |pending_create_stream_queues_| when WeakPtr is | |
| 1374 // supported in memory_usage_estimator.h. | |
| 1375 *is_session_active = is_active(); | |
| 1376 connection_->DumpMemoryStats(stats); | |
| 1377 | |
| 1378 // |connection_| is estimated in stats->total_size. |read_buffer_| is | |
| 1379 // estimated in |read_buffer_size|. TODO(xunjieli): Make them use EMU(). | |
| 1380 size_t read_buffer_size = read_buffer_ ? kReadBufferSize : 0; | |
| 1381 return stats->total_size + read_buffer_size + | |
| 1382 SpdyEstimateMemoryUsage(spdy_session_key_) + | |
| 1383 SpdyEstimateMemoryUsage(pooled_aliases_) + | |
| 1384 SpdyEstimateMemoryUsage(active_streams_) + | |
| 1385 SpdyEstimateMemoryUsage(unclaimed_pushed_streams_) + | |
| 1386 SpdyEstimateMemoryUsage(created_streams_) + | |
| 1387 SpdyEstimateMemoryUsage(write_queue_) + | |
| 1388 SpdyEstimateMemoryUsage(in_flight_write_) + | |
| 1389 SpdyEstimateMemoryUsage(buffered_spdy_framer_) + | |
| 1390 SpdyEstimateMemoryUsage(initial_settings_) + | |
| 1391 SpdyEstimateMemoryUsage(stream_send_unstall_queue_) + | |
| 1392 SpdyEstimateMemoryUsage(priority_dependency_state_); | |
| 1393 } | |
| 1394 | |
| 1395 // {,Try}CreateStream() can be called with |in_io_loop_| set if a stream is | |
| 1396 // being created in response to another being closed due to received data. | |
| 1397 | |
| 1398 int SpdySession::TryCreateStream( | |
| 1399 const base::WeakPtr<SpdyStreamRequest>& request, | |
| 1400 base::WeakPtr<SpdyStream>* stream) { | |
| 1401 DCHECK(request); | |
| 1402 | |
| 1403 if (availability_state_ == STATE_GOING_AWAY) | |
| 1404 return ERR_FAILED; | |
| 1405 | |
| 1406 if (availability_state_ == STATE_DRAINING) | |
| 1407 return ERR_CONNECTION_CLOSED; | |
| 1408 | |
| 1409 if ((active_streams_.size() + created_streams_.size() - num_pushed_streams_ < | |
| 1410 max_concurrent_streams_)) { | |
| 1411 return CreateStream(*request, stream); | |
| 1412 } | |
| 1413 | |
| 1414 if (net_log().IsCapturing()) { | |
| 1415 net_log().AddEvent( | |
| 1416 NetLogEventType::HTTP2_SESSION_STALLED_MAX_STREAMS, | |
| 1417 base::Bind(&NetLogSpdySessionStalledCallback, active_streams_.size(), | |
| 1418 created_streams_.size(), num_pushed_streams_, | |
| 1419 max_concurrent_streams_, request->url().spec())); | |
| 1420 } | |
| 1421 RequestPriority priority = request->priority(); | |
| 1422 CHECK_GE(priority, MINIMUM_PRIORITY); | |
| 1423 CHECK_LE(priority, MAXIMUM_PRIORITY); | |
| 1424 pending_create_stream_queues_[priority].push_back(request); | |
| 1425 return ERR_IO_PENDING; | |
| 1426 } | |
| 1427 | |
| 1428 int SpdySession::CreateStream(const SpdyStreamRequest& request, | |
| 1429 base::WeakPtr<SpdyStream>* stream) { | |
| 1430 DCHECK_GE(request.priority(), MINIMUM_PRIORITY); | |
| 1431 DCHECK_LE(request.priority(), MAXIMUM_PRIORITY); | |
| 1432 | |
| 1433 if (availability_state_ == STATE_GOING_AWAY) | |
| 1434 return ERR_FAILED; | |
| 1435 | |
| 1436 if (availability_state_ == STATE_DRAINING) | |
| 1437 return ERR_CONNECTION_CLOSED; | |
| 1438 | |
| 1439 DCHECK(connection_->socket()); | |
| 1440 UMA_HISTOGRAM_BOOLEAN("Net.SpdySession.CreateStreamWithSocketConnected", | |
| 1441 connection_->socket()->IsConnected()); | |
| 1442 if (!connection_->socket()->IsConnected()) { | |
| 1443 DoDrainSession( | |
| 1444 ERR_CONNECTION_CLOSED, | |
| 1445 "Tried to create SPDY stream for a closed socket connection."); | |
| 1446 return ERR_CONNECTION_CLOSED; | |
| 1447 } | |
| 1448 | |
| 1449 std::unique_ptr<SpdyStream> new_stream( | |
| 1450 new SpdyStream(request.type(), GetWeakPtr(), request.url(), | |
| 1451 request.priority(), stream_initial_send_window_size_, | |
| 1452 stream_max_recv_window_size_, request.net_log())); | |
| 1453 *stream = new_stream->GetWeakPtr(); | |
| 1454 InsertCreatedStream(std::move(new_stream)); | |
| 1455 | |
| 1456 return OK; | |
| 1457 } | |
| 1458 | |
| 1459 void SpdySession::CancelStreamRequest( | |
| 1460 const base::WeakPtr<SpdyStreamRequest>& request) { | |
| 1461 DCHECK(request); | |
| 1462 RequestPriority priority = request->priority(); | |
| 1463 CHECK_GE(priority, MINIMUM_PRIORITY); | |
| 1464 CHECK_LE(priority, MAXIMUM_PRIORITY); | |
| 1465 | |
| 1466 #if DCHECK_IS_ON() | |
| 1467 // |request| should not be in a queue not matching its priority. | |
| 1468 for (int i = MINIMUM_PRIORITY; i <= MAXIMUM_PRIORITY; ++i) { | |
| 1469 if (priority == i) | |
| 1470 continue; | |
| 1471 PendingStreamRequestQueue* queue = &pending_create_stream_queues_[i]; | |
| 1472 DCHECK(std::find_if(queue->begin(), queue->end(), RequestEquals(request)) == | |
| 1473 queue->end()); | |
| 1474 } | |
| 1475 #endif | |
| 1476 | |
| 1477 PendingStreamRequestQueue* queue = &pending_create_stream_queues_[priority]; | |
| 1478 // Remove |request| from |queue| while preserving the order of the | |
| 1479 // other elements. | |
| 1480 PendingStreamRequestQueue::iterator it = | |
| 1481 std::find_if(queue->begin(), queue->end(), RequestEquals(request)); | |
| 1482 // The request may already be removed if there's a | |
| 1483 // CompleteStreamRequest() in flight. | |
| 1484 if (it != queue->end()) { | |
| 1485 it = queue->erase(it); | |
| 1486 // |request| should be in the queue at most once, and if it is | |
| 1487 // present, should not be pending completion. | |
| 1488 DCHECK(std::find_if(it, queue->end(), RequestEquals(request)) == | |
| 1489 queue->end()); | |
| 1490 } | |
| 1491 } | |
| 1492 | |
| 1493 base::WeakPtr<SpdyStreamRequest> SpdySession::GetNextPendingStreamRequest() { | |
| 1494 for (int j = MAXIMUM_PRIORITY; j >= MINIMUM_PRIORITY; --j) { | |
| 1495 if (pending_create_stream_queues_[j].empty()) | |
| 1496 continue; | |
| 1497 | |
| 1498 base::WeakPtr<SpdyStreamRequest> pending_request = | |
| 1499 pending_create_stream_queues_[j].front(); | |
| 1500 DCHECK(pending_request); | |
| 1501 pending_create_stream_queues_[j].pop_front(); | |
| 1502 return pending_request; | |
| 1503 } | |
| 1504 return base::WeakPtr<SpdyStreamRequest>(); | |
| 1505 } | |
| 1506 | |
| 1507 void SpdySession::ProcessPendingStreamRequests() { | |
| 1508 size_t max_requests_to_process = | |
| 1509 max_concurrent_streams_ - | |
| 1510 (active_streams_.size() + created_streams_.size()); | |
| 1511 for (size_t i = 0; i < max_requests_to_process; ++i) { | |
| 1512 base::WeakPtr<SpdyStreamRequest> pending_request = | |
| 1513 GetNextPendingStreamRequest(); | |
| 1514 if (!pending_request) | |
| 1515 break; | |
| 1516 | |
| 1517 // Note that this post can race with other stream creations, and it's | |
| 1518 // possible that the un-stalled stream will be stalled again if it loses. | |
| 1519 // TODO(jgraettinger): Provide stronger ordering guarantees. | |
| 1520 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1521 FROM_HERE, base::Bind(&SpdySession::CompleteStreamRequest, | |
| 1522 weak_factory_.GetWeakPtr(), pending_request)); | |
| 1523 } | |
| 1524 } | |
| 1525 | |
| 1526 void SpdySession::TryCreatePushStream(SpdyStreamId stream_id, | |
| 1527 SpdyStreamId associated_stream_id, | |
| 1528 SpdyHeaderBlock headers) { | |
| 1529 // Server-initiated streams should have even sequence numbers. | |
| 1530 if ((stream_id & 0x1) != 0) { | |
| 1531 LOG(WARNING) << "Received invalid push stream id " << stream_id; | |
| 1532 CloseSessionOnError(ERR_SPDY_PROTOCOL_ERROR, "Odd push stream id."); | |
| 1533 return; | |
| 1534 } | |
| 1535 | |
| 1536 // Server-initiated streams must be associated with client-initiated streams. | |
| 1537 if ((associated_stream_id & 0x1) != 1) { | |
| 1538 LOG(WARNING) << "Received push stream id " << stream_id | |
| 1539 << " with invalid associated stream id"; | |
| 1540 CloseSessionOnError(ERR_SPDY_PROTOCOL_ERROR, "Push on even stream id."); | |
| 1541 return; | |
| 1542 } | |
| 1543 | |
| 1544 if (stream_id <= last_accepted_push_stream_id_) { | |
| 1545 LOG(WARNING) << "Received push stream id " << stream_id | |
| 1546 << " lesser or equal to the last accepted before"; | |
| 1547 CloseSessionOnError( | |
| 1548 ERR_SPDY_PROTOCOL_ERROR, | |
| 1549 "New push stream id must be greater than the last accepted."); | |
| 1550 return; | |
| 1551 } | |
| 1552 | |
| 1553 if (IsStreamActive(stream_id)) { | |
| 1554 // We should not get here, we'll start going away earlier on | |
| 1555 // |last_seen_push_stream_id_| check. | |
| 1556 LOG(WARNING) << "Received push for active stream " << stream_id; | |
| 1557 return; | |
| 1558 } | |
| 1559 | |
| 1560 last_accepted_push_stream_id_ = stream_id; | |
| 1561 | |
| 1562 // Pushed streams are speculative, so they start at an IDLE priority. | |
| 1563 const RequestPriority request_priority = IDLE; | |
| 1564 | |
| 1565 if (availability_state_ == STATE_GOING_AWAY) { | |
| 1566 // TODO(akalin): This behavior isn't in the SPDY spec, although it | |
| 1567 // probably should be. | |
| 1568 EnqueueResetStreamFrame(stream_id, request_priority, | |
| 1569 ERROR_CODE_REFUSED_STREAM, | |
| 1570 "push stream request received when going away"); | |
| 1571 return; | |
| 1572 } | |
| 1573 | |
| 1574 if (associated_stream_id == 0) { | |
| 1575 // In HTTP/2 0 stream id in PUSH_PROMISE frame leads to framer error and | |
| 1576 // session going away. We should never get here. | |
| 1577 SpdyString description = SpdyStringPrintf( | |
| 1578 "Received invalid associated stream id %d for pushed stream %d", | |
| 1579 associated_stream_id, stream_id); | |
| 1580 EnqueueResetStreamFrame(stream_id, request_priority, | |
| 1581 ERROR_CODE_REFUSED_STREAM, description); | |
| 1582 return; | |
| 1583 } | |
| 1584 | |
| 1585 streams_pushed_count_++; | |
| 1586 | |
| 1587 // TODO(mbelshe): DCHECK that this is a GET method? | |
| 1588 | |
| 1589 // Verify that the response had a URL for us. | |
| 1590 GURL gurl = GetUrlFromHeaderBlock(headers); | |
| 1591 if (!gurl.is_valid()) { | |
| 1592 EnqueueResetStreamFrame( | |
| 1593 stream_id, request_priority, ERROR_CODE_PROTOCOL_ERROR, | |
| 1594 "Pushed stream url was invalid: " + gurl.possibly_invalid_spec()); | |
| 1595 return; | |
| 1596 } | |
| 1597 | |
| 1598 // Verify we have a valid stream association. | |
| 1599 ActiveStreamMap::iterator associated_it = | |
| 1600 active_streams_.find(associated_stream_id); | |
| 1601 if (associated_it == active_streams_.end()) { | |
| 1602 EnqueueResetStreamFrame( | |
| 1603 stream_id, request_priority, ERROR_CODE_STREAM_CLOSED, | |
| 1604 SpdyStringPrintf("Received push for inactive associated stream %d", | |
| 1605 associated_stream_id)); | |
| 1606 return; | |
| 1607 } | |
| 1608 | |
| 1609 DCHECK(gurl.is_valid()); | |
| 1610 | |
| 1611 // Check that the pushed stream advertises the same origin as its associated | |
| 1612 // stream. Bypass this check if and only if this session is with a SPDY proxy | |
| 1613 // that is trusted explicitly as determined by the |proxy_delegate_| or if the | |
| 1614 // proxy is pushing same-origin resources. | |
| 1615 if (!HostPortPair::FromURL(gurl).Equals(host_port_pair())) { | |
| 1616 if (proxy_delegate_ && | |
| 1617 proxy_delegate_->IsTrustedSpdyProxy( | |
| 1618 ProxyServer(ProxyServer::SCHEME_HTTPS, host_port_pair()))) { | |
| 1619 // Disallow pushing of HTTPS content. | |
| 1620 if (gurl.SchemeIs("https")) { | |
| 1621 EnqueueResetStreamFrame( | |
| 1622 stream_id, request_priority, ERROR_CODE_REFUSED_STREAM, | |
| 1623 SpdyStringPrintf("Rejected push of cross origin HTTPS content %d " | |
| 1624 "from trusted proxy", | |
| 1625 associated_stream_id)); | |
| 1626 return; | |
| 1627 } | |
| 1628 } else { | |
| 1629 GURL associated_url(associated_it->second->GetUrlFromHeaders()); | |
| 1630 if (associated_url.SchemeIs("https")) { | |
| 1631 SSLInfo ssl_info; | |
| 1632 CHECK(GetSSLInfo(&ssl_info)); | |
| 1633 if (!gurl.SchemeIs("https") || | |
| 1634 !CanPool(transport_security_state_, ssl_info, associated_url.host(), | |
| 1635 gurl.host())) { | |
| 1636 EnqueueResetStreamFrame( | |
| 1637 stream_id, request_priority, ERROR_CODE_REFUSED_STREAM, | |
| 1638 SpdyStringPrintf("Rejected push stream %d on secure connection", | |
| 1639 associated_stream_id)); | |
| 1640 return; | |
| 1641 } | |
| 1642 } else { | |
| 1643 // TODO(bnc): Change SpdyNetworkTransactionTests to use secure sockets. | |
| 1644 if (associated_url.GetOrigin() != gurl.GetOrigin()) { | |
| 1645 EnqueueResetStreamFrame( | |
| 1646 stream_id, request_priority, ERROR_CODE_REFUSED_STREAM, | |
| 1647 SpdyStringPrintf( | |
| 1648 "Rejected cross origin push stream %d on insecure connection", | |
| 1649 associated_stream_id)); | |
| 1650 return; | |
| 1651 } | |
| 1652 } | |
| 1653 } | |
| 1654 } | |
| 1655 | |
| 1656 // There should not be an existing pushed stream with the same path. | |
| 1657 UnclaimedPushedStreamContainer::const_iterator pushed_it = | |
| 1658 unclaimed_pushed_streams_.lower_bound(gurl); | |
| 1659 if (pushed_it != unclaimed_pushed_streams_.end() && | |
| 1660 pushed_it->first == gurl) { | |
| 1661 EnqueueResetStreamFrame( | |
| 1662 stream_id, request_priority, ERROR_CODE_PROTOCOL_ERROR, | |
| 1663 "Received duplicate pushed stream with url: " + gurl.spec()); | |
| 1664 return; | |
| 1665 } | |
| 1666 | |
| 1667 std::unique_ptr<SpdyStream> stream( | |
| 1668 new SpdyStream(SPDY_PUSH_STREAM, GetWeakPtr(), gurl, request_priority, | |
| 1669 stream_initial_send_window_size_, | |
| 1670 stream_max_recv_window_size_, net_log_)); | |
| 1671 stream->set_stream_id(stream_id); | |
| 1672 | |
| 1673 // Convert RequestPriority to a SpdyPriority to send in a PRIORITY frame. | |
| 1674 SpdyPriority spdy_priority = | |
| 1675 ConvertRequestPriorityToSpdyPriority(request_priority); | |
| 1676 SpdyStreamId dependency_id = 0; | |
| 1677 bool exclusive = false; | |
| 1678 priority_dependency_state_.OnStreamCreation(stream_id, spdy_priority, | |
| 1679 &dependency_id, &exclusive); | |
| 1680 EnqueuePriorityFrame(stream_id, dependency_id, | |
| 1681 Spdy3PriorityToHttp2Weight(spdy_priority), exclusive); | |
| 1682 | |
| 1683 // PUSH_PROMISE arrives on associated stream. | |
| 1684 associated_it->second->AddRawReceivedBytes(last_compressed_frame_len_); | |
| 1685 last_compressed_frame_len_ = 0; | |
| 1686 | |
| 1687 UnclaimedPushedStreamContainer::const_iterator inserted_pushed_it = | |
| 1688 unclaimed_pushed_streams_.insert(pushed_it, gurl, stream_id, | |
| 1689 time_func_()); | |
| 1690 DCHECK(inserted_pushed_it != pushed_it); | |
| 1691 DeleteExpiredPushedStreams(); | |
| 1692 | |
| 1693 InsertActivatedStream(std::move(stream)); | |
| 1694 | |
| 1695 ActiveStreamMap::iterator active_it = active_streams_.find(stream_id); | |
| 1696 if (active_it == active_streams_.end()) { | |
| 1697 NOTREACHED(); | |
| 1698 return; | |
| 1699 } | |
| 1700 | |
| 1701 // Notify the push_delegate that a push promise has been received. | |
| 1702 if (push_delegate_) { | |
| 1703 push_delegate_->OnPush(base::MakeUnique<SpdyServerPushHelper>( | |
| 1704 weak_factory_.GetWeakPtr(), gurl), | |
| 1705 net_log_); | |
| 1706 } | |
| 1707 | |
| 1708 active_it->second->OnPushPromiseHeadersReceived(std::move(headers)); | |
| 1709 DCHECK(active_it->second->IsReservedRemote()); | |
| 1710 num_pushed_streams_++; | |
| 1711 return; | |
| 1712 } | |
| 1713 | |
| 1714 void SpdySession::CloseActiveStreamIterator(ActiveStreamMap::iterator it, | |
| 1715 int status) { | |
| 1716 // TODO(mbelshe): We should send a RST_STREAM control frame here | |
| 1717 // so that the server can cancel a large send. | |
| 1718 | |
| 1719 std::unique_ptr<SpdyStream> owned_stream(it->second); | |
| 1720 active_streams_.erase(it); | |
| 1721 priority_dependency_state_.OnStreamDestruction(owned_stream->stream_id()); | |
| 1722 | |
| 1723 // TODO(akalin): When SpdyStream was ref-counted (and | |
| 1724 // |unclaimed_pushed_streams_| held scoped_refptr<SpdyStream>), this | |
| 1725 // was only done when status was not OK. This meant that pushed | |
| 1726 // streams can still be claimed after they're closed. This is | |
| 1727 // probably something that we still want to support, although server | |
| 1728 // push is hardly used. Write tests for this and fix this. (See | |
| 1729 // http://crbug.com/261712 .) | |
| 1730 if (owned_stream->type() == SPDY_PUSH_STREAM) { | |
| 1731 unclaimed_pushed_streams_.erase(owned_stream->url()); | |
| 1732 bytes_pushed_count_ += owned_stream->recv_bytes(); | |
| 1733 num_pushed_streams_--; | |
| 1734 if (!owned_stream->IsReservedRemote()) | |
| 1735 num_active_pushed_streams_--; | |
| 1736 } | |
| 1737 | |
| 1738 DeleteStream(std::move(owned_stream), status); | |
| 1739 | |
| 1740 // If there are no active streams and the socket pool is stalled, close the | |
| 1741 // session to free up a socket slot. | |
| 1742 if (active_streams_.empty() && created_streams_.empty() && | |
| 1743 connection_->IsPoolStalled()) { | |
| 1744 DoDrainSession(ERR_CONNECTION_CLOSED, "Closing idle connection."); | |
| 1745 } | |
| 1746 } | |
| 1747 | |
| 1748 void SpdySession::CloseCreatedStreamIterator(CreatedStreamSet::iterator it, | |
| 1749 int status) { | |
| 1750 std::unique_ptr<SpdyStream> owned_stream(*it); | |
| 1751 created_streams_.erase(it); | |
| 1752 DeleteStream(std::move(owned_stream), status); | |
| 1753 } | |
| 1754 | |
| 1755 void SpdySession::ResetStreamIterator(ActiveStreamMap::iterator it, | |
| 1756 SpdyErrorCode error_code, | |
| 1757 const SpdyString& description) { | |
| 1758 // Send the RST_STREAM frame first as CloseActiveStreamIterator() | |
| 1759 // may close us. | |
| 1760 SpdyStreamId stream_id = it->first; | |
| 1761 RequestPriority priority = it->second->priority(); | |
| 1762 EnqueueResetStreamFrame(stream_id, priority, error_code, description); | |
| 1763 | |
| 1764 // Removes any pending writes for the stream except for possibly an | |
| 1765 // in-flight one. | |
| 1766 CloseActiveStreamIterator(it, ERR_SPDY_PROTOCOL_ERROR); | |
| 1767 } | |
| 1768 | |
| 1769 void SpdySession::EnqueueResetStreamFrame(SpdyStreamId stream_id, | |
| 1770 RequestPriority priority, | |
| 1771 SpdyErrorCode error_code, | |
| 1772 const SpdyString& description) { | |
| 1773 DCHECK_NE(stream_id, 0u); | |
| 1774 | |
| 1775 net_log().AddEvent(NetLogEventType::HTTP2_SESSION_SEND_RST_STREAM, | |
| 1776 base::Bind(&NetLogSpdySendRstStreamCallback, stream_id, | |
| 1777 error_code, &description)); | |
| 1778 | |
| 1779 DCHECK(buffered_spdy_framer_.get()); | |
| 1780 std::unique_ptr<SpdySerializedFrame> rst_frame( | |
| 1781 buffered_spdy_framer_->CreateRstStream(stream_id, error_code)); | |
| 1782 | |
| 1783 EnqueueSessionWrite(priority, SpdyFrameType::RST_STREAM, | |
| 1784 std::move(rst_frame)); | |
| 1785 RecordProtocolErrorHistogram(MapRstStreamStatusToProtocolError(error_code)); | |
| 1786 } | |
| 1787 | |
| 1788 void SpdySession::EnqueuePriorityFrame(SpdyStreamId stream_id, | |
| 1789 SpdyStreamId dependency_id, | |
| 1790 int weight, | |
| 1791 bool exclusive) { | |
| 1792 net_log().AddEvent(NetLogEventType::HTTP2_STREAM_SEND_PRIORITY, | |
| 1793 base::Bind(&NetLogSpdyPriorityCallback, stream_id, | |
| 1794 dependency_id, weight, exclusive)); | |
| 1795 | |
| 1796 DCHECK(buffered_spdy_framer_.get()); | |
| 1797 std::unique_ptr<SpdySerializedFrame> frame( | |
| 1798 buffered_spdy_framer_->CreatePriority(stream_id, dependency_id, weight, | |
| 1799 exclusive)); | |
| 1800 | |
| 1801 // PRIORITY frames describe sequenced updates to the tree, so they must | |
| 1802 // be serialized. We do this by queueing all PRIORITY frames at HIGHEST | |
| 1803 // priority. | |
| 1804 EnqueueWrite(HIGHEST, SpdyFrameType::PRIORITY, | |
| 1805 base::MakeUnique<SimpleBufferProducer>( | |
| 1806 base::MakeUnique<SpdyBuffer>(std::move(frame))), | |
| 1807 base::WeakPtr<SpdyStream>()); | |
| 1808 } | |
| 1809 | |
| 1810 void SpdySession::PumpReadLoop(ReadState expected_read_state, int result) { | |
| 1811 CHECK(!in_io_loop_); | |
| 1812 if (availability_state_ == STATE_DRAINING) { | |
| 1813 return; | |
| 1814 } | |
| 1815 ignore_result(DoReadLoop(expected_read_state, result)); | |
| 1816 } | |
| 1817 | |
| 1818 int SpdySession::DoReadLoop(ReadState expected_read_state, int result) { | |
| 1819 CHECK(!in_io_loop_); | |
| 1820 CHECK_EQ(read_state_, expected_read_state); | |
| 1821 | |
| 1822 in_io_loop_ = true; | |
| 1823 | |
| 1824 int bytes_read_without_yielding = 0; | |
| 1825 const base::TimeTicks yield_after_time = | |
| 1826 time_func_() + | |
| 1827 base::TimeDelta::FromMilliseconds(kYieldAfterDurationMilliseconds); | |
| 1828 | |
| 1829 // Loop until the session is draining, the read becomes blocked, or | |
| 1830 // the read limit is exceeded. | |
| 1831 while (true) { | |
| 1832 switch (read_state_) { | |
| 1833 case READ_STATE_DO_READ: | |
| 1834 CHECK_EQ(result, OK); | |
| 1835 result = DoRead(); | |
| 1836 break; | |
| 1837 case READ_STATE_DO_READ_COMPLETE: | |
| 1838 if (result > 0) | |
| 1839 bytes_read_without_yielding += result; | |
| 1840 result = DoReadComplete(result); | |
| 1841 break; | |
| 1842 default: | |
| 1843 NOTREACHED() << "read_state_: " << read_state_; | |
| 1844 break; | |
| 1845 } | |
| 1846 | |
| 1847 if (availability_state_ == STATE_DRAINING) | |
| 1848 break; | |
| 1849 | |
| 1850 if (result == ERR_IO_PENDING) | |
| 1851 break; | |
| 1852 | |
| 1853 if (read_state_ == READ_STATE_DO_READ && | |
| 1854 (bytes_read_without_yielding > kYieldAfterBytesRead || | |
| 1855 time_func_() > yield_after_time)) { | |
| 1856 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1857 FROM_HERE, | |
| 1858 base::Bind(&SpdySession::PumpReadLoop, weak_factory_.GetWeakPtr(), | |
| 1859 READ_STATE_DO_READ, OK)); | |
| 1860 result = ERR_IO_PENDING; | |
| 1861 break; | |
| 1862 } | |
| 1863 } | |
| 1864 | |
| 1865 CHECK(in_io_loop_); | |
| 1866 in_io_loop_ = false; | |
| 1867 | |
| 1868 return result; | |
| 1869 } | |
| 1870 | |
| 1871 int SpdySession::DoRead() { | |
| 1872 DCHECK(!read_buffer_); | |
| 1873 CHECK(in_io_loop_); | |
| 1874 | |
| 1875 CHECK(connection_); | |
| 1876 CHECK(connection_->socket()); | |
| 1877 read_state_ = READ_STATE_DO_READ_COMPLETE; | |
| 1878 int rv = ERR_READ_IF_READY_NOT_IMPLEMENTED; | |
| 1879 read_buffer_ = new IOBuffer(kReadBufferSize); | |
| 1880 if (base::FeatureList::IsEnabled(Socket::kReadIfReadyExperiment)) { | |
| 1881 rv = connection_->socket()->ReadIfReady( | |
| 1882 read_buffer_.get(), kReadBufferSize, | |
| 1883 base::Bind(&SpdySession::PumpReadLoop, weak_factory_.GetWeakPtr(), | |
| 1884 READ_STATE_DO_READ)); | |
| 1885 if (rv == ERR_IO_PENDING) { | |
| 1886 read_buffer_ = nullptr; | |
| 1887 read_state_ = READ_STATE_DO_READ; | |
| 1888 return rv; | |
| 1889 } | |
| 1890 } | |
| 1891 if (rv == ERR_READ_IF_READY_NOT_IMPLEMENTED) { | |
| 1892 // Fallback to regular Read(). | |
| 1893 return connection_->socket()->Read( | |
| 1894 read_buffer_.get(), kReadBufferSize, | |
| 1895 base::Bind(&SpdySession::PumpReadLoop, weak_factory_.GetWeakPtr(), | |
| 1896 READ_STATE_DO_READ_COMPLETE)); | |
| 1897 } | |
| 1898 return rv; | |
| 1899 } | |
| 1900 | |
| 1901 int SpdySession::DoReadComplete(int result) { | |
| 1902 DCHECK(read_buffer_); | |
| 1903 CHECK(in_io_loop_); | |
| 1904 | |
| 1905 // Parse a frame. For now this code requires that the frame fit into our | |
| 1906 // buffer (kReadBufferSize). | |
| 1907 // TODO(mbelshe): support arbitrarily large frames! | |
| 1908 | |
| 1909 if (result == 0) { | |
| 1910 DoDrainSession(ERR_CONNECTION_CLOSED, "Connection closed"); | |
| 1911 return ERR_CONNECTION_CLOSED; | |
| 1912 } | |
| 1913 | |
| 1914 if (result < 0) { | |
| 1915 DoDrainSession(static_cast<Error>(result), | |
| 1916 SpdyStringPrintf("Error %d reading from socket.", -result)); | |
| 1917 return result; | |
| 1918 } | |
| 1919 CHECK_LE(result, kReadBufferSize); | |
| 1920 | |
| 1921 last_activity_time_ = time_func_(); | |
| 1922 | |
| 1923 DCHECK(buffered_spdy_framer_.get()); | |
| 1924 char* data = read_buffer_->data(); | |
| 1925 while (result > 0) { | |
| 1926 uint32_t bytes_processed = | |
| 1927 buffered_spdy_framer_->ProcessInput(data, result); | |
| 1928 result -= bytes_processed; | |
| 1929 data += bytes_processed; | |
| 1930 | |
| 1931 if (availability_state_ == STATE_DRAINING) { | |
| 1932 return ERR_CONNECTION_CLOSED; | |
| 1933 } | |
| 1934 | |
| 1935 DCHECK_EQ(buffered_spdy_framer_->spdy_framer_error(), | |
| 1936 SpdyFramer::SPDY_NO_ERROR); | |
| 1937 } | |
| 1938 | |
| 1939 read_buffer_ = nullptr; | |
| 1940 read_state_ = READ_STATE_DO_READ; | |
| 1941 return OK; | |
| 1942 } | |
| 1943 | |
| 1944 void SpdySession::PumpWriteLoop(WriteState expected_write_state, int result) { | |
| 1945 CHECK(!in_io_loop_); | |
| 1946 DCHECK_EQ(write_state_, expected_write_state); | |
| 1947 | |
| 1948 DoWriteLoop(expected_write_state, result); | |
| 1949 | |
| 1950 if (availability_state_ == STATE_DRAINING && !in_flight_write_ && | |
| 1951 write_queue_.IsEmpty()) { | |
| 1952 pool_->RemoveUnavailableSession(GetWeakPtr()); // Destroys |this|. | |
| 1953 return; | |
| 1954 } | |
| 1955 } | |
| 1956 | |
| 1957 void SpdySession::MaybePostWriteLoop() { | |
| 1958 if (write_state_ == WRITE_STATE_IDLE) { | |
| 1959 CHECK(!in_flight_write_); | |
| 1960 write_state_ = WRITE_STATE_DO_WRITE; | |
| 1961 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1962 FROM_HERE, | |
| 1963 base::Bind(&SpdySession::PumpWriteLoop, weak_factory_.GetWeakPtr(), | |
| 1964 WRITE_STATE_DO_WRITE, OK)); | |
| 1965 } | |
| 1966 } | |
| 1967 | |
| 1968 int SpdySession::DoWriteLoop(WriteState expected_write_state, int result) { | |
| 1969 CHECK(!in_io_loop_); | |
| 1970 DCHECK_NE(write_state_, WRITE_STATE_IDLE); | |
| 1971 DCHECK_EQ(write_state_, expected_write_state); | |
| 1972 | |
| 1973 in_io_loop_ = true; | |
| 1974 | |
| 1975 // Loop until the session is closed or the write becomes blocked. | |
| 1976 while (true) { | |
| 1977 switch (write_state_) { | |
| 1978 case WRITE_STATE_DO_WRITE: | |
| 1979 DCHECK_EQ(result, OK); | |
| 1980 result = DoWrite(); | |
| 1981 break; | |
| 1982 case WRITE_STATE_DO_WRITE_COMPLETE: | |
| 1983 result = DoWriteComplete(result); | |
| 1984 break; | |
| 1985 case WRITE_STATE_IDLE: | |
| 1986 default: | |
| 1987 NOTREACHED() << "write_state_: " << write_state_; | |
| 1988 break; | |
| 1989 } | |
| 1990 | |
| 1991 if (write_state_ == WRITE_STATE_IDLE) { | |
| 1992 DCHECK_EQ(result, ERR_IO_PENDING); | |
| 1993 break; | |
| 1994 } | |
| 1995 | |
| 1996 if (result == ERR_IO_PENDING) | |
| 1997 break; | |
| 1998 } | |
| 1999 | |
| 2000 CHECK(in_io_loop_); | |
| 2001 in_io_loop_ = false; | |
| 2002 | |
| 2003 return result; | |
| 2004 } | |
| 2005 | |
| 2006 int SpdySession::DoWrite() { | |
| 2007 CHECK(in_io_loop_); | |
| 2008 | |
| 2009 DCHECK(buffered_spdy_framer_); | |
| 2010 if (in_flight_write_) { | |
| 2011 DCHECK_GT(in_flight_write_->GetRemainingSize(), 0u); | |
| 2012 } else { | |
| 2013 // Grab the next frame to send. | |
| 2014 SpdyFrameType frame_type = SpdyFrameType::DATA; | |
| 2015 std::unique_ptr<SpdyBufferProducer> producer; | |
| 2016 base::WeakPtr<SpdyStream> stream; | |
| 2017 if (!write_queue_.Dequeue(&frame_type, &producer, &stream)) { | |
| 2018 write_state_ = WRITE_STATE_IDLE; | |
| 2019 return ERR_IO_PENDING; | |
| 2020 } | |
| 2021 | |
| 2022 if (stream.get()) | |
| 2023 CHECK(!stream->IsClosed()); | |
| 2024 | |
| 2025 // Activate the stream only when sending the HEADERS frame to | |
| 2026 // guarantee monotonically-increasing stream IDs. | |
| 2027 if (frame_type == SpdyFrameType::HEADERS) { | |
| 2028 CHECK(stream.get()); | |
| 2029 CHECK_EQ(stream->stream_id(), 0u); | |
| 2030 std::unique_ptr<SpdyStream> owned_stream = | |
| 2031 ActivateCreatedStream(stream.get()); | |
| 2032 InsertActivatedStream(std::move(owned_stream)); | |
| 2033 | |
| 2034 if (stream_hi_water_mark_ > kLastStreamId) { | |
| 2035 CHECK_EQ(stream->stream_id(), kLastStreamId); | |
| 2036 // We've exhausted the stream ID space, and no new streams may be | |
| 2037 // created after this one. | |
| 2038 MakeUnavailable(); | |
| 2039 StartGoingAway(kLastStreamId, ERR_ABORTED); | |
| 2040 } | |
| 2041 } | |
| 2042 | |
| 2043 // TODO(pkasting): Remove ScopedTracker below once crbug.com/457517 is | |
| 2044 // fixed. | |
| 2045 tracked_objects::ScopedTracker tracking_profile1( | |
| 2046 FROM_HERE_WITH_EXPLICIT_FUNCTION("457517 SpdySession::DoWrite1")); | |
| 2047 in_flight_write_ = producer->ProduceBuffer(); | |
| 2048 if (!in_flight_write_) { | |
| 2049 NOTREACHED(); | |
| 2050 return ERR_UNEXPECTED; | |
| 2051 } | |
| 2052 in_flight_write_frame_type_ = frame_type; | |
| 2053 in_flight_write_frame_size_ = in_flight_write_->GetRemainingSize(); | |
| 2054 DCHECK_GE(in_flight_write_frame_size_, | |
| 2055 buffered_spdy_framer_->GetFrameMinimumSize()); | |
| 2056 in_flight_write_stream_ = stream; | |
| 2057 } | |
| 2058 | |
| 2059 write_state_ = WRITE_STATE_DO_WRITE_COMPLETE; | |
| 2060 | |
| 2061 // Explicitly store in a scoped_refptr<IOBuffer> to avoid problems | |
| 2062 // with Socket implementations that don't store their IOBuffer | |
| 2063 // argument in a scoped_refptr<IOBuffer> (see crbug.com/232345). | |
| 2064 // TODO(pkasting): Remove ScopedTracker below once crbug.com/457517 is fixed. | |
| 2065 tracked_objects::ScopedTracker tracking_profile2( | |
| 2066 FROM_HERE_WITH_EXPLICIT_FUNCTION("457517 SpdySession::DoWrite2")); | |
| 2067 scoped_refptr<IOBuffer> write_io_buffer = | |
| 2068 in_flight_write_->GetIOBufferForRemainingData(); | |
| 2069 return connection_->socket()->Write( | |
| 2070 write_io_buffer.get(), in_flight_write_->GetRemainingSize(), | |
| 2071 base::Bind(&SpdySession::PumpWriteLoop, weak_factory_.GetWeakPtr(), | |
| 2072 WRITE_STATE_DO_WRITE_COMPLETE)); | |
| 2073 } | |
| 2074 | |
| 2075 int SpdySession::DoWriteComplete(int result) { | |
| 2076 CHECK(in_io_loop_); | |
| 2077 DCHECK_NE(result, ERR_IO_PENDING); | |
| 2078 DCHECK_GT(in_flight_write_->GetRemainingSize(), 0u); | |
| 2079 | |
| 2080 last_activity_time_ = time_func_(); | |
| 2081 | |
| 2082 if (result < 0) { | |
| 2083 DCHECK_NE(result, ERR_IO_PENDING); | |
| 2084 in_flight_write_.reset(); | |
| 2085 in_flight_write_frame_type_ = SpdyFrameType::DATA; | |
| 2086 in_flight_write_frame_size_ = 0; | |
| 2087 in_flight_write_stream_.reset(); | |
| 2088 write_state_ = WRITE_STATE_DO_WRITE; | |
| 2089 DoDrainSession(static_cast<Error>(result), "Write error"); | |
| 2090 return OK; | |
| 2091 } | |
| 2092 | |
| 2093 // It should not be possible to have written more bytes than our | |
| 2094 // in_flight_write_. | |
| 2095 DCHECK_LE(static_cast<size_t>(result), in_flight_write_->GetRemainingSize()); | |
| 2096 | |
| 2097 if (result > 0) { | |
| 2098 in_flight_write_->Consume(static_cast<size_t>(result)); | |
| 2099 if (in_flight_write_stream_.get()) | |
| 2100 in_flight_write_stream_->AddRawSentBytes(static_cast<size_t>(result)); | |
| 2101 | |
| 2102 // We only notify the stream when we've fully written the pending frame. | |
| 2103 if (in_flight_write_->GetRemainingSize() == 0) { | |
| 2104 // It is possible that the stream was cancelled while we were | |
| 2105 // writing to the socket. | |
| 2106 if (in_flight_write_stream_.get()) { | |
| 2107 DCHECK_GT(in_flight_write_frame_size_, 0u); | |
| 2108 in_flight_write_stream_->OnFrameWriteComplete( | |
| 2109 in_flight_write_frame_type_, in_flight_write_frame_size_); | |
| 2110 } | |
| 2111 | |
| 2112 // Cleanup the write which just completed. | |
| 2113 in_flight_write_.reset(); | |
| 2114 in_flight_write_frame_type_ = SpdyFrameType::DATA; | |
| 2115 in_flight_write_frame_size_ = 0; | |
| 2116 in_flight_write_stream_.reset(); | |
| 2117 } | |
| 2118 } | |
| 2119 | |
| 2120 write_state_ = WRITE_STATE_DO_WRITE; | |
| 2121 return OK; | |
| 2122 } | |
| 2123 | |
| 2124 void SpdySession::SendInitialData() { | |
| 2125 DCHECK(enable_sending_initial_data_); | |
| 2126 | |
| 2127 std::unique_ptr<SpdySerializedFrame> connection_header_prefix_frame( | |
| 2128 new SpdySerializedFrame(const_cast<char*>(kHttp2ConnectionHeaderPrefix), | |
| 2129 kHttp2ConnectionHeaderPrefixSize, | |
| 2130 false /* take_ownership */)); | |
| 2131 // Count the prefix as part of the subsequent SETTINGS frame. | |
| 2132 EnqueueSessionWrite(HIGHEST, SpdyFrameType::SETTINGS, | |
| 2133 std::move(connection_header_prefix_frame)); | |
| 2134 | |
| 2135 // First, notify the server about the settings they should use when | |
| 2136 // communicating with us. Only send settings that have a value different from | |
| 2137 // the protocol default value. | |
| 2138 SettingsMap settings_map; | |
| 2139 for (auto setting : initial_settings_) { | |
| 2140 if (!IsSpdySettingAtDefaultInitialValue(setting.first, setting.second)) { | |
| 2141 settings_map.insert(setting); | |
| 2142 } | |
| 2143 } | |
| 2144 SendSettings(settings_map); | |
| 2145 | |
| 2146 // Next, notify the server about our initial recv window size. | |
| 2147 // Bump up the receive window size to the real initial value. This | |
| 2148 // has to go here since the WINDOW_UPDATE frame sent by | |
| 2149 // IncreaseRecvWindowSize() call uses |buffered_spdy_framer_|. | |
| 2150 // This condition implies that |session_max_recv_window_size_| - | |
| 2151 // |session_recv_window_size_| doesn't overflow. | |
| 2152 DCHECK_GE(session_max_recv_window_size_, session_recv_window_size_); | |
| 2153 DCHECK_GE(session_recv_window_size_, 0); | |
| 2154 if (session_max_recv_window_size_ > session_recv_window_size_) { | |
| 2155 IncreaseRecvWindowSize(session_max_recv_window_size_ - | |
| 2156 session_recv_window_size_); | |
| 2157 } | |
| 2158 } | |
| 2159 | |
| 2160 void SpdySession::SendSettings(const SettingsMap& settings) { | |
| 2161 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_SEND_SETTINGS, | |
| 2162 base::Bind(&NetLogSpdySendSettingsCallback, &settings)); | |
| 2163 // Create the SETTINGS frame and send it. | |
| 2164 DCHECK(buffered_spdy_framer_.get()); | |
| 2165 std::unique_ptr<SpdySerializedFrame> settings_frame( | |
| 2166 buffered_spdy_framer_->CreateSettings(settings)); | |
| 2167 EnqueueSessionWrite(HIGHEST, SpdyFrameType::SETTINGS, | |
| 2168 std::move(settings_frame)); | |
| 2169 } | |
| 2170 | |
| 2171 void SpdySession::HandleSetting(uint32_t id, uint32_t value) { | |
| 2172 switch (id) { | |
| 2173 case SETTINGS_MAX_CONCURRENT_STREAMS: | |
| 2174 max_concurrent_streams_ = | |
| 2175 std::min(static_cast<size_t>(value), kMaxConcurrentStreamLimit); | |
| 2176 ProcessPendingStreamRequests(); | |
| 2177 break; | |
| 2178 case SETTINGS_INITIAL_WINDOW_SIZE: { | |
| 2179 if (value > static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) { | |
| 2180 net_log().AddEvent( | |
| 2181 NetLogEventType::HTTP2_SESSION_INITIAL_WINDOW_SIZE_OUT_OF_RANGE, | |
| 2182 NetLog::IntCallback("initial_window_size", value)); | |
| 2183 return; | |
| 2184 } | |
| 2185 | |
| 2186 // SETTINGS_INITIAL_WINDOW_SIZE updates initial_send_window_size_ only. | |
| 2187 int32_t delta_window_size = | |
| 2188 static_cast<int32_t>(value) - stream_initial_send_window_size_; | |
| 2189 stream_initial_send_window_size_ = static_cast<int32_t>(value); | |
| 2190 UpdateStreamsSendWindowSize(delta_window_size); | |
| 2191 net_log().AddEvent( | |
| 2192 NetLogEventType::HTTP2_SESSION_UPDATE_STREAMS_SEND_WINDOW_SIZE, | |
| 2193 NetLog::IntCallback("delta_window_size", delta_window_size)); | |
| 2194 break; | |
| 2195 } | |
| 2196 } | |
| 2197 } | |
| 2198 | |
| 2199 void SpdySession::UpdateStreamsSendWindowSize(int32_t delta_window_size) { | |
| 2200 for (ActiveStreamMap::iterator it = active_streams_.begin(); | |
| 2201 it != active_streams_.end(); ++it) { | |
| 2202 it->second->AdjustSendWindowSize(delta_window_size); | |
| 2203 } | |
| 2204 | |
| 2205 for (CreatedStreamSet::const_iterator it = created_streams_.begin(); | |
| 2206 it != created_streams_.end(); it++) { | |
| 2207 (*it)->AdjustSendWindowSize(delta_window_size); | |
| 2208 } | |
| 2209 } | |
| 2210 | |
| 2211 void SpdySession::SendPrefacePingIfNoneInFlight() { | |
| 2212 if (pings_in_flight_ || !enable_ping_based_connection_checking_) | |
| 2213 return; | |
| 2214 | |
| 2215 base::TimeTicks now = time_func_(); | |
| 2216 // If there is no activity in the session, then send a preface-PING. | |
| 2217 if ((now - last_activity_time_) > connection_at_risk_of_loss_time_) | |
| 2218 SendPrefacePing(); | |
| 2219 } | |
| 2220 | |
| 2221 void SpdySession::SendPrefacePing() { | |
| 2222 WritePingFrame(next_ping_id_, false); | |
| 2223 } | |
| 2224 | |
| 2225 void SpdySession::SendWindowUpdateFrame(SpdyStreamId stream_id, | |
| 2226 uint32_t delta_window_size, | |
| 2227 RequestPriority priority) { | |
| 2228 ActiveStreamMap::const_iterator it = active_streams_.find(stream_id); | |
| 2229 if (it != active_streams_.end()) { | |
| 2230 CHECK_EQ(it->second->stream_id(), stream_id); | |
| 2231 } else { | |
| 2232 CHECK_EQ(stream_id, kSessionFlowControlStreamId); | |
| 2233 } | |
| 2234 | |
| 2235 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_SEND_WINDOW_UPDATE, | |
| 2236 base::Bind(&NetLogSpdyWindowUpdateFrameCallback, stream_id, | |
| 2237 delta_window_size)); | |
| 2238 | |
| 2239 DCHECK(buffered_spdy_framer_.get()); | |
| 2240 std::unique_ptr<SpdySerializedFrame> window_update_frame( | |
| 2241 buffered_spdy_framer_->CreateWindowUpdate(stream_id, delta_window_size)); | |
| 2242 EnqueueSessionWrite(priority, SpdyFrameType::WINDOW_UPDATE, | |
| 2243 std::move(window_update_frame)); | |
| 2244 } | |
| 2245 | |
| 2246 void SpdySession::WritePingFrame(SpdyPingId unique_id, bool is_ack) { | |
| 2247 DCHECK(buffered_spdy_framer_.get()); | |
| 2248 std::unique_ptr<SpdySerializedFrame> ping_frame( | |
| 2249 buffered_spdy_framer_->CreatePingFrame(unique_id, is_ack)); | |
| 2250 EnqueueSessionWrite(HIGHEST, SpdyFrameType::PING, std::move(ping_frame)); | |
| 2251 | |
| 2252 if (net_log().IsCapturing()) { | |
| 2253 net_log().AddEvent( | |
| 2254 NetLogEventType::HTTP2_SESSION_PING, | |
| 2255 base::Bind(&NetLogSpdyPingCallback, unique_id, is_ack, "sent")); | |
| 2256 } | |
| 2257 if (!is_ack) { | |
| 2258 next_ping_id_ += 2; | |
| 2259 ++pings_in_flight_; | |
| 2260 PlanToCheckPingStatus(); | |
| 2261 last_ping_sent_time_ = time_func_(); | |
| 2262 } | |
| 2263 } | |
| 2264 | |
| 2265 void SpdySession::PlanToCheckPingStatus() { | |
| 2266 if (check_ping_status_pending_) | |
| 2267 return; | |
| 2268 | |
| 2269 check_ping_status_pending_ = true; | |
| 2270 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | |
| 2271 FROM_HERE, base::Bind(&SpdySession::CheckPingStatus, | |
| 2272 weak_factory_.GetWeakPtr(), time_func_()), | |
| 2273 hung_interval_); | |
| 2274 } | |
| 2275 | |
| 2276 void SpdySession::CheckPingStatus(base::TimeTicks last_check_time) { | |
| 2277 CHECK(!in_io_loop_); | |
| 2278 | |
| 2279 // Check if we got a response back for all PINGs we had sent. | |
| 2280 if (pings_in_flight_ == 0) { | |
| 2281 check_ping_status_pending_ = false; | |
| 2282 return; | |
| 2283 } | |
| 2284 | |
| 2285 DCHECK(check_ping_status_pending_); | |
| 2286 | |
| 2287 base::TimeTicks now = time_func_(); | |
| 2288 base::TimeDelta delay = hung_interval_ - (now - last_activity_time_); | |
| 2289 | |
| 2290 if (delay.InMilliseconds() < 0 || last_activity_time_ < last_check_time) { | |
| 2291 DoDrainSession(ERR_SPDY_PING_FAILED, "Failed ping."); | |
| 2292 return; | |
| 2293 } | |
| 2294 | |
| 2295 // Check the status of connection after a delay. | |
| 2296 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | |
| 2297 FROM_HERE, base::Bind(&SpdySession::CheckPingStatus, | |
| 2298 weak_factory_.GetWeakPtr(), now), | |
| 2299 delay); | |
| 2300 } | |
| 2301 | |
| 2302 SpdyStreamId SpdySession::GetNewStreamId() { | |
| 2303 CHECK_LE(stream_hi_water_mark_, kLastStreamId); | |
| 2304 SpdyStreamId id = stream_hi_water_mark_; | |
| 2305 stream_hi_water_mark_ += 2; | |
| 2306 return id; | |
| 2307 } | |
| 2308 | |
| 2309 void SpdySession::EnqueueSessionWrite( | |
| 2310 RequestPriority priority, | |
| 2311 SpdyFrameType frame_type, | |
| 2312 std::unique_ptr<SpdySerializedFrame> frame) { | |
| 2313 DCHECK(frame_type == SpdyFrameType::RST_STREAM || | |
| 2314 frame_type == SpdyFrameType::SETTINGS || | |
| 2315 frame_type == SpdyFrameType::WINDOW_UPDATE || | |
| 2316 frame_type == SpdyFrameType::PING || | |
| 2317 frame_type == SpdyFrameType::GOAWAY); | |
| 2318 EnqueueWrite( | |
| 2319 priority, frame_type, | |
| 2320 std::unique_ptr<SpdyBufferProducer>(new SimpleBufferProducer( | |
| 2321 std::unique_ptr<SpdyBuffer>(new SpdyBuffer(std::move(frame))))), | |
| 2322 base::WeakPtr<SpdyStream>()); | |
| 2323 } | |
| 2324 | |
| 2325 void SpdySession::EnqueueWrite(RequestPriority priority, | |
| 2326 SpdyFrameType frame_type, | |
| 2327 std::unique_ptr<SpdyBufferProducer> producer, | |
| 2328 const base::WeakPtr<SpdyStream>& stream) { | |
| 2329 if (availability_state_ == STATE_DRAINING) | |
| 2330 return; | |
| 2331 | |
| 2332 write_queue_.Enqueue(priority, frame_type, std::move(producer), stream); | |
| 2333 MaybePostWriteLoop(); | |
| 2334 } | |
| 2335 | |
| 2336 void SpdySession::InsertCreatedStream(std::unique_ptr<SpdyStream> stream) { | |
| 2337 CHECK_EQ(stream->stream_id(), 0u); | |
| 2338 CHECK(created_streams_.find(stream.get()) == created_streams_.end()); | |
| 2339 created_streams_.insert(stream.release()); | |
| 2340 } | |
| 2341 | |
| 2342 std::unique_ptr<SpdyStream> SpdySession::ActivateCreatedStream( | |
| 2343 SpdyStream* stream) { | |
| 2344 CHECK_EQ(stream->stream_id(), 0u); | |
| 2345 CHECK(created_streams_.find(stream) != created_streams_.end()); | |
| 2346 stream->set_stream_id(GetNewStreamId()); | |
| 2347 std::unique_ptr<SpdyStream> owned_stream(stream); | |
| 2348 created_streams_.erase(stream); | |
| 2349 return owned_stream; | |
| 2350 } | |
| 2351 | |
| 2352 void SpdySession::InsertActivatedStream(std::unique_ptr<SpdyStream> stream) { | |
| 2353 SpdyStreamId stream_id = stream->stream_id(); | |
| 2354 CHECK_NE(stream_id, 0u); | |
| 2355 std::pair<ActiveStreamMap::iterator, bool> result = | |
| 2356 active_streams_.insert(std::make_pair(stream_id, stream.get())); | |
| 2357 CHECK(result.second); | |
| 2358 ignore_result(stream.release()); | |
| 2359 } | |
| 2360 | |
| 2361 void SpdySession::DeleteStream(std::unique_ptr<SpdyStream> stream, int status) { | |
| 2362 if (in_flight_write_stream_.get() == stream.get()) { | |
| 2363 // If we're deleting the stream for the in-flight write, we still | |
| 2364 // need to let the write complete, so we clear | |
| 2365 // |in_flight_write_stream_| and let the write finish on its own | |
| 2366 // without notifying |in_flight_write_stream_|. | |
| 2367 in_flight_write_stream_.reset(); | |
| 2368 } | |
| 2369 | |
| 2370 write_queue_.RemovePendingWritesForStream(stream->GetWeakPtr()); | |
| 2371 stream->OnClose(status); | |
| 2372 | |
| 2373 if (availability_state_ == STATE_AVAILABLE) { | |
| 2374 ProcessPendingStreamRequests(); | |
| 2375 } | |
| 2376 } | |
| 2377 | |
| 2378 SpdyStreamId SpdySession::GetStreamIdForPush(const GURL& url) { | |
| 2379 UnclaimedPushedStreamContainer::const_iterator unclaimed_it = | |
| 2380 unclaimed_pushed_streams_.find(url); | |
| 2381 if (unclaimed_it == unclaimed_pushed_streams_.end()) | |
| 2382 return 0; | |
| 2383 return unclaimed_it->second.stream_id; | |
| 2384 } | |
| 2385 | |
| 2386 SpdyStream* SpdySession::GetActivePushStream(const GURL& url) { | |
| 2387 UnclaimedPushedStreamContainer::const_iterator unclaimed_it = | |
| 2388 unclaimed_pushed_streams_.find(url); | |
| 2389 if (unclaimed_it == unclaimed_pushed_streams_.end()) | |
| 2390 return nullptr; | |
| 2391 | |
| 2392 SpdyStreamId stream_id = unclaimed_it->second.stream_id; | |
| 2393 unclaimed_pushed_streams_.erase(unclaimed_it); | |
| 2394 | |
| 2395 ActiveStreamMap::iterator active_it = active_streams_.find(stream_id); | |
| 2396 if (active_it == active_streams_.end()) { | |
| 2397 NOTREACHED(); | |
| 2398 return nullptr; | |
| 2399 } | |
| 2400 | |
| 2401 net_log_.AddEvent(NetLogEventType::HTTP2_STREAM_ADOPTED_PUSH_STREAM, | |
| 2402 base::Bind(&NetLogSpdyAdoptedPushStreamCallback, | |
| 2403 active_it->second->stream_id(), &url)); | |
| 2404 return active_it->second; | |
| 2405 } | |
| 2406 | |
| 2407 void SpdySession::RecordPingRTTHistogram(base::TimeDelta duration) { | |
| 2408 UMA_HISTOGRAM_CUSTOM_TIMES("Net.SpdyPing.RTT", duration, | |
| 2409 base::TimeDelta::FromMilliseconds(1), | |
| 2410 base::TimeDelta::FromMinutes(10), 100); | |
| 2411 } | |
| 2412 | |
| 2413 void SpdySession::RecordHistograms() { | |
| 2414 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamsPerSession", | |
| 2415 streams_initiated_count_, 1, 300, 50); | |
| 2416 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamsPushedPerSession", | |
| 2417 streams_pushed_count_, 1, 300, 50); | |
| 2418 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamsPushedAndClaimedPerSession", | |
| 2419 streams_pushed_and_claimed_count_, 1, 300, 50); | |
| 2420 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamsAbandonedPerSession", | |
| 2421 streams_abandoned_count_, 1, 300, 50); | |
| 2422 UMA_HISTOGRAM_COUNTS_1M("Net.SpdySession.PushedBytes", bytes_pushed_count_); | |
| 2423 DCHECK_LE(bytes_pushed_and_unclaimed_count_, bytes_pushed_count_); | |
| 2424 UMA_HISTOGRAM_COUNTS_1M("Net.SpdySession.PushedAndUnclaimedBytes", | |
| 2425 bytes_pushed_and_unclaimed_count_); | |
| 2426 } | |
| 2427 | |
| 2428 void SpdySession::RecordProtocolErrorHistogram( | |
| 2429 SpdyProtocolErrorDetails details) { | |
| 2430 UMA_HISTOGRAM_ENUMERATION("Net.SpdySessionErrorDetails2", details, | |
| 2431 NUM_SPDY_PROTOCOL_ERROR_DETAILS); | |
| 2432 if (base::EndsWith(host_port_pair().host(), "google.com", | |
| 2433 base::CompareCase::INSENSITIVE_ASCII)) { | |
| 2434 UMA_HISTOGRAM_ENUMERATION("Net.SpdySessionErrorDetails_Google2", details, | |
| 2435 NUM_SPDY_PROTOCOL_ERROR_DETAILS); | |
| 2436 } | |
| 2437 } | |
| 2438 | |
| 2439 void SpdySession::DcheckGoingAway() const { | |
| 2440 #if DCHECK_IS_ON() | |
| 2441 DCHECK_GE(availability_state_, STATE_GOING_AWAY); | |
| 2442 for (int i = MINIMUM_PRIORITY; i <= MAXIMUM_PRIORITY; ++i) { | |
| 2443 DCHECK(pending_create_stream_queues_[i].empty()); | |
| 2444 } | |
| 2445 DCHECK(created_streams_.empty()); | |
| 2446 #endif | |
| 2447 } | |
| 2448 | |
| 2449 void SpdySession::DcheckDraining() const { | |
| 2450 DcheckGoingAway(); | |
| 2451 DCHECK_EQ(availability_state_, STATE_DRAINING); | |
| 2452 DCHECK(active_streams_.empty()); | |
| 2453 DCHECK(unclaimed_pushed_streams_.empty()); | |
| 2454 } | |
| 2455 | |
| 2456 void SpdySession::DoDrainSession(Error err, const SpdyString& description) { | |
| 2457 if (availability_state_ == STATE_DRAINING) { | |
| 2458 return; | |
| 2459 } | |
| 2460 MakeUnavailable(); | |
| 2461 | |
| 2462 // Mark host_port_pair requiring HTTP/1.1 for subsequent connections. | |
| 2463 if (err == ERR_HTTP_1_1_REQUIRED) { | |
| 2464 http_server_properties_->SetHTTP11Required(host_port_pair()); | |
| 2465 } | |
| 2466 | |
| 2467 // If |err| indicates an error occurred, inform the peer that we're closing | |
| 2468 // and why. Don't GOAWAY on a graceful or idle close, as that may | |
| 2469 // unnecessarily wake the radio. We could technically GOAWAY on network errors | |
| 2470 // (we'll probably fail to actually write it, but that's okay), however many | |
| 2471 // unit-tests would need to be updated. | |
| 2472 if (err != OK && | |
| 2473 err != ERR_ABORTED && // Used by SpdySessionPool to close idle sessions. | |
| 2474 err != ERR_NETWORK_CHANGED && // Used to deprecate sessions on IP change. | |
| 2475 err != ERR_SOCKET_NOT_CONNECTED && err != ERR_HTTP_1_1_REQUIRED && | |
| 2476 err != ERR_CONNECTION_CLOSED && err != ERR_CONNECTION_RESET) { | |
| 2477 // Enqueue a GOAWAY to inform the peer of why we're closing the connection. | |
| 2478 SpdyGoAwayIR goaway_ir(last_accepted_push_stream_id_, | |
| 2479 MapNetErrorToGoAwayStatus(err), description); | |
| 2480 EnqueueSessionWrite( | |
| 2481 HIGHEST, SpdyFrameType::GOAWAY, | |
| 2482 std::unique_ptr<SpdySerializedFrame>(new SpdySerializedFrame( | |
| 2483 buffered_spdy_framer_->SerializeFrame(goaway_ir)))); | |
| 2484 } | |
| 2485 | |
| 2486 availability_state_ = STATE_DRAINING; | |
| 2487 error_on_close_ = err; | |
| 2488 | |
| 2489 net_log_.AddEvent( | |
| 2490 NetLogEventType::HTTP2_SESSION_CLOSE, | |
| 2491 base::Bind(&NetLogSpdySessionCloseCallback, err, &description)); | |
| 2492 | |
| 2493 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.SpdySession.ClosedOnError", -err); | |
| 2494 | |
| 2495 if (err == OK) { | |
| 2496 // We ought to be going away already, as this is a graceful close. | |
| 2497 DcheckGoingAway(); | |
| 2498 } else { | |
| 2499 StartGoingAway(0, err); | |
| 2500 } | |
| 2501 DcheckDraining(); | |
| 2502 MaybePostWriteLoop(); | |
| 2503 } | |
| 2504 | |
| 2505 void SpdySession::LogAbandonedStream(SpdyStream* stream, Error status) { | |
| 2506 DCHECK(stream); | |
| 2507 SpdyString description = | |
| 2508 SpdyStringPrintf("ABANDONED (stream_id=%d): ", stream->stream_id()) + | |
| 2509 stream->url().spec(); | |
| 2510 stream->LogStreamError(status, description); | |
| 2511 // We don't increment the streams abandoned counter here. If the | |
| 2512 // stream isn't active (i.e., it hasn't written anything to the wire | |
| 2513 // yet) then it's as if it never existed. If it is active, then | |
| 2514 // LogAbandonedActiveStream() will increment the counters. | |
| 2515 } | |
| 2516 | |
| 2517 void SpdySession::LogAbandonedActiveStream(ActiveStreamMap::const_iterator it, | |
| 2518 Error status) { | |
| 2519 DCHECK_GT(it->first, 0u); | |
| 2520 LogAbandonedStream(it->second, status); | |
| 2521 ++streams_abandoned_count_; | |
| 2522 } | |
| 2523 | |
| 2524 void SpdySession::CompleteStreamRequest( | |
| 2525 const base::WeakPtr<SpdyStreamRequest>& pending_request) { | |
| 2526 // Abort if the request has already been cancelled. | |
| 2527 if (!pending_request) | |
| 2528 return; | |
| 2529 | |
| 2530 base::WeakPtr<SpdyStream> stream; | |
| 2531 int rv = TryCreateStream(pending_request, &stream); | |
| 2532 | |
| 2533 if (rv == OK) { | |
| 2534 DCHECK(stream); | |
| 2535 pending_request->OnRequestCompleteSuccess(stream); | |
| 2536 return; | |
| 2537 } | |
| 2538 DCHECK(!stream); | |
| 2539 | |
| 2540 if (rv != ERR_IO_PENDING) { | |
| 2541 pending_request->OnRequestCompleteFailure(rv); | |
| 2542 } | |
| 2543 } | |
| 2544 | |
| 2545 void SpdySession::DeleteExpiredPushedStreams() { | |
| 2546 if (unclaimed_pushed_streams_.empty()) | |
| 2547 return; | |
| 2548 | |
| 2549 // Check that adequate time has elapsed since the last sweep. | |
| 2550 if (time_func_() < next_unclaimed_push_stream_sweep_time_) | |
| 2551 return; | |
| 2552 | |
| 2553 // Gather old streams to delete. | |
| 2554 base::TimeTicks minimum_freshness = | |
| 2555 time_func_() - | |
| 2556 base::TimeDelta::FromSeconds(kMinPushedStreamLifetimeSeconds); | |
| 2557 std::vector<SpdyStreamId> streams_to_close; | |
| 2558 for (UnclaimedPushedStreamContainer::const_iterator it = | |
| 2559 unclaimed_pushed_streams_.begin(); | |
| 2560 it != unclaimed_pushed_streams_.end(); ++it) { | |
| 2561 if (minimum_freshness > it->second.creation_time) | |
| 2562 streams_to_close.push_back(it->second.stream_id); | |
| 2563 } | |
| 2564 | |
| 2565 for (std::vector<SpdyStreamId>::const_iterator to_close_it = | |
| 2566 streams_to_close.begin(); | |
| 2567 to_close_it != streams_to_close.end(); ++to_close_it) { | |
| 2568 ActiveStreamMap::iterator active_it = active_streams_.find(*to_close_it); | |
| 2569 if (active_it == active_streams_.end()) | |
| 2570 continue; | |
| 2571 bytes_pushed_and_unclaimed_count_ += active_it->second->recv_bytes(); | |
| 2572 | |
| 2573 LogAbandonedActiveStream(active_it, ERR_INVALID_SPDY_STREAM); | |
| 2574 // CloseActiveStreamIterator() will remove the stream from | |
| 2575 // |unclaimed_pushed_streams_|. | |
| 2576 ResetStreamIterator(active_it, ERROR_CODE_REFUSED_STREAM, | |
| 2577 "Stream not claimed."); | |
| 2578 } | |
| 2579 | |
| 2580 next_unclaimed_push_stream_sweep_time_ = | |
| 2581 time_func_() + | |
| 2582 base::TimeDelta::FromSeconds(kMinPushedStreamLifetimeSeconds); | |
| 2583 } | |
| 2584 | |
| 2585 void SpdySession::OnError(SpdyFramer::SpdyFramerError spdy_framer_error) { | |
| 2586 CHECK(in_io_loop_); | |
| 2587 | |
| 2588 RecordProtocolErrorHistogram( | |
| 2589 MapFramerErrorToProtocolError(spdy_framer_error)); | |
| 2590 SpdyString description = | |
| 2591 SpdyStringPrintf("Framer error: %d (%s).", spdy_framer_error, | |
| 2592 SpdyFramer::SpdyFramerErrorToString(spdy_framer_error)); | |
| 2593 DoDrainSession(MapFramerErrorToNetError(spdy_framer_error), description); | |
| 2594 } | |
| 2595 | |
| 2596 void SpdySession::OnStreamError(SpdyStreamId stream_id, | |
| 2597 const SpdyString& description) { | |
| 2598 CHECK(in_io_loop_); | |
| 2599 | |
| 2600 ActiveStreamMap::iterator it = active_streams_.find(stream_id); | |
| 2601 if (it == active_streams_.end()) { | |
| 2602 // We still want to send a frame to reset the stream even if we | |
| 2603 // don't know anything about it. | |
| 2604 EnqueueResetStreamFrame(stream_id, IDLE, ERROR_CODE_PROTOCOL_ERROR, | |
| 2605 description); | |
| 2606 return; | |
| 2607 } | |
| 2608 | |
| 2609 ResetStreamIterator(it, ERROR_CODE_PROTOCOL_ERROR, description); | |
| 2610 } | |
| 2611 | |
| 2612 void SpdySession::OnPing(SpdyPingId unique_id, bool is_ack) { | |
| 2613 CHECK(in_io_loop_); | |
| 2614 | |
| 2615 net_log_.AddEvent( | |
| 2616 NetLogEventType::HTTP2_SESSION_PING, | |
| 2617 base::Bind(&NetLogSpdyPingCallback, unique_id, is_ack, "received")); | |
| 2618 | |
| 2619 // Send response to a PING from server. | |
| 2620 if (!is_ack) { | |
| 2621 WritePingFrame(unique_id, true); | |
| 2622 return; | |
| 2623 } | |
| 2624 | |
| 2625 --pings_in_flight_; | |
| 2626 if (pings_in_flight_ < 0) { | |
| 2627 RecordProtocolErrorHistogram(PROTOCOL_ERROR_UNEXPECTED_PING); | |
| 2628 DoDrainSession(ERR_SPDY_PROTOCOL_ERROR, "pings_in_flight_ is < 0."); | |
| 2629 pings_in_flight_ = 0; | |
| 2630 return; | |
| 2631 } | |
| 2632 | |
| 2633 if (pings_in_flight_ > 0) | |
| 2634 return; | |
| 2635 | |
| 2636 // We will record RTT in histogram when there are no more client sent | |
| 2637 // pings_in_flight_. | |
| 2638 RecordPingRTTHistogram(time_func_() - last_ping_sent_time_); | |
| 2639 } | |
| 2640 | |
| 2641 void SpdySession::OnRstStream(SpdyStreamId stream_id, | |
| 2642 SpdyErrorCode error_code) { | |
| 2643 CHECK(in_io_loop_); | |
| 2644 | |
| 2645 net_log().AddEvent( | |
| 2646 NetLogEventType::HTTP2_SESSION_RECV_RST_STREAM, | |
| 2647 base::Bind(&NetLogSpdyRecvRstStreamCallback, stream_id, error_code)); | |
| 2648 | |
| 2649 ActiveStreamMap::iterator it = active_streams_.find(stream_id); | |
| 2650 if (it == active_streams_.end()) { | |
| 2651 // NOTE: it may just be that the stream was cancelled. | |
| 2652 LOG(WARNING) << "Received RST for invalid stream" << stream_id; | |
| 2653 return; | |
| 2654 } | |
| 2655 | |
| 2656 CHECK_EQ(it->second->stream_id(), stream_id); | |
| 2657 | |
| 2658 if (error_code == ERROR_CODE_NO_ERROR) { | |
| 2659 CloseActiveStreamIterator(it, ERR_SPDY_RST_STREAM_NO_ERROR_RECEIVED); | |
| 2660 } else if (error_code == ERROR_CODE_REFUSED_STREAM) { | |
| 2661 CloseActiveStreamIterator(it, ERR_SPDY_SERVER_REFUSED_STREAM); | |
| 2662 } else if (error_code == ERROR_CODE_HTTP_1_1_REQUIRED) { | |
| 2663 // TODO(bnc): Record histogram with number of open streams capped at 50. | |
| 2664 it->second->LogStreamError( | |
| 2665 ERR_HTTP_1_1_REQUIRED, | |
| 2666 SpdyStringPrintf( | |
| 2667 "SPDY session closed because of stream with error_code: %u", | |
| 2668 error_code)); | |
| 2669 DoDrainSession(ERR_HTTP_1_1_REQUIRED, "HTTP_1_1_REQUIRED for stream."); | |
| 2670 } else { | |
| 2671 RecordProtocolErrorHistogram( | |
| 2672 PROTOCOL_ERROR_RST_STREAM_FOR_NON_ACTIVE_STREAM); | |
| 2673 it->second->LogStreamError( | |
| 2674 ERR_SPDY_PROTOCOL_ERROR, | |
| 2675 SpdyStringPrintf("SPDY stream closed with error_code: %u", error_code)); | |
| 2676 // TODO(mbelshe): Map from Spdy-protocol errors to something sensical. | |
| 2677 // For now, it doesn't matter much - it is a protocol error. | |
| 2678 CloseActiveStreamIterator(it, ERR_SPDY_PROTOCOL_ERROR); | |
| 2679 } | |
| 2680 } | |
| 2681 | |
| 2682 void SpdySession::OnGoAway(SpdyStreamId last_accepted_stream_id, | |
| 2683 SpdyErrorCode error_code, | |
| 2684 SpdyStringPiece debug_data) { | |
| 2685 CHECK(in_io_loop_); | |
| 2686 | |
| 2687 // TODO(jgraettinger): UMA histogram on |error_code|. | |
| 2688 | |
| 2689 net_log_.AddEvent( | |
| 2690 NetLogEventType::HTTP2_SESSION_RECV_GOAWAY, | |
| 2691 base::Bind(&NetLogSpdyRecvGoAwayCallback, last_accepted_stream_id, | |
| 2692 active_streams_.size(), unclaimed_pushed_streams_.size(), | |
| 2693 error_code, debug_data)); | |
| 2694 MakeUnavailable(); | |
| 2695 if (error_code == ERROR_CODE_HTTP_1_1_REQUIRED) { | |
| 2696 // TODO(bnc): Record histogram with number of open streams capped at 50. | |
| 2697 DoDrainSession(ERR_HTTP_1_1_REQUIRED, "HTTP_1_1_REQUIRED for stream."); | |
| 2698 } else if (error_code == ERROR_CODE_NO_ERROR) { | |
| 2699 StartGoingAway(last_accepted_stream_id, ERR_SPDY_SERVER_REFUSED_STREAM); | |
| 2700 } else { | |
| 2701 StartGoingAway(last_accepted_stream_id, ERR_ABORTED); | |
| 2702 } | |
| 2703 // This is to handle the case when we already don't have any active | |
| 2704 // streams (i.e., StartGoingAway() did nothing). Otherwise, we have | |
| 2705 // active streams and so the last one being closed will finish the | |
| 2706 // going away process (see DeleteStream()). | |
| 2707 MaybeFinishGoingAway(); | |
| 2708 } | |
| 2709 | |
| 2710 void SpdySession::OnDataFrameHeader(SpdyStreamId stream_id, | |
| 2711 size_t length, | |
| 2712 bool fin) { | |
| 2713 CHECK(in_io_loop_); | |
| 2714 | |
| 2715 ActiveStreamMap::iterator it = active_streams_.find(stream_id); | |
| 2716 | |
| 2717 // By the time data comes in, the stream may already be inactive. | |
| 2718 if (it == active_streams_.end()) | |
| 2719 return; | |
| 2720 | |
| 2721 SpdyStream* stream = it->second; | |
| 2722 CHECK_EQ(stream->stream_id(), stream_id); | |
| 2723 | |
| 2724 DCHECK(buffered_spdy_framer_); | |
| 2725 size_t header_len = buffered_spdy_framer_->GetDataFrameMinimumSize(); | |
| 2726 stream->AddRawReceivedBytes(header_len); | |
| 2727 } | |
| 2728 | |
| 2729 void SpdySession::OnStreamFrameData(SpdyStreamId stream_id, | |
| 2730 const char* data, | |
| 2731 size_t len) { | |
| 2732 CHECK(in_io_loop_); | |
| 2733 DCHECK_LT(len, 1u << 24); | |
| 2734 if (net_log().IsCapturing()) { | |
| 2735 net_log().AddEvent( | |
| 2736 NetLogEventType::HTTP2_SESSION_RECV_DATA, | |
| 2737 base::Bind(&NetLogSpdyDataCallback, stream_id, len, false)); | |
| 2738 } | |
| 2739 | |
| 2740 // Build the buffer as early as possible so that we go through the | |
| 2741 // session flow control checks and update | |
| 2742 // |unacked_recv_window_bytes_| properly even when the stream is | |
| 2743 // inactive (since the other side has still reduced its session send | |
| 2744 // window). | |
| 2745 std::unique_ptr<SpdyBuffer> buffer; | |
| 2746 if (data) { | |
| 2747 DCHECK_GT(len, 0u); | |
| 2748 CHECK_LE(len, static_cast<size_t>(kReadBufferSize)); | |
| 2749 buffer.reset(new SpdyBuffer(data, len)); | |
| 2750 | |
| 2751 DecreaseRecvWindowSize(static_cast<int32_t>(len)); | |
| 2752 buffer->AddConsumeCallback(base::Bind(&SpdySession::OnReadBufferConsumed, | |
| 2753 weak_factory_.GetWeakPtr())); | |
| 2754 } else { | |
| 2755 DCHECK_EQ(len, 0u); | |
| 2756 } | |
| 2757 | |
| 2758 ActiveStreamMap::iterator it = active_streams_.find(stream_id); | |
| 2759 | |
| 2760 // By the time data comes in, the stream may already be inactive. | |
| 2761 if (it == active_streams_.end()) | |
| 2762 return; | |
| 2763 | |
| 2764 SpdyStream* stream = it->second; | |
| 2765 CHECK_EQ(stream->stream_id(), stream_id); | |
| 2766 | |
| 2767 stream->AddRawReceivedBytes(len); | |
| 2768 stream->OnDataReceived(std::move(buffer)); | |
| 2769 } | |
| 2770 | |
| 2771 void SpdySession::OnStreamEnd(SpdyStreamId stream_id) { | |
| 2772 CHECK(in_io_loop_); | |
| 2773 if (net_log().IsCapturing()) { | |
| 2774 net_log().AddEvent(NetLogEventType::HTTP2_SESSION_RECV_DATA, | |
| 2775 base::Bind(&NetLogSpdyDataCallback, stream_id, 0, true)); | |
| 2776 } | |
| 2777 | |
| 2778 ActiveStreamMap::iterator it = active_streams_.find(stream_id); | |
| 2779 // By the time data comes in, the stream may already be inactive. | |
| 2780 if (it == active_streams_.end()) | |
| 2781 return; | |
| 2782 | |
| 2783 SpdyStream* stream = it->second; | |
| 2784 CHECK_EQ(stream->stream_id(), stream_id); | |
| 2785 | |
| 2786 stream->OnDataReceived(std::unique_ptr<SpdyBuffer>()); | |
| 2787 } | |
| 2788 | |
| 2789 void SpdySession::OnStreamPadding(SpdyStreamId stream_id, size_t len) { | |
| 2790 CHECK(in_io_loop_); | |
| 2791 | |
| 2792 // Decrease window size because padding bytes are received. | |
| 2793 // Increase window size because padding bytes are consumed (by discarding). | |
| 2794 // Net result: |session_unacked_recv_window_bytes_| increases by |len|, | |
| 2795 // |session_recv_window_size_| does not change. | |
| 2796 DecreaseRecvWindowSize(static_cast<int32_t>(len)); | |
| 2797 IncreaseRecvWindowSize(static_cast<int32_t>(len)); | |
| 2798 | |
| 2799 ActiveStreamMap::iterator it = active_streams_.find(stream_id); | |
| 2800 if (it == active_streams_.end()) | |
| 2801 return; | |
| 2802 it->second->OnPaddingConsumed(len); | |
| 2803 } | |
| 2804 | |
| 2805 void SpdySession::OnSettings() { | |
| 2806 CHECK(in_io_loop_); | |
| 2807 | |
| 2808 if (net_log_.IsCapturing()) { | |
| 2809 net_log_.AddEvent( | |
| 2810 NetLogEventType::HTTP2_SESSION_RECV_SETTINGS, | |
| 2811 base::Bind(&NetLogSpdyRecvSettingsCallback, host_port_pair())); | |
| 2812 } | |
| 2813 | |
| 2814 // Send an acknowledgment of the setting. | |
| 2815 SpdySettingsIR settings_ir; | |
| 2816 settings_ir.set_is_ack(true); | |
| 2817 EnqueueSessionWrite( | |
| 2818 HIGHEST, SpdyFrameType::SETTINGS, | |
| 2819 std::unique_ptr<SpdySerializedFrame>(new SpdySerializedFrame( | |
| 2820 buffered_spdy_framer_->SerializeFrame(settings_ir)))); | |
| 2821 } | |
| 2822 | |
| 2823 void SpdySession::OnSetting(SpdySettingsIds id, uint32_t value) { | |
| 2824 CHECK(in_io_loop_); | |
| 2825 | |
| 2826 HandleSetting(id, value); | |
| 2827 | |
| 2828 // Log the setting. | |
| 2829 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_RECV_SETTING, | |
| 2830 base::Bind(&NetLogSpdyRecvSettingCallback, id, value)); | |
| 2831 } | |
| 2832 | |
| 2833 void SpdySession::OnWindowUpdate(SpdyStreamId stream_id, | |
| 2834 int delta_window_size) { | |
| 2835 CHECK(in_io_loop_); | |
| 2836 | |
| 2837 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_RECV_WINDOW_UPDATE, | |
| 2838 base::Bind(&NetLogSpdyWindowUpdateFrameCallback, stream_id, | |
| 2839 delta_window_size)); | |
| 2840 | |
| 2841 if (stream_id == kSessionFlowControlStreamId) { | |
| 2842 // WINDOW_UPDATE for the session. | |
| 2843 if (delta_window_size < 1) { | |
| 2844 RecordProtocolErrorHistogram(PROTOCOL_ERROR_INVALID_WINDOW_UPDATE_SIZE); | |
| 2845 DoDrainSession( | |
| 2846 ERR_SPDY_PROTOCOL_ERROR, | |
| 2847 "Received WINDOW_UPDATE with an invalid delta_window_size " + | |
| 2848 base::IntToString(delta_window_size)); | |
| 2849 return; | |
| 2850 } | |
| 2851 | |
| 2852 IncreaseSendWindowSize(delta_window_size); | |
| 2853 } else { | |
| 2854 // WINDOW_UPDATE for a stream. | |
| 2855 ActiveStreamMap::iterator it = active_streams_.find(stream_id); | |
| 2856 | |
| 2857 if (it == active_streams_.end()) { | |
| 2858 // NOTE: it may just be that the stream was cancelled. | |
| 2859 LOG(WARNING) << "Received WINDOW_UPDATE for invalid stream " << stream_id; | |
| 2860 return; | |
| 2861 } | |
| 2862 | |
| 2863 SpdyStream* stream = it->second; | |
| 2864 CHECK_EQ(stream->stream_id(), stream_id); | |
| 2865 | |
| 2866 if (delta_window_size < 1) { | |
| 2867 ResetStreamIterator( | |
| 2868 it, ERROR_CODE_FLOW_CONTROL_ERROR, | |
| 2869 SpdyStringPrintf("Received WINDOW_UPDATE with an invalid " | |
| 2870 "delta_window_size %d", | |
| 2871 delta_window_size)); | |
| 2872 return; | |
| 2873 } | |
| 2874 | |
| 2875 CHECK_EQ(it->second->stream_id(), stream_id); | |
| 2876 it->second->IncreaseSendWindowSize(delta_window_size); | |
| 2877 } | |
| 2878 } | |
| 2879 | |
| 2880 void SpdySession::OnPushPromise(SpdyStreamId stream_id, | |
| 2881 SpdyStreamId promised_stream_id, | |
| 2882 SpdyHeaderBlock headers) { | |
| 2883 CHECK(in_io_loop_); | |
| 2884 | |
| 2885 if (net_log_.IsCapturing()) { | |
| 2886 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_RECV_PUSH_PROMISE, | |
| 2887 base::Bind(&NetLogSpdyPushPromiseReceivedCallback, | |
| 2888 &headers, stream_id, promised_stream_id)); | |
| 2889 } | |
| 2890 | |
| 2891 TryCreatePushStream(promised_stream_id, stream_id, std::move(headers)); | |
| 2892 } | |
| 2893 | |
| 2894 void SpdySession::OnHeaders(SpdyStreamId stream_id, | |
| 2895 bool has_priority, | |
| 2896 int weight, | |
| 2897 SpdyStreamId parent_stream_id, | |
| 2898 bool exclusive, | |
| 2899 bool fin, | |
| 2900 SpdyHeaderBlock headers) { | |
| 2901 CHECK(in_io_loop_); | |
| 2902 | |
| 2903 if (net_log().IsCapturing()) { | |
| 2904 net_log().AddEvent(NetLogEventType::HTTP2_SESSION_RECV_HEADERS, | |
| 2905 base::Bind(&NetLogSpdyHeadersReceivedCallback, &headers, | |
| 2906 fin, stream_id)); | |
| 2907 } | |
| 2908 | |
| 2909 ActiveStreamMap::iterator it = active_streams_.find(stream_id); | |
| 2910 if (it == active_streams_.end()) { | |
| 2911 // NOTE: it may just be that the stream was cancelled. | |
| 2912 LOG(WARNING) << "Received HEADERS for invalid stream " << stream_id; | |
| 2913 return; | |
| 2914 } | |
| 2915 | |
| 2916 SpdyStream* stream = it->second; | |
| 2917 CHECK_EQ(stream->stream_id(), stream_id); | |
| 2918 | |
| 2919 stream->AddRawReceivedBytes(last_compressed_frame_len_); | |
| 2920 last_compressed_frame_len_ = 0; | |
| 2921 | |
| 2922 if (it->second->IsReservedRemote()) { | |
| 2923 DCHECK_EQ(SPDY_PUSH_STREAM, stream->type()); | |
| 2924 if (max_concurrent_pushed_streams_ && | |
| 2925 num_active_pushed_streams_ >= max_concurrent_pushed_streams_) { | |
| 2926 ResetStream(stream_id, ERROR_CODE_REFUSED_STREAM, | |
| 2927 "Stream concurrency limit reached."); | |
| 2928 return; | |
| 2929 } | |
| 2930 | |
| 2931 // Will be balanced in DeleteStream. | |
| 2932 num_active_pushed_streams_++; | |
| 2933 } | |
| 2934 | |
| 2935 base::Time response_time = base::Time::Now(); | |
| 2936 base::TimeTicks recv_first_byte_time = time_func_(); | |
| 2937 // May invalidate |stream|. | |
| 2938 stream->OnHeadersReceived(headers, response_time, recv_first_byte_time); | |
| 2939 } | |
| 2940 | |
| 2941 void SpdySession::OnAltSvc( | |
| 2942 SpdyStreamId stream_id, | |
| 2943 SpdyStringPiece origin, | |
| 2944 const SpdyAltSvcWireFormat::AlternativeServiceVector& altsvc_vector) { | |
| 2945 if (!is_secure_) | |
| 2946 return; | |
| 2947 | |
| 2948 url::SchemeHostPort scheme_host_port; | |
| 2949 if (stream_id == 0) { | |
| 2950 if (origin.empty()) | |
| 2951 return; | |
| 2952 const GURL gurl(origin); | |
| 2953 if (!gurl.SchemeIs("https")) | |
| 2954 return; | |
| 2955 SSLInfo ssl_info; | |
| 2956 if (!GetSSLInfo(&ssl_info)) | |
| 2957 return; | |
| 2958 if (!CanPool(transport_security_state_, ssl_info, host_port_pair().host(), | |
| 2959 gurl.host())) { | |
| 2960 return; | |
| 2961 } | |
| 2962 scheme_host_port = url::SchemeHostPort(gurl); | |
| 2963 } else { | |
| 2964 if (!origin.empty()) | |
| 2965 return; | |
| 2966 const ActiveStreamMap::iterator it = active_streams_.find(stream_id); | |
| 2967 if (it == active_streams_.end()) | |
| 2968 return; | |
| 2969 const GURL& gurl(it->second->url()); | |
| 2970 if (!gurl.SchemeIs("https")) | |
| 2971 return; | |
| 2972 scheme_host_port = url::SchemeHostPort(gurl); | |
| 2973 } | |
| 2974 | |
| 2975 AlternativeServiceInfoVector alternative_service_info_vector; | |
| 2976 alternative_service_info_vector.reserve(altsvc_vector.size()); | |
| 2977 const base::Time now(base::Time::Now()); | |
| 2978 for (const SpdyAltSvcWireFormat::AlternativeService& altsvc : altsvc_vector) { | |
| 2979 const NextProto protocol = NextProtoFromString(altsvc.protocol_id); | |
| 2980 if (protocol == kProtoUnknown) | |
| 2981 continue; | |
| 2982 const AlternativeService alternative_service(protocol, altsvc.host, | |
| 2983 altsvc.port); | |
| 2984 const base::Time expiration = | |
| 2985 now + base::TimeDelta::FromSeconds(altsvc.max_age); | |
| 2986 alternative_service_info_vector.push_back( | |
| 2987 AlternativeServiceInfo(alternative_service, expiration)); | |
| 2988 } | |
| 2989 http_server_properties_->SetAlternativeServices( | |
| 2990 scheme_host_port, alternative_service_info_vector); | |
| 2991 } | |
| 2992 | |
| 2993 bool SpdySession::OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) { | |
| 2994 // Validate stream id. | |
| 2995 // Was the frame sent on a stream id that has not been used in this session? | |
| 2996 if (stream_id % 2 == 1 && stream_id > stream_hi_water_mark_) | |
| 2997 return false; | |
| 2998 | |
| 2999 if (stream_id % 2 == 0 && stream_id > last_accepted_push_stream_id_) | |
| 3000 return false; | |
| 3001 | |
| 3002 return true; | |
| 3003 } | |
| 3004 | |
| 3005 void SpdySession::OnSendCompressedFrame(SpdyStreamId stream_id, | |
| 3006 SpdyFrameType type, | |
| 3007 size_t payload_len, | |
| 3008 size_t frame_len) { | |
| 3009 if (type != SpdyFrameType::HEADERS) { | |
| 3010 return; | |
| 3011 } | |
| 3012 | |
| 3013 DCHECK(buffered_spdy_framer_.get()); | |
| 3014 size_t compressed_len = | |
| 3015 frame_len - buffered_spdy_framer_->GetFrameMinimumSize(); | |
| 3016 | |
| 3017 if (payload_len) { | |
| 3018 // Make sure we avoid early decimal truncation. | |
| 3019 int compression_pct = 100 - (100 * compressed_len) / payload_len; | |
| 3020 UMA_HISTOGRAM_PERCENTAGE("Net.SpdyHeadersCompressionPercentage", | |
| 3021 compression_pct); | |
| 3022 } | |
| 3023 } | |
| 3024 | |
| 3025 void SpdySession::OnReceiveCompressedFrame(SpdyStreamId stream_id, | |
| 3026 SpdyFrameType type, | |
| 3027 size_t frame_len) { | |
| 3028 last_compressed_frame_len_ = frame_len; | |
| 3029 } | |
| 3030 | |
| 3031 void SpdySession::OnWriteBufferConsumed( | |
| 3032 size_t frame_payload_size, | |
| 3033 size_t consume_size, | |
| 3034 SpdyBuffer::ConsumeSource consume_source) { | |
| 3035 // We can be called with |in_io_loop_| set if a write SpdyBuffer is | |
| 3036 // deleted (e.g., a stream is closed due to incoming data). | |
| 3037 if (consume_source == SpdyBuffer::DISCARD) { | |
| 3038 // If we're discarding a frame or part of it, increase the send | |
| 3039 // window by the number of discarded bytes. (Although if we're | |
| 3040 // discarding part of a frame, it's probably because of a write | |
| 3041 // error and we'll be tearing down the session soon.) | |
| 3042 int remaining_payload_bytes = std::min(consume_size, frame_payload_size); | |
| 3043 DCHECK_GT(remaining_payload_bytes, 0); | |
| 3044 IncreaseSendWindowSize(remaining_payload_bytes); | |
| 3045 } | |
| 3046 // For consumed bytes, the send window is increased when we receive | |
| 3047 // a WINDOW_UPDATE frame. | |
| 3048 } | |
| 3049 | |
| 3050 void SpdySession::IncreaseSendWindowSize(int delta_window_size) { | |
| 3051 // We can be called with |in_io_loop_| set if a SpdyBuffer is | |
| 3052 // deleted (e.g., a stream is closed due to incoming data). | |
| 3053 DCHECK_GE(delta_window_size, 1); | |
| 3054 | |
| 3055 // Check for overflow. | |
| 3056 int32_t max_delta_window_size = | |
| 3057 std::numeric_limits<int32_t>::max() - session_send_window_size_; | |
| 3058 if (delta_window_size > max_delta_window_size) { | |
| 3059 RecordProtocolErrorHistogram(PROTOCOL_ERROR_INVALID_WINDOW_UPDATE_SIZE); | |
| 3060 DoDrainSession( | |
| 3061 ERR_SPDY_PROTOCOL_ERROR, | |
| 3062 "Received WINDOW_UPDATE [delta: " + | |
| 3063 base::IntToString(delta_window_size) + | |
| 3064 "] for session overflows session_send_window_size_ [current: " + | |
| 3065 base::IntToString(session_send_window_size_) + "]"); | |
| 3066 return; | |
| 3067 } | |
| 3068 | |
| 3069 session_send_window_size_ += delta_window_size; | |
| 3070 | |
| 3071 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_UPDATE_SEND_WINDOW, | |
| 3072 base::Bind(&NetLogSpdySessionWindowUpdateCallback, | |
| 3073 delta_window_size, session_send_window_size_)); | |
| 3074 | |
| 3075 DCHECK(!IsSendStalled()); | |
| 3076 ResumeSendStalledStreams(); | |
| 3077 } | |
| 3078 | |
| 3079 void SpdySession::DecreaseSendWindowSize(int32_t delta_window_size) { | |
| 3080 // We only call this method when sending a frame. Therefore, | |
| 3081 // |delta_window_size| should be within the valid frame size range. | |
| 3082 DCHECK_GE(delta_window_size, 1); | |
| 3083 DCHECK_LE(delta_window_size, kMaxSpdyFrameChunkSize); | |
| 3084 | |
| 3085 // |send_window_size_| should have been at least |delta_window_size| for | |
| 3086 // this call to happen. | |
| 3087 DCHECK_GE(session_send_window_size_, delta_window_size); | |
| 3088 | |
| 3089 session_send_window_size_ -= delta_window_size; | |
| 3090 | |
| 3091 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_UPDATE_SEND_WINDOW, | |
| 3092 base::Bind(&NetLogSpdySessionWindowUpdateCallback, | |
| 3093 -delta_window_size, session_send_window_size_)); | |
| 3094 } | |
| 3095 | |
| 3096 void SpdySession::OnReadBufferConsumed( | |
| 3097 size_t consume_size, | |
| 3098 SpdyBuffer::ConsumeSource consume_source) { | |
| 3099 // We can be called with |in_io_loop_| set if a read SpdyBuffer is | |
| 3100 // deleted (e.g., discarded by a SpdyReadQueue). | |
| 3101 DCHECK_GE(consume_size, 1u); | |
| 3102 DCHECK_LE(consume_size, | |
| 3103 static_cast<size_t>(std::numeric_limits<int32_t>::max())); | |
| 3104 | |
| 3105 IncreaseRecvWindowSize(static_cast<int32_t>(consume_size)); | |
| 3106 } | |
| 3107 | |
| 3108 void SpdySession::IncreaseRecvWindowSize(int32_t delta_window_size) { | |
| 3109 DCHECK_GE(session_unacked_recv_window_bytes_, 0); | |
| 3110 DCHECK_GE(session_recv_window_size_, session_unacked_recv_window_bytes_); | |
| 3111 DCHECK_GE(delta_window_size, 1); | |
| 3112 // Check for overflow. | |
| 3113 DCHECK_LE(delta_window_size, | |
| 3114 std::numeric_limits<int32_t>::max() - session_recv_window_size_); | |
| 3115 | |
| 3116 session_recv_window_size_ += delta_window_size; | |
| 3117 net_log_.AddEvent(NetLogEventType::HTTP2_STREAM_UPDATE_RECV_WINDOW, | |
| 3118 base::Bind(&NetLogSpdySessionWindowUpdateCallback, | |
| 3119 delta_window_size, session_recv_window_size_)); | |
| 3120 | |
| 3121 session_unacked_recv_window_bytes_ += delta_window_size; | |
| 3122 if (session_unacked_recv_window_bytes_ > session_max_recv_window_size_ / 2) { | |
| 3123 SendWindowUpdateFrame(kSessionFlowControlStreamId, | |
| 3124 session_unacked_recv_window_bytes_, HIGHEST); | |
| 3125 session_unacked_recv_window_bytes_ = 0; | |
| 3126 } | |
| 3127 } | |
| 3128 | |
| 3129 void SpdySession::DecreaseRecvWindowSize(int32_t delta_window_size) { | |
| 3130 CHECK(in_io_loop_); | |
| 3131 DCHECK_GE(delta_window_size, 1); | |
| 3132 | |
| 3133 // The receiving window size as the peer knows it is | |
| 3134 // |session_recv_window_size_ - session_unacked_recv_window_bytes_|, if more | |
| 3135 // data are sent by the peer, that means that the receive window is not being | |
| 3136 // respected. | |
| 3137 if (delta_window_size > | |
| 3138 session_recv_window_size_ - session_unacked_recv_window_bytes_) { | |
| 3139 RecordProtocolErrorHistogram(PROTOCOL_ERROR_RECEIVE_WINDOW_VIOLATION); | |
| 3140 DoDrainSession( | |
| 3141 ERR_SPDY_FLOW_CONTROL_ERROR, | |
| 3142 "delta_window_size is " + base::IntToString(delta_window_size) + | |
| 3143 " in DecreaseRecvWindowSize, which is larger than the receive " + | |
| 3144 "window size of " + base::IntToString(session_recv_window_size_)); | |
| 3145 return; | |
| 3146 } | |
| 3147 | |
| 3148 session_recv_window_size_ -= delta_window_size; | |
| 3149 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_UPDATE_RECV_WINDOW, | |
| 3150 base::Bind(&NetLogSpdySessionWindowUpdateCallback, | |
| 3151 -delta_window_size, session_recv_window_size_)); | |
| 3152 } | |
| 3153 | |
| 3154 void SpdySession::QueueSendStalledStream(const SpdyStream& stream) { | |
| 3155 DCHECK(stream.send_stalled_by_flow_control()); | |
| 3156 RequestPriority priority = stream.priority(); | |
| 3157 CHECK_GE(priority, MINIMUM_PRIORITY); | |
| 3158 CHECK_LE(priority, MAXIMUM_PRIORITY); | |
| 3159 stream_send_unstall_queue_[priority].push_back(stream.stream_id()); | |
| 3160 } | |
| 3161 | |
| 3162 void SpdySession::ResumeSendStalledStreams() { | |
| 3163 // We don't have to worry about new streams being queued, since | |
| 3164 // doing so would cause IsSendStalled() to return true. But we do | |
| 3165 // have to worry about streams being closed, as well as ourselves | |
| 3166 // being closed. | |
| 3167 | |
| 3168 while (!IsSendStalled()) { | |
| 3169 size_t old_size = 0; | |
| 3170 #if DCHECK_IS_ON() | |
| 3171 old_size = GetTotalSize(stream_send_unstall_queue_); | |
| 3172 #endif | |
| 3173 | |
| 3174 SpdyStreamId stream_id = PopStreamToPossiblyResume(); | |
| 3175 if (stream_id == 0) | |
| 3176 break; | |
| 3177 ActiveStreamMap::const_iterator it = active_streams_.find(stream_id); | |
| 3178 // The stream may actually still be send-stalled after this (due | |
| 3179 // to its own send window) but that's okay -- it'll then be | |
| 3180 // resumed once its send window increases. | |
| 3181 if (it != active_streams_.end()) | |
| 3182 it->second->PossiblyResumeIfSendStalled(); | |
| 3183 | |
| 3184 // The size should decrease unless we got send-stalled again. | |
| 3185 if (!IsSendStalled()) | |
| 3186 DCHECK_LT(GetTotalSize(stream_send_unstall_queue_), old_size); | |
| 3187 } | |
| 3188 } | |
| 3189 | |
| 3190 SpdyStreamId SpdySession::PopStreamToPossiblyResume() { | |
| 3191 for (int i = MAXIMUM_PRIORITY; i >= MINIMUM_PRIORITY; --i) { | |
| 3192 std::deque<SpdyStreamId>* queue = &stream_send_unstall_queue_[i]; | |
| 3193 if (!queue->empty()) { | |
| 3194 SpdyStreamId stream_id = queue->front(); | |
| 3195 queue->pop_front(); | |
| 3196 return stream_id; | |
| 3197 } | |
| 3198 } | |
| 3199 return 0; | |
| 3200 } | |
| 3201 | |
| 3202 } // namespace net | |
| OLD | NEW |