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