1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/spdy/spdy_session.h"
6
7 #include <algorithm>
8 #include <limits>
9 #include <map>
10 #include <string>
11 #include <utility>
12
13 #include "base/bind.h"
14 #include "base/location.h"
15 #include "base/logging.h"
16 #include "base/metrics/histogram_functions.h"
17 #include "base/metrics/histogram_macros.h"
18 #include "base/single_thread_task_runner.h"
19 #include "base/stl_util.h"
20 #include "base/strings/string_number_conversions.h"
21 #include "base/strings/string_split.h"
22 #include "base/strings/string_util.h"
23 #include "base/strings/stringprintf.h"
24 #include "base/strings/utf_string_conversions.h"
25 #include "base/threading/thread_task_runner_handle.h"
26 #include "base/time/time.h"
27 #include "base/trace_event/memory_usage_estimator.h"
28 #include "base/trace_event/trace_event.h"
29 #include "base/values.h"
30 #include "net/base/url_util.h"
31 #include "net/cert/asn1_util.h"
32 #include "net/cert/cert_verify_result.h"
33 #include "net/cert/ct_policy_status.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/http_vary_data.h"
38 #include "net/http/transport_security_state.h"
39 #include "net/log/net_log.h"
40 #include "net/log/net_log_capture_mode.h"
41 #include "net/log/net_log_event_type.h"
42 #include "net/log/net_log_source_type.h"
43 #include "net/log/net_log_with_source.h"
44 #include "net/nqe/network_quality_estimator.h"
45 #include "net/quic/quic_http_utils.h"
46 #include "net/socket/client_socket_handle.h"
47 #include "net/socket/socket.h"
48 #include "net/socket/ssl_client_socket.h"
49 #include "net/spdy/header_coalescer.h"
50 #include "net/spdy/spdy_buffer_producer.h"
51 #include "net/spdy/spdy_http_utils.h"
52 #include "net/spdy/spdy_log_util.h"
53 #include "net/spdy/spdy_session_pool.h"
54 #include "net/spdy/spdy_stream.h"
55 #include "net/ssl/ssl_cipher_suite_names.h"
56 #include "net/ssl/ssl_connection_status_flags.h"
57 #include "net/third_party/quiche/src/quic/core/http/spdy_server_push_utils.h"
58 #include "net/third_party/quiche/src/spdy/core/spdy_frame_builder.h"
59 #include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
60 #include "url/scheme_host_port.h"
61 #include "url/url_constants.h"
62
63 namespace net {
64
65 namespace {
66
67 constexpr net::NetworkTrafficAnnotationTag
68 kSpdySessionCommandsTrafficAnnotation =
69 net::DefineNetworkTrafficAnnotation("spdy_session_control", R"(
70 semantics {
71 sender: "Spdy Session"
72 description:
73 "Sends commands to control an HTTP/2 session."
74 trigger:
75 "Required control commands like initiating stream, requesting "
76 "stream reset, changing priorities, etc."
77 data: "No user data."
78 destination: OTHER
79 destination_other:
80 "Any destination the HTTP/2 session is connected to."
81 }
82 policy {
83 cookies_allowed: NO
84 setting: "This feature cannot be disabled in settings."
85 policy_exception_justification: "Essential for network access."
86 }
87 )");
88
89 const int kReadBufferSize = 8 * 1024;
90 const int kDefaultConnectionAtRiskOfLossSeconds = 10;
91 const int kHungIntervalSeconds = 10;
92
93 // Lifetime of unclaimed pushed stream, in seconds: after this period, a pushed
94 // stream is cancelled if still not claimed.
95 const int kPushedStreamLifetimeSeconds = 300;
96
97 // Default initial value for HTTP/2 SETTINGS.
98 const uint32_t kDefaultInitialHeaderTableSize = 4096;
99 const uint32_t kDefaultInitialEnablePush = 1;
100 const uint32_t kDefaultInitialInitialWindowSize = 65535;
101 const uint32_t kDefaultInitialMaxFrameSize = 16384;
102
103 // Values of Vary response header on pushed streams. This is logged to
104 // Net.PushedStreamVaryResponseHeader, entries must not be changed.
105 enum PushedStreamVaryResponseHeaderValues {
106 // There is no Vary header.
107 kNoVaryHeader = 0,
108 // The value of Vary is empty.
109 kVaryIsEmpty = 1,
110 // The value of Vary is "*".
111 kVaryIsStar = 2,
112 // The value of Vary is "accept-encoding" (case insensitive).
113 kVaryIsAcceptEncoding = 3,
114 // The value of Vary contains "accept-encoding" (case insensitive) and some
115 // other field names as well.
116 kVaryHasAcceptEncoding = 4,
117 // The value of Vary does not contain "accept-encoding", is not empty, and is
118 // not "*".
119 kVaryHasNoAcceptEncoding = 5,
120 // The number of entries above.
121 kNumberOfVaryEntries = 6
122 };
123
124 // String literals for parsing the Vary header in a pushed response.
125 const char kVary[] = "vary";
126 const char kStar[] = "*";
127 const char kAcceptEncoding[] = "accept-encoding";
128
ParseVaryInPushedResponse(const spdy::SpdyHeaderBlock & headers)129 enum PushedStreamVaryResponseHeaderValues ParseVaryInPushedResponse(
130 const spdy::SpdyHeaderBlock& headers) {
131 spdy::SpdyHeaderBlock::iterator it = headers.find(kVary);
132 if (it == headers.end())
133 return kNoVaryHeader;
134 base::StringPiece value(it->second);
135 if (value.empty())
136 return kVaryIsEmpty;
137 if (value == kStar)
138 return kVaryIsStar;
139 std::string lowercase_value = ToLowerASCII(value);
140 if (lowercase_value == kAcceptEncoding)
141 return kVaryIsAcceptEncoding;
142 // Both comma and newline delimiters occur in the wild.
143 for (const auto& substr :
144 SplitString(lowercase_value, ",\n", base::TRIM_WHITESPACE,
145 base::SPLIT_WANT_NONEMPTY)) {
146 if (substr == kAcceptEncoding)
147 return kVaryHasAcceptEncoding;
148 }
149
150 return kVaryHasNoAcceptEncoding;
151 }
152
153 // A SpdyBufferProducer implementation that creates an HTTP/2 frame by adding
154 // stream ID to greased frame parameters.
155 class GreasedBufferProducer : public SpdyBufferProducer {
156 public:
157 GreasedBufferProducer() = delete;
GreasedBufferProducer(base::WeakPtr<SpdyStream> stream,const SpdySessionPool::GreasedHttp2Frame * greased_http2_frame,BufferedSpdyFramer * buffered_spdy_framer)158 GreasedBufferProducer(
159 base::WeakPtr<SpdyStream> stream,
160 const SpdySessionPool::GreasedHttp2Frame* greased_http2_frame,
161 BufferedSpdyFramer* buffered_spdy_framer)
162 : stream_(stream),
163 greased_http2_frame_(greased_http2_frame),
164 buffered_spdy_framer_(buffered_spdy_framer) {}
165
166 ~GreasedBufferProducer() override = default;
167
ProduceBuffer()168 std::unique_ptr<SpdyBuffer> ProduceBuffer() override {
169 const spdy::SpdyStreamId stream_id = stream_ ? stream_->stream_id() : 0;
170 spdy::SpdyUnknownIR frame(stream_id, greased_http2_frame_->type,
171 greased_http2_frame_->flags,
172 greased_http2_frame_->payload);
173 auto serialized_frame = std::make_unique<spdy::SpdySerializedFrame>(
174 buffered_spdy_framer_->SerializeFrame(frame));
175 return std::make_unique<SpdyBuffer>(std::move(serialized_frame));
176 }
177
EstimateMemoryUsage() const178 size_t EstimateMemoryUsage() const override {
179 return base::trace_event::EstimateMemoryUsage(
180 greased_http2_frame_->payload);
181 }
182
183 private:
184 base::WeakPtr<SpdyStream> stream_;
185 const SpdySessionPool::GreasedHttp2Frame* const greased_http2_frame_;
186 BufferedSpdyFramer* buffered_spdy_framer_;
187 };
188
IsSpdySettingAtDefaultInitialValue(spdy::SpdySettingsId setting_id,uint32_t value)189 bool IsSpdySettingAtDefaultInitialValue(spdy::SpdySettingsId setting_id,
190 uint32_t value) {
191 switch (setting_id) {
192 case spdy::SETTINGS_HEADER_TABLE_SIZE:
193 return value == kDefaultInitialHeaderTableSize;
194 case spdy::SETTINGS_ENABLE_PUSH:
195 return value == kDefaultInitialEnablePush;
196 case spdy::SETTINGS_MAX_CONCURRENT_STREAMS:
197 // There is no initial limit on the number of concurrent streams.
198 return false;
199 case spdy::SETTINGS_INITIAL_WINDOW_SIZE:
200 return value == kDefaultInitialInitialWindowSize;
201 case spdy::SETTINGS_MAX_FRAME_SIZE:
202 return value == kDefaultInitialMaxFrameSize;
203 case spdy::SETTINGS_MAX_HEADER_LIST_SIZE:
204 // There is no initial limit on the size of the header list.
205 return false;
206 case spdy::SETTINGS_ENABLE_CONNECT_PROTOCOL:
207 return value == 0;
208 default:
209 // Undefined parameters have no initial value.
210 return false;
211 }
212 }
213
IsPushEnabled(const spdy::SettingsMap & initial_settings)214 bool IsPushEnabled(const spdy::SettingsMap& initial_settings) {
215 const auto it = initial_settings.find(spdy::SETTINGS_ENABLE_PUSH);
216
217 // Push is enabled by default.
218 if (it == initial_settings.end())
219 return true;
220
221 return it->second == 1;
222 }
223
NetLogSpdyHeadersSentParams(const spdy::SpdyHeaderBlock * headers,bool fin,spdy::SpdyStreamId stream_id,bool has_priority,int weight,spdy::SpdyStreamId parent_stream_id,bool exclusive,NetLogSource source_dependency,NetLogCaptureMode capture_mode)224 base::Value NetLogSpdyHeadersSentParams(const spdy::SpdyHeaderBlock* headers,
225 bool fin,
226 spdy::SpdyStreamId stream_id,
227 bool has_priority,
228 int weight,
229 spdy::SpdyStreamId parent_stream_id,
230 bool exclusive,
231 NetLogSource source_dependency,
232 NetLogCaptureMode capture_mode) {
233 base::Value dict(base::Value::Type::DICTIONARY);
234 dict.SetKey("headers", ElideSpdyHeaderBlockForNetLog(*headers, capture_mode));
235 dict.SetBoolKey("fin", fin);
236 dict.SetIntKey("stream_id", stream_id);
237 dict.SetBoolKey("has_priority", has_priority);
238 if (has_priority) {
239 dict.SetIntKey("parent_stream_id", parent_stream_id);
240 dict.SetIntKey("weight", weight);
241 dict.SetBoolKey("exclusive", exclusive);
242 }
243 if (source_dependency.IsValid()) {
244 source_dependency.AddToEventParameters(&dict);
245 }
246 return dict;
247 }
248
NetLogSpdyHeadersReceivedParams(const spdy::SpdyHeaderBlock * headers,bool fin,spdy::SpdyStreamId stream_id,NetLogCaptureMode capture_mode)249 base::Value NetLogSpdyHeadersReceivedParams(
250 const spdy::SpdyHeaderBlock* headers,
251 bool fin,
252 spdy::SpdyStreamId stream_id,
253 NetLogCaptureMode capture_mode) {
254 base::Value dict(base::Value::Type::DICTIONARY);
255 dict.SetKey("headers", ElideSpdyHeaderBlockForNetLog(*headers, capture_mode));
256 dict.SetBoolKey("fin", fin);
257 dict.SetIntKey("stream_id", stream_id);
258 return dict;
259 }
260
NetLogSpdySessionCloseParams(int net_error,const std::string & description)261 base::Value NetLogSpdySessionCloseParams(int net_error,
262 const std::string& description) {
263 base::Value dict(base::Value::Type::DICTIONARY);
264 dict.SetIntKey("net_error", net_error);
265 dict.SetStringKey("description", description);
266 return dict;
267 }
268
NetLogSpdySessionParams(const HostPortProxyPair & host_pair)269 base::Value NetLogSpdySessionParams(const HostPortProxyPair& host_pair) {
270 base::Value dict(base::Value::Type::DICTIONARY);
271 dict.SetStringKey("host", host_pair.first.ToString());
272 dict.SetStringKey("proxy", host_pair.second.ToPacString());
273 return dict;
274 }
275
NetLogSpdyInitializedParams(NetLogSource source)276 base::Value NetLogSpdyInitializedParams(NetLogSource source) {
277 base::Value dict(base::Value::Type::DICTIONARY);
278 if (source.IsValid()) {
279 source.AddToEventParameters(&dict);
280 }
281 dict.SetStringKey("protocol", NextProtoToString(kProtoHTTP2));
282 return dict;
283 }
284
NetLogSpdySendSettingsParams(const spdy::SettingsMap * settings)285 base::Value NetLogSpdySendSettingsParams(const spdy::SettingsMap* settings) {
286 base::Value dict(base::Value::Type::DICTIONARY);
287 base::ListValue settings_list;
288 for (auto it = settings->begin(); it != settings->end(); ++it) {
289 const spdy::SpdySettingsId id = it->first;
290 const uint32_t value = it->second;
291 settings_list.AppendString(
292 base::StringPrintf("[id:%u (%s) value:%u]", id,
293 spdy::SettingsIdToString(id).c_str(), value));
294 }
295 dict.SetKey("settings", std::move(settings_list));
296 return dict;
297 }
298
NetLogSpdyRecvSettingParams(spdy::SpdySettingsId id,uint32_t value)299 base::Value NetLogSpdyRecvSettingParams(spdy::SpdySettingsId id,
300 uint32_t value) {
301 base::Value dict(base::Value::Type::DICTIONARY);
302 dict.SetStringKey(
303 "id",
304 base::StringPrintf("%u (%s)", id, spdy::SettingsIdToString(id).c_str()));
305 dict.SetIntKey("value", value);
306 return dict;
307 }
308
NetLogSpdyWindowUpdateFrameParams(spdy::SpdyStreamId stream_id,uint32_t delta)309 base::Value NetLogSpdyWindowUpdateFrameParams(spdy::SpdyStreamId stream_id,
310 uint32_t delta) {
311 base::Value dict(base::Value::Type::DICTIONARY);
312 dict.SetIntKey("stream_id", static_cast<int>(stream_id));
313 dict.SetIntKey("delta", delta);
314 return dict;
315 }
316
NetLogSpdySessionWindowUpdateParams(int32_t delta,int32_t window_size)317 base::Value NetLogSpdySessionWindowUpdateParams(int32_t delta,
318 int32_t window_size) {
319 base::Value dict(base::Value::Type::DICTIONARY);
320 dict.SetIntKey("delta", delta);
321 dict.SetIntKey("window_size", window_size);
322 return dict;
323 }
324
NetLogSpdyDataParams(spdy::SpdyStreamId stream_id,int size,bool fin)325 base::Value NetLogSpdyDataParams(spdy::SpdyStreamId stream_id,
326 int size,
327 bool fin) {
328 base::Value dict(base::Value::Type::DICTIONARY);
329 dict.SetIntKey("stream_id", static_cast<int>(stream_id));
330 dict.SetIntKey("size", size);
331 dict.SetBoolKey("fin", fin);
332 return dict;
333 }
334
NetLogSpdyRecvRstStreamParams(spdy::SpdyStreamId stream_id,spdy::SpdyErrorCode error_code)335 base::Value NetLogSpdyRecvRstStreamParams(spdy::SpdyStreamId stream_id,
336 spdy::SpdyErrorCode error_code) {
337 base::Value dict(base::Value::Type::DICTIONARY);
338 dict.SetIntKey("stream_id", static_cast<int>(stream_id));
339 dict.SetStringKey(
340 "error_code",
341 base::StringPrintf("%u (%s)", error_code, ErrorCodeToString(error_code)));
342 return dict;
343 }
344
NetLogSpdySendRstStreamParams(spdy::SpdyStreamId stream_id,spdy::SpdyErrorCode error_code,const std::string & description)345 base::Value NetLogSpdySendRstStreamParams(spdy::SpdyStreamId stream_id,
346 spdy::SpdyErrorCode error_code,
347 const std::string& description) {
348 base::Value dict(base::Value::Type::DICTIONARY);
349 dict.SetIntKey("stream_id", static_cast<int>(stream_id));
350 dict.SetStringKey(
351 "error_code",
352 base::StringPrintf("%u (%s)", error_code, ErrorCodeToString(error_code)));
353 dict.SetStringKey("description", description);
354 return dict;
355 }
356
NetLogSpdyPingParams(spdy::SpdyPingId unique_id,bool is_ack,const char * type)357 base::Value NetLogSpdyPingParams(spdy::SpdyPingId unique_id,
358 bool is_ack,
359 const char* type) {
360 base::Value dict(base::Value::Type::DICTIONARY);
361 dict.SetIntKey("unique_id", static_cast<int>(unique_id));
362 dict.SetStringKey("type", type);
363 dict.SetBoolKey("is_ack", is_ack);
364 return dict;
365 }
366
NetLogSpdyRecvGoAwayParams(spdy::SpdyStreamId last_stream_id,int active_streams,int unclaimed_streams,spdy::SpdyErrorCode error_code,base::StringPiece debug_data,NetLogCaptureMode capture_mode)367 base::Value NetLogSpdyRecvGoAwayParams(spdy::SpdyStreamId last_stream_id,
368 int active_streams,
369 int unclaimed_streams,
370 spdy::SpdyErrorCode error_code,
371 base::StringPiece debug_data,
372 NetLogCaptureMode capture_mode) {
373 base::Value dict(base::Value::Type::DICTIONARY);
374 dict.SetIntKey("last_accepted_stream_id", static_cast<int>(last_stream_id));
375 dict.SetIntKey("active_streams", active_streams);
376 dict.SetIntKey("unclaimed_streams", unclaimed_streams);
377 dict.SetStringKey(
378 "error_code",
379 base::StringPrintf("%u (%s)", error_code, ErrorCodeToString(error_code)));
380 dict.SetKey("debug_data",
381 ElideGoAwayDebugDataForNetLog(capture_mode, debug_data));
382 return dict;
383 }
384
NetLogSpdyPushPromiseReceivedParams(const spdy::SpdyHeaderBlock * headers,spdy::SpdyStreamId stream_id,spdy::SpdyStreamId promised_stream_id,NetLogCaptureMode capture_mode)385 base::Value NetLogSpdyPushPromiseReceivedParams(
386 const spdy::SpdyHeaderBlock* headers,
387 spdy::SpdyStreamId stream_id,
388 spdy::SpdyStreamId promised_stream_id,
389 NetLogCaptureMode capture_mode) {
390 base::Value dict(base::Value::Type::DICTIONARY);
391 dict.SetKey("headers", ElideSpdyHeaderBlockForNetLog(*headers, capture_mode));
392 dict.SetIntKey("id", stream_id);
393 dict.SetIntKey("promised_stream_id", promised_stream_id);
394 return dict;
395 }
396
NetLogSpdyAdoptedPushStreamParams(spdy::SpdyStreamId stream_id,const GURL & url)397 base::Value NetLogSpdyAdoptedPushStreamParams(spdy::SpdyStreamId stream_id,
398 const GURL& url) {
399 base::Value dict(base::Value::Type::DICTIONARY);
400 dict.SetIntKey("stream_id", stream_id);
401 dict.SetStringKey("url", url.spec());
402 return dict;
403 }
404
NetLogSpdySessionStalledParams(size_t num_active_streams,size_t num_created_streams,size_t num_pushed_streams,size_t max_concurrent_streams,const std::string & url)405 base::Value NetLogSpdySessionStalledParams(size_t num_active_streams,
406 size_t num_created_streams,
407 size_t num_pushed_streams,
408 size_t max_concurrent_streams,
409 const std::string& url) {
410 base::Value dict(base::Value::Type::DICTIONARY);
411 dict.SetIntKey("num_active_streams", num_active_streams);
412 dict.SetIntKey("num_created_streams", num_created_streams);
413 dict.SetIntKey("num_pushed_streams", num_pushed_streams);
414 dict.SetIntKey("max_concurrent_streams", max_concurrent_streams);
415 dict.SetStringKey("url", url);
416 return dict;
417 }
418
NetLogSpdyPriorityParams(spdy::SpdyStreamId stream_id,spdy::SpdyStreamId parent_stream_id,int weight,bool exclusive)419 base::Value NetLogSpdyPriorityParams(spdy::SpdyStreamId stream_id,
420 spdy::SpdyStreamId parent_stream_id,
421 int weight,
422 bool exclusive) {
423 base::Value dict(base::Value::Type::DICTIONARY);
424 dict.SetIntKey("stream_id", stream_id);
425 dict.SetIntKey("parent_stream_id", parent_stream_id);
426 dict.SetIntKey("weight", weight);
427 dict.SetBoolKey("exclusive", exclusive);
428 return dict;
429 }
430
431 // Helper function to return the total size of an array of objects
432 // with .size() member functions.
433 template <typename T, size_t N>
GetTotalSize(const T (& arr)[N])434 size_t GetTotalSize(const T (&arr)[N]) {
435 size_t total_size = 0;
436 for (size_t i = 0; i < N; ++i) {
437 total_size += arr[i].size();
438 }
439 return total_size;
440 }
441
442 // Helper class for std:find_if on STL container containing
443 // SpdyStreamRequest weak pointers.
444 class RequestEquals {
445 public:
RequestEquals(const base::WeakPtr<SpdyStreamRequest> & request)446 explicit RequestEquals(const base::WeakPtr<SpdyStreamRequest>& request)
447 : request_(request) {}
448
operator ()(const base::WeakPtr<SpdyStreamRequest> & request) const449 bool operator()(const base::WeakPtr<SpdyStreamRequest>& request) const {
450 return request_.get() == request.get();
451 }
452
453 private:
454 const base::WeakPtr<SpdyStreamRequest> request_;
455 };
456
457 // The maximum number of concurrent streams we will ever create. Even if
458 // the server permits more, we will never exceed this limit.
459 const size_t kMaxConcurrentStreamLimit = 256;
460
461 class SpdyServerPushHelper : public ServerPushDelegate::ServerPushHelper {
462 public:
SpdyServerPushHelper(base::WeakPtr<SpdySession> session,const GURL & url)463 explicit SpdyServerPushHelper(base::WeakPtr<SpdySession> session,
464 const GURL& url)
465 : session_(session), request_url_(url) {}
466
Cancel()467 void Cancel() override {
468 if (session_)
469 session_->CancelPush(request_url_);
470 }
471
GetURL() const472 const GURL& GetURL() const override { return request_url_; }
473
GetNetworkIsolationKey() const474 NetworkIsolationKey GetNetworkIsolationKey() const override {
475 if (session_) {
476 return session_->spdy_session_key().network_isolation_key();
477 }
478 return NetworkIsolationKey();
479 }
480
481 private:
482 base::WeakPtr<SpdySession> session_;
483 const GURL request_url_;
484 };
485
486 } // namespace
487
MapFramerErrorToProtocolError(http2::Http2DecoderAdapter::SpdyFramerError err)488 SpdyProtocolErrorDetails MapFramerErrorToProtocolError(
489 http2::Http2DecoderAdapter::SpdyFramerError err) {
490 switch (err) {
491 case http2::Http2DecoderAdapter::SPDY_NO_ERROR:
492 return SPDY_ERROR_NO_ERROR;
493 case http2::Http2DecoderAdapter::SPDY_INVALID_STREAM_ID:
494 return SPDY_ERROR_INVALID_STREAM_ID;
495 case http2::Http2DecoderAdapter::SPDY_INVALID_CONTROL_FRAME:
496 return SPDY_ERROR_INVALID_CONTROL_FRAME;
497 case http2::Http2DecoderAdapter::SPDY_CONTROL_PAYLOAD_TOO_LARGE:
498 return SPDY_ERROR_CONTROL_PAYLOAD_TOO_LARGE;
499 case http2::Http2DecoderAdapter::SPDY_ZLIB_INIT_FAILURE:
500 return SPDY_ERROR_ZLIB_INIT_FAILURE;
501 case http2::Http2DecoderAdapter::SPDY_UNSUPPORTED_VERSION:
502 return SPDY_ERROR_UNSUPPORTED_VERSION;
503 case http2::Http2DecoderAdapter::SPDY_DECOMPRESS_FAILURE:
504 return SPDY_ERROR_DECOMPRESS_FAILURE;
505 case http2::Http2DecoderAdapter::SPDY_COMPRESS_FAILURE:
506 return SPDY_ERROR_COMPRESS_FAILURE;
507 case http2::Http2DecoderAdapter::SPDY_GOAWAY_FRAME_CORRUPT:
508 return SPDY_ERROR_GOAWAY_FRAME_CORRUPT;
509 case http2::Http2DecoderAdapter::SPDY_RST_STREAM_FRAME_CORRUPT:
510 return SPDY_ERROR_RST_STREAM_FRAME_CORRUPT;
511 case http2::Http2DecoderAdapter::SPDY_INVALID_PADDING:
512 return SPDY_ERROR_INVALID_PADDING;
513 case http2::Http2DecoderAdapter::SPDY_INVALID_DATA_FRAME_FLAGS:
514 return SPDY_ERROR_INVALID_DATA_FRAME_FLAGS;
515 case http2::Http2DecoderAdapter::SPDY_INVALID_CONTROL_FRAME_FLAGS:
516 return SPDY_ERROR_INVALID_CONTROL_FRAME_FLAGS;
517 case http2::Http2DecoderAdapter::SPDY_UNEXPECTED_FRAME:
518 return SPDY_ERROR_UNEXPECTED_FRAME;
519 case http2::Http2DecoderAdapter::SPDY_INTERNAL_FRAMER_ERROR:
520 return SPDY_ERROR_INTERNAL_FRAMER_ERROR;
521 case http2::Http2DecoderAdapter::SPDY_INVALID_CONTROL_FRAME_SIZE:
522 return SPDY_ERROR_INVALID_CONTROL_FRAME_SIZE;
523 case http2::Http2DecoderAdapter::SPDY_OVERSIZED_PAYLOAD:
524 return SPDY_ERROR_OVERSIZED_PAYLOAD;
525 case http2::Http2DecoderAdapter::SPDY_HPACK_INDEX_VARINT_ERROR:
526 return SPDY_ERROR_HPACK_INDEX_VARINT_ERROR;
527 case http2::Http2DecoderAdapter::SPDY_HPACK_NAME_LENGTH_VARINT_ERROR:
528 return SPDY_ERROR_HPACK_NAME_LENGTH_VARINT_ERROR;
529 case http2::Http2DecoderAdapter::SPDY_HPACK_VALUE_LENGTH_VARINT_ERROR:
530 return SPDY_ERROR_HPACK_VALUE_LENGTH_VARINT_ERROR;
531 case http2::Http2DecoderAdapter::SPDY_HPACK_NAME_TOO_LONG:
532 return SPDY_ERROR_HPACK_NAME_TOO_LONG;
533 case http2::Http2DecoderAdapter::SPDY_HPACK_VALUE_TOO_LONG:
534 return SPDY_ERROR_HPACK_VALUE_TOO_LONG;
535 case http2::Http2DecoderAdapter::SPDY_HPACK_NAME_HUFFMAN_ERROR:
536 return SPDY_ERROR_HPACK_NAME_HUFFMAN_ERROR;
537 case http2::Http2DecoderAdapter::SPDY_HPACK_VALUE_HUFFMAN_ERROR:
538 return SPDY_ERROR_HPACK_VALUE_HUFFMAN_ERROR;
539 case http2::Http2DecoderAdapter::
540 SPDY_HPACK_MISSING_DYNAMIC_TABLE_SIZE_UPDATE:
541 return SPDY_ERROR_HPACK_MISSING_DYNAMIC_TABLE_SIZE_UPDATE;
542 case http2::Http2DecoderAdapter::SPDY_HPACK_INVALID_INDEX:
543 return SPDY_ERROR_HPACK_INVALID_INDEX;
544 case http2::Http2DecoderAdapter::SPDY_HPACK_INVALID_NAME_INDEX:
545 return SPDY_ERROR_HPACK_INVALID_NAME_INDEX;
546 case http2::Http2DecoderAdapter::
547 SPDY_HPACK_DYNAMIC_TABLE_SIZE_UPDATE_NOT_ALLOWED:
548 return SPDY_ERROR_HPACK_DYNAMIC_TABLE_SIZE_UPDATE_NOT_ALLOWED;
549 case http2::Http2DecoderAdapter::
550 SPDY_HPACK_INITIAL_DYNAMIC_TABLE_SIZE_UPDATE_IS_ABOVE_LOW_WATER_MARK:
551 return SPDY_ERROR_HPACK_INITIAL_DYNAMIC_TABLE_SIZE_UPDATE_IS_ABOVE_LOW_WATER_MARK;
552 case http2::Http2DecoderAdapter::
553 SPDY_HPACK_DYNAMIC_TABLE_SIZE_UPDATE_IS_ABOVE_ACKNOWLEDGED_SETTING:
554 return SPDY_ERROR_HPACK_DYNAMIC_TABLE_SIZE_UPDATE_IS_ABOVE_ACKNOWLEDGED_SETTING;
555 case http2::Http2DecoderAdapter::SPDY_HPACK_TRUNCATED_BLOCK:
556 return SPDY_ERROR_HPACK_TRUNCATED_BLOCK;
557 case http2::Http2DecoderAdapter::SPDY_HPACK_FRAGMENT_TOO_LONG:
558 return SPDY_ERROR_HPACK_FRAGMENT_TOO_LONG;
559 case http2::Http2DecoderAdapter::
560 SPDY_HPACK_COMPRESSED_HEADER_SIZE_EXCEEDS_LIMIT:
561 return SPDY_ERROR_HPACK_COMPRESSED_HEADER_SIZE_EXCEEDS_LIMIT;
562
563 case http2::Http2DecoderAdapter::LAST_ERROR:
564 NOTREACHED();
565 }
566 NOTREACHED();
567 return static_cast<SpdyProtocolErrorDetails>(-1);
568 }
569
MapFramerErrorToNetError(http2::Http2DecoderAdapter::SpdyFramerError err)570 Error MapFramerErrorToNetError(
571 http2::Http2DecoderAdapter::SpdyFramerError err) {
572 switch (err) {
573 case http2::Http2DecoderAdapter::SPDY_NO_ERROR:
574 return OK;
575 case http2::Http2DecoderAdapter::SPDY_INVALID_CONTROL_FRAME:
576 return ERR_HTTP2_PROTOCOL_ERROR;
577 case http2::Http2DecoderAdapter::SPDY_CONTROL_PAYLOAD_TOO_LARGE:
578 return ERR_HTTP2_FRAME_SIZE_ERROR;
579 case http2::Http2DecoderAdapter::SPDY_ZLIB_INIT_FAILURE:
580 return ERR_HTTP2_COMPRESSION_ERROR;
581 case http2::Http2DecoderAdapter::SPDY_UNSUPPORTED_VERSION:
582 return ERR_HTTP2_PROTOCOL_ERROR;
583 case http2::Http2DecoderAdapter::SPDY_DECOMPRESS_FAILURE:
584 case http2::Http2DecoderAdapter::SPDY_HPACK_INDEX_VARINT_ERROR:
585 case http2::Http2DecoderAdapter::SPDY_HPACK_NAME_LENGTH_VARINT_ERROR:
586 case http2::Http2DecoderAdapter::SPDY_HPACK_VALUE_LENGTH_VARINT_ERROR:
587 case http2::Http2DecoderAdapter::SPDY_HPACK_NAME_TOO_LONG:
588 case http2::Http2DecoderAdapter::SPDY_HPACK_VALUE_TOO_LONG:
589 case http2::Http2DecoderAdapter::SPDY_HPACK_NAME_HUFFMAN_ERROR:
590 case http2::Http2DecoderAdapter::SPDY_HPACK_VALUE_HUFFMAN_ERROR:
591 case http2::Http2DecoderAdapter::
592 SPDY_HPACK_MISSING_DYNAMIC_TABLE_SIZE_UPDATE:
593 case http2::Http2DecoderAdapter::SPDY_HPACK_INVALID_INDEX:
594 case http2::Http2DecoderAdapter::SPDY_HPACK_INVALID_NAME_INDEX:
595 case http2::Http2DecoderAdapter::
596 SPDY_HPACK_DYNAMIC_TABLE_SIZE_UPDATE_NOT_ALLOWED:
597 case http2::Http2DecoderAdapter::
598 SPDY_HPACK_INITIAL_DYNAMIC_TABLE_SIZE_UPDATE_IS_ABOVE_LOW_WATER_MARK:
599 case http2::Http2DecoderAdapter::
600 SPDY_HPACK_DYNAMIC_TABLE_SIZE_UPDATE_IS_ABOVE_ACKNOWLEDGED_SETTING:
601 case http2::Http2DecoderAdapter::SPDY_HPACK_TRUNCATED_BLOCK:
602 case http2::Http2DecoderAdapter::SPDY_HPACK_FRAGMENT_TOO_LONG:
603 case http2::Http2DecoderAdapter::
604 SPDY_HPACK_COMPRESSED_HEADER_SIZE_EXCEEDS_LIMIT:
605 return ERR_HTTP2_COMPRESSION_ERROR;
606 case http2::Http2DecoderAdapter::SPDY_COMPRESS_FAILURE:
607 return ERR_HTTP2_COMPRESSION_ERROR;
608 case http2::Http2DecoderAdapter::SPDY_GOAWAY_FRAME_CORRUPT:
609 return ERR_HTTP2_PROTOCOL_ERROR;
610 case http2::Http2DecoderAdapter::SPDY_RST_STREAM_FRAME_CORRUPT:
611 return ERR_HTTP2_PROTOCOL_ERROR;
612 case http2::Http2DecoderAdapter::SPDY_INVALID_PADDING:
613 return ERR_HTTP2_PROTOCOL_ERROR;
614 case http2::Http2DecoderAdapter::SPDY_INVALID_DATA_FRAME_FLAGS:
615 return ERR_HTTP2_PROTOCOL_ERROR;
616 case http2::Http2DecoderAdapter::SPDY_INVALID_CONTROL_FRAME_FLAGS:
617 return ERR_HTTP2_PROTOCOL_ERROR;
618 case http2::Http2DecoderAdapter::SPDY_UNEXPECTED_FRAME:
619 return ERR_HTTP2_PROTOCOL_ERROR;
620 case http2::Http2DecoderAdapter::SPDY_INTERNAL_FRAMER_ERROR:
621 return ERR_HTTP2_PROTOCOL_ERROR;
622 case http2::Http2DecoderAdapter::SPDY_INVALID_CONTROL_FRAME_SIZE:
623 return ERR_HTTP2_FRAME_SIZE_ERROR;
624 case http2::Http2DecoderAdapter::SPDY_INVALID_STREAM_ID:
625 return ERR_HTTP2_PROTOCOL_ERROR;
626 case http2::Http2DecoderAdapter::SPDY_OVERSIZED_PAYLOAD:
627 return ERR_HTTP2_FRAME_SIZE_ERROR;
628 case http2::Http2DecoderAdapter::LAST_ERROR:
629 NOTREACHED();
630 }
631 NOTREACHED();
632 return ERR_HTTP2_PROTOCOL_ERROR;
633 }
634
MapRstStreamStatusToProtocolError(spdy::SpdyErrorCode error_code)635 SpdyProtocolErrorDetails MapRstStreamStatusToProtocolError(
636 spdy::SpdyErrorCode error_code) {
637 switch (error_code) {
638 case spdy::ERROR_CODE_NO_ERROR:
639 return STATUS_CODE_NO_ERROR;
640 case spdy::ERROR_CODE_PROTOCOL_ERROR:
641 return STATUS_CODE_PROTOCOL_ERROR;
642 case spdy::ERROR_CODE_INTERNAL_ERROR:
643 return STATUS_CODE_INTERNAL_ERROR;
644 case spdy::ERROR_CODE_FLOW_CONTROL_ERROR:
645 return STATUS_CODE_FLOW_CONTROL_ERROR;
646 case spdy::ERROR_CODE_SETTINGS_TIMEOUT:
647 return STATUS_CODE_SETTINGS_TIMEOUT;
648 case spdy::ERROR_CODE_STREAM_CLOSED:
649 return STATUS_CODE_STREAM_CLOSED;
650 case spdy::ERROR_CODE_FRAME_SIZE_ERROR:
651 return STATUS_CODE_FRAME_SIZE_ERROR;
652 case spdy::ERROR_CODE_REFUSED_STREAM:
653 return STATUS_CODE_REFUSED_STREAM;
654 case spdy::ERROR_CODE_CANCEL:
655 return STATUS_CODE_CANCEL;
656 case spdy::ERROR_CODE_COMPRESSION_ERROR:
657 return STATUS_CODE_COMPRESSION_ERROR;
658 case spdy::ERROR_CODE_CONNECT_ERROR:
659 return STATUS_CODE_CONNECT_ERROR;
660 case spdy::ERROR_CODE_ENHANCE_YOUR_CALM:
661 return STATUS_CODE_ENHANCE_YOUR_CALM;
662 case spdy::ERROR_CODE_INADEQUATE_SECURITY:
663 return STATUS_CODE_INADEQUATE_SECURITY;
664 case spdy::ERROR_CODE_HTTP_1_1_REQUIRED:
665 return STATUS_CODE_HTTP_1_1_REQUIRED;
666 }
667 NOTREACHED();
668 return static_cast<SpdyProtocolErrorDetails>(-1);
669 }
670
MapNetErrorToGoAwayStatus(Error err)671 spdy::SpdyErrorCode MapNetErrorToGoAwayStatus(Error err) {
672 switch (err) {
673 case OK:
674 return spdy::ERROR_CODE_NO_ERROR;
675 case ERR_HTTP2_PROTOCOL_ERROR:
676 return spdy::ERROR_CODE_PROTOCOL_ERROR;
677 case ERR_HTTP2_FLOW_CONTROL_ERROR:
678 return spdy::ERROR_CODE_FLOW_CONTROL_ERROR;
679 case ERR_HTTP2_FRAME_SIZE_ERROR:
680 return spdy::ERROR_CODE_FRAME_SIZE_ERROR;
681 case ERR_HTTP2_COMPRESSION_ERROR:
682 return spdy::ERROR_CODE_COMPRESSION_ERROR;
683 case ERR_HTTP2_INADEQUATE_TRANSPORT_SECURITY:
684 return spdy::ERROR_CODE_INADEQUATE_SECURITY;
685 default:
686 return spdy::ERROR_CODE_PROTOCOL_ERROR;
687 }
688 }
689
SpdyStreamRequest()690 SpdyStreamRequest::SpdyStreamRequest() {
691 Reset();
692 }
693
~SpdyStreamRequest()694 SpdyStreamRequest::~SpdyStreamRequest() {
695 CancelRequest();
696 }
697
StartRequest(SpdyStreamType type,const base::WeakPtr<SpdySession> & session,const GURL & url,bool can_send_early,RequestPriority priority,const SocketTag & socket_tag,const NetLogWithSource & net_log,CompletionOnceCallback callback,const NetworkTrafficAnnotationTag & traffic_annotation)698 int SpdyStreamRequest::StartRequest(
699 SpdyStreamType type,
700 const base::WeakPtr<SpdySession>& session,
701 const GURL& url,
702 bool can_send_early,
703 RequestPriority priority,
704 const SocketTag& socket_tag,
705 const NetLogWithSource& net_log,
706 CompletionOnceCallback callback,
707 const NetworkTrafficAnnotationTag& traffic_annotation) {
708 DCHECK(session);
709 DCHECK(!session_);
710 DCHECK(!stream_);
711 DCHECK(callback_.is_null());
712 DCHECK(url.is_valid()) << url.possibly_invalid_spec();
713
714 type_ = type;
715 session_ = session;
716 url_ = SimplifyUrlForRequest(url);
717 priority_ = priority;
718 socket_tag_ = socket_tag;
719 net_log_ = net_log;
720 callback_ = std::move(callback);
721 traffic_annotation_ = MutableNetworkTrafficAnnotationTag(traffic_annotation);
722
723 // If early data is not allowed, confirm the handshake first.
724 int rv = OK;
725 if (!can_send_early) {
726 rv = session_->ConfirmHandshake(
727 base::BindOnce(&SpdyStreamRequest::OnConfirmHandshakeComplete,
728 weak_ptr_factory_.GetWeakPtr()));
729 }
730 if (rv != OK) {
731 // If rv is ERR_IO_PENDING, OnConfirmHandshakeComplete() will call
732 // TryCreateStream() later.
733 return rv;
734 }
735
736 base::WeakPtr<SpdyStream> stream;
737 rv = session->TryCreateStream(weak_ptr_factory_.GetWeakPtr(), &stream);
738 if (rv != OK) {
739 // If rv is ERR_IO_PENDING, the SpdySession will call
740 // OnRequestCompleteSuccess() or OnRequestCompleteFailure() later.
741 return rv;
742 }
743
744 Reset();
745 stream_ = stream;
746 return OK;
747 }
748
CancelRequest()749 void SpdyStreamRequest::CancelRequest() {
750 if (session_)
751 session_->CancelStreamRequest(weak_ptr_factory_.GetWeakPtr());
752 Reset();
753 // Do this to cancel any pending CompleteStreamRequest() and
754 // OnConfirmHandshakeComplete() tasks.
755 weak_ptr_factory_.InvalidateWeakPtrs();
756 }
757
ReleaseStream()758 base::WeakPtr<SpdyStream> SpdyStreamRequest::ReleaseStream() {
759 DCHECK(!session_);
760 base::WeakPtr<SpdyStream> stream = stream_;
761 DCHECK(stream);
762 Reset();
763 return stream;
764 }
765
EstimateMemoryUsage() const766 size_t SpdyStreamRequest::EstimateMemoryUsage() const {
767 return base::trace_event::EstimateItemMemoryUsage(url_);
768 }
769
SetPriority(RequestPriority priority)770 void SpdyStreamRequest::SetPriority(RequestPriority priority) {
771 if (priority_ == priority)
772 return;
773
774 if (stream_)
775 stream_->SetPriority(priority);
776 if (session_)
777 session_->ChangeStreamRequestPriority(weak_ptr_factory_.GetWeakPtr(),
778 priority);
779 priority_ = priority;
780 }
781
OnRequestCompleteSuccess(const base::WeakPtr<SpdyStream> & stream)782 void SpdyStreamRequest::OnRequestCompleteSuccess(
783 const base::WeakPtr<SpdyStream>& stream) {
784 DCHECK(session_);
785 DCHECK(!stream_);
786 DCHECK(!callback_.is_null());
787 CompletionOnceCallback callback = std::move(callback_);
788 Reset();
789 DCHECK(stream);
790 stream_ = stream;
791 std::move(callback).Run(OK);
792 }
793
OnRequestCompleteFailure(int rv)794 void SpdyStreamRequest::OnRequestCompleteFailure(int rv) {
795 DCHECK(session_);
796 DCHECK(!stream_);
797 DCHECK(!callback_.is_null());
798 CompletionOnceCallback callback = std::move(callback_);
799 Reset();
800 DCHECK_NE(rv, OK);
801 std::move(callback).Run(rv);
802 }
803
Reset()804 void SpdyStreamRequest::Reset() {
805 type_ = SPDY_BIDIRECTIONAL_STREAM;
806 session_.reset();
807 stream_.reset();
808 url_ = GURL();
809 priority_ = MINIMUM_PRIORITY;
810 socket_tag_ = SocketTag();
811 net_log_ = NetLogWithSource();
812 callback_.Reset();
813 traffic_annotation_.reset();
814 }
815
OnConfirmHandshakeComplete(int rv)816 void SpdyStreamRequest::OnConfirmHandshakeComplete(int rv) {
817 DCHECK_NE(ERR_IO_PENDING, rv);
818 if (rv != OK) {
819 OnRequestCompleteFailure(rv);
820 return;
821 }
822
823 // ConfirmHandshake() completed asynchronously. Record the time so the caller
824 // can adjust LoadTimingInfo.
825 confirm_handshake_end_ = base::TimeTicks::Now();
826
827 base::WeakPtr<SpdyStream> stream;
828 rv = session_->TryCreateStream(weak_ptr_factory_.GetWeakPtr(), &stream);
829 if (rv == OK) {
830 OnRequestCompleteSuccess(stream);
831 } else if (rv != ERR_IO_PENDING) {
832 // If rv is ERR_IO_PENDING, the SpdySession will call
833 // OnRequestCompleteSuccess() or OnRequestCompleteFailure() later.
834 OnRequestCompleteFailure(rv);
835 }
836 }
837
838 // static
CanPool(TransportSecurityState * transport_security_state,const SSLInfo & ssl_info,const SSLConfigService & ssl_config_service,const std::string & old_hostname,const std::string & new_hostname)839 bool SpdySession::CanPool(TransportSecurityState* transport_security_state,
840 const SSLInfo& ssl_info,
841 const SSLConfigService& ssl_config_service,
842 const std::string& old_hostname,
843 const std::string& new_hostname) {
844 // Pooling is prohibited if the server cert is not valid for the new domain,
845 // and for connections on which client certs were sent. It is also prohibited
846 // when channel ID was sent if the hosts are from different eTLDs+1.
847 if (IsCertStatusError(ssl_info.cert_status))
848 return false;
849
850 if (ssl_info.client_cert_sent &&
851 !(ssl_config_service.CanShareConnectionWithClientCerts(old_hostname) &&
852 ssl_config_service.CanShareConnectionWithClientCerts(new_hostname))) {
853 return false;
854 }
855
856 if (!ssl_info.cert->VerifyNameMatch(new_hostname))
857 return false;
858
859 std::string pinning_failure_log;
860 // DISABLE_PIN_REPORTS is set here because this check can fail in
861 // normal operation without being indicative of a misconfiguration or
862 // attack. Port is left at 0 as it is never used.
863 if (transport_security_state->CheckPublicKeyPins(
864 HostPortPair(new_hostname, 0), ssl_info.is_issued_by_known_root,
865 ssl_info.public_key_hashes, ssl_info.unverified_cert.get(),
866 ssl_info.cert.get(), TransportSecurityState::DISABLE_PIN_REPORTS,
867 &pinning_failure_log) ==
868 TransportSecurityState::PKPStatus::VIOLATED) {
869 return false;
870 }
871
872 // As with CheckPublicKeyPins above, disable Expect-CT reports.
873 switch (transport_security_state->CheckCTRequirements(
874 HostPortPair(new_hostname, 0), ssl_info.is_issued_by_known_root,
875 ssl_info.public_key_hashes, ssl_info.cert.get(),
876 ssl_info.unverified_cert.get(), ssl_info.signed_certificate_timestamps,
877 TransportSecurityState::DISABLE_EXPECT_CT_REPORTS,
878 ssl_info.ct_policy_compliance)) {
879 case TransportSecurityState::CT_REQUIREMENTS_NOT_MET:
880 return false;
881 case TransportSecurityState::CT_REQUIREMENTS_MET:
882 case TransportSecurityState::CT_NOT_REQUIRED:
883 // Intentional fallthrough; this case is just here to make sure that all
884 // possible values of CheckCTRequirements() are handled.
885 break;
886 }
887
888 return true;
889 }
890
SpdySession(const SpdySessionKey & spdy_session_key,HttpServerProperties * http_server_properties,TransportSecurityState * transport_security_state,SSLConfigService * ssl_config_service,const quic::ParsedQuicVersionVector & quic_supported_versions,bool enable_sending_initial_data,bool enable_ping_based_connection_checking,bool is_http2_enabled,bool is_quic_enabled,bool is_trusted_proxy,size_t session_max_recv_window_size,int session_max_queued_capped_frames,const spdy::SettingsMap & initial_settings,const base::Optional<SpdySessionPool::GreasedHttp2Frame> & greased_http2_frame,TimeFunc time_func,ServerPushDelegate * push_delegate,NetworkQualityEstimator * network_quality_estimator,NetLog * net_log)891 SpdySession::SpdySession(
892 const SpdySessionKey& spdy_session_key,
893 HttpServerProperties* http_server_properties,
894 TransportSecurityState* transport_security_state,
895 SSLConfigService* ssl_config_service,
896 const quic::ParsedQuicVersionVector& quic_supported_versions,
897 bool enable_sending_initial_data,
898 bool enable_ping_based_connection_checking,
899 bool is_http2_enabled,
900 bool is_quic_enabled,
901 bool is_trusted_proxy,
902 size_t session_max_recv_window_size,
903 int session_max_queued_capped_frames,
904 const spdy::SettingsMap& initial_settings,
905 const base::Optional<SpdySessionPool::GreasedHttp2Frame>&
906 greased_http2_frame,
907 TimeFunc time_func,
908 ServerPushDelegate* push_delegate,
909 NetworkQualityEstimator* network_quality_estimator,
910 NetLog* net_log)
911 : in_io_loop_(false),
912 spdy_session_key_(spdy_session_key),
913 pool_(nullptr),
914 http_server_properties_(http_server_properties),
915 transport_security_state_(transport_security_state),
916 ssl_config_service_(ssl_config_service),
917 socket_(nullptr),
918 stream_hi_water_mark_(kFirstStreamId),
919 last_accepted_push_stream_id_(0),
920 push_delegate_(push_delegate),
921 num_pushed_streams_(0u),
922 num_active_pushed_streams_(0u),
923 bytes_pushed_count_(0u),
924 bytes_pushed_and_unclaimed_count_(0u),
925 in_flight_write_frame_type_(spdy::SpdyFrameType::DATA),
926 in_flight_write_frame_size_(0),
927 availability_state_(STATE_AVAILABLE),
928 read_state_(READ_STATE_DO_READ),
929 write_state_(WRITE_STATE_IDLE),
930 error_on_close_(OK),
931 initial_settings_(initial_settings),
932 greased_http2_frame_(greased_http2_frame),
933 in_confirm_handshake_(false),
934 max_concurrent_streams_(kInitialMaxConcurrentStreams),
935 max_concurrent_pushed_streams_(
936 initial_settings.at(spdy::SETTINGS_MAX_CONCURRENT_STREAMS)),
937 streams_initiated_count_(0),
938 streams_pushed_count_(0),
939 streams_pushed_and_claimed_count_(0),
940 streams_abandoned_count_(0),
941 ping_in_flight_(false),
942 next_ping_id_(1),
943 last_read_time_(time_func()),
944 last_compressed_frame_len_(0),
945 check_ping_status_pending_(false),
946 session_send_window_size_(0),
947 session_max_recv_window_size_(session_max_recv_window_size),
948 session_max_queued_capped_frames_(session_max_queued_capped_frames),
949 session_recv_window_size_(0),
950 session_unacked_recv_window_bytes_(0),
951 stream_initial_send_window_size_(kDefaultInitialWindowSize),
952 max_header_table_size_(
953 initial_settings.at(spdy::SETTINGS_HEADER_TABLE_SIZE)),
954 stream_max_recv_window_size_(
955 initial_settings.at(spdy::SETTINGS_INITIAL_WINDOW_SIZE)),
956 net_log_(
957 NetLogWithSource::Make(net_log, NetLogSourceType::HTTP2_SESSION)),
958 quic_supported_versions_(quic_supported_versions),
959 enable_sending_initial_data_(enable_sending_initial_data),
960 enable_ping_based_connection_checking_(
961 enable_ping_based_connection_checking),
962 is_http2_enabled_(is_http2_enabled),
963 is_quic_enabled_(is_quic_enabled),
964 is_trusted_proxy_(is_trusted_proxy),
965 enable_push_(IsPushEnabled(initial_settings)),
966 support_websocket_(false),
967 connection_at_risk_of_loss_time_(
968 base::TimeDelta::FromSeconds(kDefaultConnectionAtRiskOfLossSeconds)),
969 hung_interval_(base::TimeDelta::FromSeconds(kHungIntervalSeconds)),
970 time_func_(time_func),
971 network_quality_estimator_(network_quality_estimator) {
972 net_log_.BeginEvent(NetLogEventType::HTTP2_SESSION, [&] {
973 return NetLogSpdySessionParams(host_port_proxy_pair());
974 });
975
976 DCHECK(base::Contains(initial_settings_, spdy::SETTINGS_HEADER_TABLE_SIZE));
977 DCHECK(
978 base::Contains(initial_settings_, spdy::SETTINGS_MAX_CONCURRENT_STREAMS));
979 DCHECK(base::Contains(initial_settings_, spdy::SETTINGS_INITIAL_WINDOW_SIZE));
980
981 if (greased_http2_frame_) {
982 // See https://tools.ietf.org/html/draft-bishop-httpbis-grease-00
983 // for reserved frame types.
984 DCHECK_EQ(0x0b, greased_http2_frame_.value().type % 0x1f);
985 }
986
987 // TODO(mbelshe): consider randomization of the stream_hi_water_mark.
988 }
989
~SpdySession()990 SpdySession::~SpdySession() {
991 CHECK(!in_io_loop_);
992 DcheckDraining();
993
994 DCHECK(waiting_for_confirmation_callbacks_.empty());
995
996 // TODO(akalin): Check connection->is_initialized().
997 DCHECK(socket_);
998 // With SPDY we can't recycle sockets.
999 socket_->Disconnect();
1000
1001 RecordHistograms();
1002
1003 net_log_.EndEvent(NetLogEventType::HTTP2_SESSION);
1004 }
1005
GetPushedStream(const GURL & url,spdy::SpdyStreamId pushed_stream_id,RequestPriority priority,SpdyStream ** stream)1006 int SpdySession::GetPushedStream(const GURL& url,
1007 spdy::SpdyStreamId pushed_stream_id,
1008 RequestPriority priority,
1009 SpdyStream** stream) {
1010 CHECK(!in_io_loop_);
1011 // |pushed_stream_id| must be valid.
1012 DCHECK_NE(pushed_stream_id, kNoPushedStreamFound);
1013 // |pushed_stream_id| must already have been claimed.
1014 DCHECK_NE(pushed_stream_id,
1015 pool_->push_promise_index()->FindStream(url, this));
1016
1017 if (availability_state_ == STATE_DRAINING) {
1018 return ERR_CONNECTION_CLOSED;
1019 }
1020
1021 auto active_it = active_streams_.find(pushed_stream_id);
1022 if (active_it == active_streams_.end()) {
1023 // A previously claimed pushed stream might not be available, for example,
1024 // if the server has reset it in the meanwhile.
1025 return ERR_HTTP2_PUSHED_STREAM_NOT_AVAILABLE;
1026 }
1027
1028 net_log_.AddEvent(NetLogEventType::HTTP2_STREAM_ADOPTED_PUSH_STREAM, [&] {
1029 return NetLogSpdyAdoptedPushStreamParams(pushed_stream_id, url);
1030 });
1031
1032 *stream = active_it->second;
1033
1034 DCHECK_LT(streams_pushed_and_claimed_count_, streams_pushed_count_);
1035 streams_pushed_and_claimed_count_++;
1036
1037 // If the stream is still open, update its priority to that of the request.
1038 if (!(*stream)->IsClosed()) {
1039 (*stream)->SetPriority(priority);
1040 }
1041
1042 return OK;
1043 }
1044
CancelPush(const GURL & url)1045 void SpdySession::CancelPush(const GURL& url) {
1046 const spdy::SpdyStreamId stream_id =
1047 pool_->push_promise_index()->FindStream(url, this);
1048 if (stream_id == kNoPushedStreamFound)
1049 return;
1050
1051 DCHECK(IsStreamActive(stream_id));
1052 RecordSpdyPushedStreamFateHistogram(SpdyPushedStreamFate::kAlreadyInCache);
1053 ResetStream(stream_id, ERR_ABORTED, "Cancelled push stream.");
1054 }
1055
InitializeWithSocketHandle(std::unique_ptr<ClientSocketHandle> client_socket_handle,SpdySessionPool * pool)1056 void SpdySession::InitializeWithSocketHandle(
1057 std::unique_ptr<ClientSocketHandle> client_socket_handle,
1058 SpdySessionPool* pool) {
1059 DCHECK(!client_socket_handle_);
1060 DCHECK(!owned_stream_socket_);
1061 DCHECK(!socket_);
1062
1063 // TODO(akalin): Check connection->is_initialized() instead. This
1064 // requires re-working CreateFakeSpdySession(), though.
1065 DCHECK(client_socket_handle->socket());
1066
1067 client_socket_handle_ = std::move(client_socket_handle);
1068 socket_ = client_socket_handle_->socket();
1069 client_socket_handle_->AddHigherLayeredPool(this);
1070
1071 InitializeInternal(pool);
1072 }
1073
InitializeWithSocket(std::unique_ptr<StreamSocket> stream_socket,const LoadTimingInfo::ConnectTiming & connect_timing,SpdySessionPool * pool)1074 void SpdySession::InitializeWithSocket(
1075 std::unique_ptr<StreamSocket> stream_socket,
1076 const LoadTimingInfo::ConnectTiming& connect_timing,
1077 SpdySessionPool* pool) {
1078 DCHECK(!client_socket_handle_);
1079 DCHECK(!owned_stream_socket_);
1080 DCHECK(!socket_);
1081
1082 DCHECK(stream_socket);
1083
1084 owned_stream_socket_ = std::move(stream_socket);
1085 socket_ = owned_stream_socket_.get();
1086 connect_timing_ =
1087 std::make_unique<LoadTimingInfo::ConnectTiming>(connect_timing);
1088
1089 InitializeInternal(pool);
1090 }
1091
VerifyDomainAuthentication(const std::string & domain) const1092 bool SpdySession::VerifyDomainAuthentication(const std::string& domain) const {
1093 if (availability_state_ == STATE_DRAINING)
1094 return false;
1095
1096 SSLInfo ssl_info;
1097 if (!GetSSLInfo(&ssl_info))
1098 return true; // This is not a secure session, so all domains are okay.
1099
1100 return CanPool(transport_security_state_, ssl_info, *ssl_config_service_,
1101 host_port_pair().host(), domain);
1102 }
1103
EnqueueStreamWrite(const base::WeakPtr<SpdyStream> & stream,spdy::SpdyFrameType frame_type,std::unique_ptr<SpdyBufferProducer> producer)1104 void SpdySession::EnqueueStreamWrite(
1105 const base::WeakPtr<SpdyStream>& stream,
1106 spdy::SpdyFrameType frame_type,
1107 std::unique_ptr<SpdyBufferProducer> producer) {
1108 DCHECK(frame_type == spdy::SpdyFrameType::HEADERS ||
1109 frame_type == spdy::SpdyFrameType::DATA);
1110 EnqueueWrite(stream->priority(), frame_type, std::move(producer), stream,
1111 stream->traffic_annotation());
1112 }
1113
GreasedFramesEnabled() const1114 bool SpdySession::GreasedFramesEnabled() const {
1115 return greased_http2_frame_.has_value();
1116 }
1117
EnqueueGreasedFrame(const base::WeakPtr<SpdyStream> & stream)1118 void SpdySession::EnqueueGreasedFrame(const base::WeakPtr<SpdyStream>& stream) {
1119 if (availability_state_ == STATE_DRAINING)
1120 return;
1121
1122 EnqueueWrite(
1123 stream->priority(),
1124 static_cast<spdy::SpdyFrameType>(greased_http2_frame_.value().type),
1125 std::make_unique<GreasedBufferProducer>(
1126 stream, &greased_http2_frame_.value(), buffered_spdy_framer_.get()),
1127 stream, stream->traffic_annotation());
1128 }
1129
ConfirmHandshake(CompletionOnceCallback callback)1130 int SpdySession::ConfirmHandshake(CompletionOnceCallback callback) {
1131 int rv = ERR_IO_PENDING;
1132 if (!in_confirm_handshake_) {
1133 rv = socket_->ConfirmHandshake(
1134 base::BindOnce(&SpdySession::NotifyRequestsOfConfirmation,
1135 weak_factory_.GetWeakPtr()));
1136 }
1137 if (rv == ERR_IO_PENDING) {
1138 in_confirm_handshake_ = true;
1139 waiting_for_confirmation_callbacks_.push_back(std::move(callback));
1140 }
1141 return rv;
1142 }
1143
CreateHeaders(spdy::SpdyStreamId stream_id,RequestPriority priority,spdy::SpdyControlFlags flags,spdy::SpdyHeaderBlock block,NetLogSource source_dependency)1144 std::unique_ptr<spdy::SpdySerializedFrame> SpdySession::CreateHeaders(
1145 spdy::SpdyStreamId stream_id,
1146 RequestPriority priority,
1147 spdy::SpdyControlFlags flags,
1148 spdy::SpdyHeaderBlock block,
1149 NetLogSource source_dependency) {
1150 ActiveStreamMap::const_iterator it = active_streams_.find(stream_id);
1151 CHECK(it != active_streams_.end());
1152 CHECK_EQ(it->second->stream_id(), stream_id);
1153
1154 MaybeSendPrefacePing();
1155
1156 DCHECK(buffered_spdy_framer_.get());
1157 spdy::SpdyPriority spdy_priority =
1158 ConvertRequestPriorityToSpdyPriority(priority);
1159
1160 bool has_priority = true;
1161 int weight = 0;
1162 spdy::SpdyStreamId parent_stream_id = 0;
1163 bool exclusive = false;
1164
1165 priority_dependency_state_.OnStreamCreation(
1166 stream_id, spdy_priority, &parent_stream_id, &weight, &exclusive);
1167
1168 if (net_log().IsCapturing()) {
1169 net_log().AddEvent(NetLogEventType::HTTP2_SESSION_SEND_HEADERS,
1170 [&](NetLogCaptureMode capture_mode) {
1171 return NetLogSpdyHeadersSentParams(
1172 &block, (flags & spdy::CONTROL_FLAG_FIN) != 0,
1173 stream_id, has_priority, weight, parent_stream_id,
1174 exclusive, source_dependency, capture_mode);
1175 });
1176 }
1177
1178 spdy::SpdyHeadersIR headers(stream_id, std::move(block));
1179 headers.set_has_priority(has_priority);
1180 headers.set_weight(weight);
1181 headers.set_parent_stream_id(parent_stream_id);
1182 headers.set_exclusive(exclusive);
1183 headers.set_fin((flags & spdy::CONTROL_FLAG_FIN) != 0);
1184
1185 streams_initiated_count_++;
1186
1187 return std::make_unique<spdy::SpdySerializedFrame>(
1188 buffered_spdy_framer_->SerializeFrame(headers));
1189 }
1190
CreateDataBuffer(spdy::SpdyStreamId stream_id,IOBuffer * data,int len,spdy::SpdyDataFlags flags)1191 std::unique_ptr<SpdyBuffer> SpdySession::CreateDataBuffer(
1192 spdy::SpdyStreamId stream_id,
1193 IOBuffer* data,
1194 int len,
1195 spdy::SpdyDataFlags flags) {
1196 if (availability_state_ == STATE_DRAINING) {
1197 return std::unique_ptr<SpdyBuffer>();
1198 }
1199
1200 ActiveStreamMap::const_iterator it = active_streams_.find(stream_id);
1201 CHECK(it != active_streams_.end());
1202 SpdyStream* stream = it->second;
1203 CHECK_EQ(stream->stream_id(), stream_id);
1204
1205 if (len < 0) {
1206 NOTREACHED();
1207 return std::unique_ptr<SpdyBuffer>();
1208 }
1209
1210 int effective_len = std::min(len, kMaxSpdyFrameChunkSize);
1211
1212 bool send_stalled_by_stream = (stream->send_window_size() <= 0);
1213 bool send_stalled_by_session = IsSendStalled();
1214
1215 // NOTE: There's an enum of the same name in histograms.xml.
1216 enum SpdyFrameFlowControlState {
1217 SEND_NOT_STALLED,
1218 SEND_STALLED_BY_STREAM,
1219 SEND_STALLED_BY_SESSION,
1220 SEND_STALLED_BY_STREAM_AND_SESSION,
1221 };
1222
1223 SpdyFrameFlowControlState frame_flow_control_state = SEND_NOT_STALLED;
1224 if (send_stalled_by_stream) {
1225 if (send_stalled_by_session) {
1226 frame_flow_control_state = SEND_STALLED_BY_STREAM_AND_SESSION;
1227 } else {
1228 frame_flow_control_state = SEND_STALLED_BY_STREAM;
1229 }
1230 } else if (send_stalled_by_session) {
1231 frame_flow_control_state = SEND_STALLED_BY_SESSION;
1232 }
1233
1234 UMA_HISTOGRAM_ENUMERATION("Net.SpdyFrameStreamAndSessionFlowControlState",
1235 frame_flow_control_state,
1236 SEND_STALLED_BY_STREAM_AND_SESSION + 1);
1237
1238 // Obey send window size of the stream.
1239 if (send_stalled_by_stream) {
1240 stream->set_send_stalled_by_flow_control(true);
1241 // Even though we're currently stalled only by the stream, we
1242 // might end up being stalled by the session also.
1243 QueueSendStalledStream(*stream);
1244 net_log().AddEventWithIntParams(
1245 NetLogEventType::HTTP2_SESSION_STREAM_STALLED_BY_STREAM_SEND_WINDOW,
1246 "stream_id", stream_id);
1247 return std::unique_ptr<SpdyBuffer>();
1248 }
1249
1250 effective_len = std::min(effective_len, stream->send_window_size());
1251
1252 // Obey send window size of the session.
1253 if (send_stalled_by_session) {
1254 stream->set_send_stalled_by_flow_control(true);
1255 QueueSendStalledStream(*stream);
1256 net_log().AddEventWithIntParams(
1257 NetLogEventType::HTTP2_SESSION_STREAM_STALLED_BY_SESSION_SEND_WINDOW,
1258 "stream_id", stream_id);
1259 return std::unique_ptr<SpdyBuffer>();
1260 }
1261
1262 effective_len = std::min(effective_len, session_send_window_size_);
1263
1264 DCHECK_GE(effective_len, 0);
1265
1266 // Clear FIN flag if only some of the data will be in the data
1267 // frame.
1268 if (effective_len < len)
1269 flags = static_cast<spdy::SpdyDataFlags>(flags & ~spdy::DATA_FLAG_FIN);
1270
1271 if (net_log().IsCapturing()) {
1272 net_log().AddEvent(NetLogEventType::HTTP2_SESSION_SEND_DATA, [&] {
1273 return NetLogSpdyDataParams(stream_id, effective_len,
1274 (flags & spdy::DATA_FLAG_FIN) != 0);
1275 });
1276 }
1277
1278 // Send PrefacePing for DATA_FRAMEs with nonzero payload size.
1279 if (effective_len > 0)
1280 MaybeSendPrefacePing();
1281
1282 // TODO(mbelshe): reduce memory copies here.
1283 DCHECK(buffered_spdy_framer_.get());
1284 std::unique_ptr<spdy::SpdySerializedFrame> frame(
1285 buffered_spdy_framer_->CreateDataFrame(
1286 stream_id, data->data(), static_cast<uint32_t>(effective_len),
1287 flags));
1288
1289 auto data_buffer = std::make_unique<SpdyBuffer>(std::move(frame));
1290
1291 // Send window size is based on payload size, so nothing to do if this is
1292 // just a FIN with no payload.
1293 if (effective_len != 0) {
1294 DecreaseSendWindowSize(static_cast<int32_t>(effective_len));
1295 data_buffer->AddConsumeCallback(base::BindRepeating(
1296 &SpdySession::OnWriteBufferConsumed, weak_factory_.GetWeakPtr(),
1297 static_cast<size_t>(effective_len)));
1298 }
1299
1300 return data_buffer;
1301 }
1302
UpdateStreamPriority(SpdyStream * stream,RequestPriority old_priority,RequestPriority new_priority)1303 void SpdySession::UpdateStreamPriority(SpdyStream* stream,
1304 RequestPriority old_priority,
1305 RequestPriority new_priority) {
1306 // There might be write frames enqueued for |stream| regardless of whether it
1307 // is active (stream_id != 0) or inactive (no HEADERS frame has been sent out
1308 // yet and stream_id == 0).
1309 write_queue_.ChangePriorityOfWritesForStream(stream, old_priority,
1310 new_priority);
1311
1312 // PRIORITY frames only need to be sent if |stream| is active.
1313 const spdy::SpdyStreamId stream_id = stream->stream_id();
1314 if (stream_id == 0)
1315 return;
1316
1317 DCHECK(IsStreamActive(stream_id));
1318
1319 auto updates = priority_dependency_state_.OnStreamUpdate(
1320 stream_id, ConvertRequestPriorityToSpdyPriority(new_priority));
1321 for (auto u : updates) {
1322 DCHECK(IsStreamActive(u.id));
1323 EnqueuePriorityFrame(u.id, u.parent_stream_id, u.weight, u.exclusive);
1324 }
1325 }
1326
CloseActiveStream(spdy::SpdyStreamId stream_id,int status)1327 void SpdySession::CloseActiveStream(spdy::SpdyStreamId stream_id, int status) {
1328 DCHECK_NE(stream_id, 0u);
1329
1330 auto it = active_streams_.find(stream_id);
1331 if (it == active_streams_.end()) {
1332 NOTREACHED();
1333 return;
1334 }
1335
1336 CloseActiveStreamIterator(it, status);
1337 }
1338
CloseCreatedStream(const base::WeakPtr<SpdyStream> & stream,int status)1339 void SpdySession::CloseCreatedStream(const base::WeakPtr<SpdyStream>& stream,
1340 int status) {
1341 DCHECK_EQ(stream->stream_id(), 0u);
1342
1343 auto it = created_streams_.find(stream.get());
1344 if (it == created_streams_.end()) {
1345 NOTREACHED();
1346 return;
1347 }
1348
1349 CloseCreatedStreamIterator(it, status);
1350 }
1351
ResetStream(spdy::SpdyStreamId stream_id,int error,const std::string & description)1352 void SpdySession::ResetStream(spdy::SpdyStreamId stream_id,
1353 int error,
1354 const std::string& description) {
1355 DCHECK_NE(stream_id, 0u);
1356
1357 auto it = active_streams_.find(stream_id);
1358 if (it == active_streams_.end()) {
1359 NOTREACHED();
1360 return;
1361 }
1362
1363 ResetStreamIterator(it, error, description);
1364 }
1365
IsStreamActive(spdy::SpdyStreamId stream_id) const1366 bool SpdySession::IsStreamActive(spdy::SpdyStreamId stream_id) const {
1367 return base::Contains(active_streams_, stream_id);
1368 }
1369
GetLoadState() const1370 LoadState SpdySession::GetLoadState() const {
1371 // Just report that we're idle since the session could be doing
1372 // many things concurrently.
1373 return LOAD_STATE_IDLE;
1374 }
1375
GetRemoteEndpoint(IPEndPoint * endpoint)1376 bool SpdySession::GetRemoteEndpoint(IPEndPoint* endpoint) {
1377 return GetPeerAddress(endpoint) == OK;
1378 }
1379
GetSSLInfo(SSLInfo * ssl_info) const1380 bool SpdySession::GetSSLInfo(SSLInfo* ssl_info) const {
1381 return socket_->GetSSLInfo(ssl_info);
1382 }
1383
WasAlpnNegotiated() const1384 bool SpdySession::WasAlpnNegotiated() const {
1385 return socket_->WasAlpnNegotiated();
1386 }
1387
GetNegotiatedProtocol() const1388 NextProto SpdySession::GetNegotiatedProtocol() const {
1389 return socket_->GetNegotiatedProtocol();
1390 }
1391
SendStreamWindowUpdate(spdy::SpdyStreamId stream_id,uint32_t delta_window_size)1392 void SpdySession::SendStreamWindowUpdate(spdy::SpdyStreamId stream_id,
1393 uint32_t delta_window_size) {
1394 ActiveStreamMap::const_iterator it = active_streams_.find(stream_id);
1395 CHECK(it != active_streams_.end());
1396 CHECK_EQ(it->second->stream_id(), stream_id);
1397 SendWindowUpdateFrame(stream_id, delta_window_size, it->second->priority());
1398 }
1399
CloseSessionOnError(Error err,const std::string & description)1400 void SpdySession::CloseSessionOnError(Error err,
1401 const std::string& description) {
1402 DCHECK_LT(err, ERR_IO_PENDING);
1403 DoDrainSession(err, description);
1404 }
1405
MakeUnavailable()1406 void SpdySession::MakeUnavailable() {
1407 if (availability_state_ == STATE_AVAILABLE) {
1408 availability_state_ = STATE_GOING_AWAY;
1409 pool_->MakeSessionUnavailable(GetWeakPtr());
1410 }
1411 }
1412
StartGoingAway(spdy::SpdyStreamId last_good_stream_id,Error status)1413 void SpdySession::StartGoingAway(spdy::SpdyStreamId last_good_stream_id,
1414 Error status) {
1415 DCHECK_GE(availability_state_, STATE_GOING_AWAY);
1416 DCHECK_NE(OK, status);
1417 DCHECK_NE(ERR_IO_PENDING, status);
1418
1419 // The loops below are carefully written to avoid reentrancy problems.
1420
1421 while (true) {
1422 size_t old_size = GetTotalSize(pending_create_stream_queues_);
1423 base::WeakPtr<SpdyStreamRequest> pending_request =
1424 GetNextPendingStreamRequest();
1425 if (!pending_request)
1426 break;
1427 // No new stream requests should be added while the session is
1428 // going away.
1429 DCHECK_GT(old_size, GetTotalSize(pending_create_stream_queues_));
1430 pending_request->OnRequestCompleteFailure(status);
1431 }
1432
1433 while (true) {
1434 size_t old_size = active_streams_.size();
1435 auto it = active_streams_.lower_bound(last_good_stream_id + 1);
1436 if (it == active_streams_.end())
1437 break;
1438 LogAbandonedActiveStream(it, status);
1439 CloseActiveStreamIterator(it, status);
1440 // No new streams should be activated while the session is going
1441 // away.
1442 DCHECK_GT(old_size, active_streams_.size());
1443 }
1444
1445 while (!created_streams_.empty()) {
1446 size_t old_size = created_streams_.size();
1447 auto it = created_streams_.begin();
1448 LogAbandonedStream(*it, status);
1449 CloseCreatedStreamIterator(it, status);
1450 // No new streams should be created while the session is going
1451 // away.
1452 DCHECK_GT(old_size, created_streams_.size());
1453 }
1454
1455 write_queue_.RemovePendingWritesForStreamsAfter(last_good_stream_id);
1456
1457 DcheckGoingAway();
1458 MaybeFinishGoingAway();
1459 }
1460
MaybeFinishGoingAway()1461 void SpdySession::MaybeFinishGoingAway() {
1462 if (active_streams_.empty() && created_streams_.empty() &&
1463 availability_state_ == STATE_GOING_AWAY) {
1464 DoDrainSession(OK, "Finished going away");
1465 }
1466 }
1467
GetInfoAsValue() const1468 base::Value SpdySession::GetInfoAsValue() const {
1469 base::Value dict(base::Value::Type::DICTIONARY);
1470
1471 dict.SetIntKey("source_id", net_log_.source().id);
1472
1473 dict.SetStringKey("host_port_pair", host_port_pair().ToString());
1474 if (!pooled_aliases_.empty()) {
1475 base::Value alias_list(base::Value::Type::LIST);
1476 for (const auto& alias : pooled_aliases_) {
1477 alias_list.Append(alias.host_port_pair().ToString());
1478 }
1479 dict.SetKey("aliases", std::move(alias_list));
1480 }
1481 dict.SetStringKey("proxy", host_port_proxy_pair().second.ToURI());
1482 dict.SetStringKey("network_isolation_key",
1483 spdy_session_key_.network_isolation_key().ToDebugString());
1484
1485 dict.SetIntKey("active_streams", active_streams_.size());
1486
1487 dict.SetIntKey("unclaimed_pushed_streams",
1488 pool_->push_promise_index()->CountStreamsForSession(this));
1489
1490 dict.SetStringKey("negotiated_protocol",
1491 NextProtoToString(socket_->GetNegotiatedProtocol()));
1492
1493 dict.SetIntKey("error", error_on_close_);
1494 dict.SetIntKey("max_concurrent_streams", max_concurrent_streams_);
1495
1496 dict.SetIntKey("streams_initiated_count", streams_initiated_count_);
1497 dict.SetIntKey("streams_pushed_count", streams_pushed_count_);
1498 dict.SetIntKey("streams_pushed_and_claimed_count",
1499 streams_pushed_and_claimed_count_);
1500 dict.SetIntKey("streams_abandoned_count", streams_abandoned_count_);
1501 DCHECK(buffered_spdy_framer_.get());
1502 dict.SetIntKey("frames_received", buffered_spdy_framer_->frames_received());
1503
1504 dict.SetIntKey("send_window_size", session_send_window_size_);
1505 dict.SetIntKey("recv_window_size", session_recv_window_size_);
1506 dict.SetIntKey("unacked_recv_window_bytes",
1507 session_unacked_recv_window_bytes_);
1508 return dict;
1509 }
1510
IsReused() const1511 bool SpdySession::IsReused() const {
1512 if (buffered_spdy_framer_->frames_received() > 0)
1513 return true;
1514
1515 // If there's no socket pool in use (i.e., |owned_stream_socket_| is
1516 // non-null), then the SpdySession could only have been created with freshly
1517 // connected socket, since canceling the H2 session request would have
1518 // destroyed the socket.
1519 return owned_stream_socket_ ||
1520 client_socket_handle_->reuse_type() == ClientSocketHandle::UNUSED_IDLE;
1521 }
1522
GetLoadTimingInfo(spdy::SpdyStreamId stream_id,LoadTimingInfo * load_timing_info) const1523 bool SpdySession::GetLoadTimingInfo(spdy::SpdyStreamId stream_id,
1524 LoadTimingInfo* load_timing_info) const {
1525 if (client_socket_handle_) {
1526 DCHECK(!connect_timing_);
1527 return client_socket_handle_->GetLoadTimingInfo(stream_id != kFirstStreamId,
1528 load_timing_info);
1529 }
1530
1531 DCHECK(connect_timing_);
1532 DCHECK(socket_);
1533
1534 // The socket is considered "fresh" (not reused) only for the first stream on
1535 // a SPDY session. All others consider it reused, and don't return connection
1536 // establishment timing information.
1537 load_timing_info->socket_reused = (stream_id != kFirstStreamId);
1538 if (!load_timing_info->socket_reused)
1539 load_timing_info->connect_timing = *connect_timing_;
1540
1541 load_timing_info->socket_log_id = socket_->NetLog().source().id;
1542
1543 return true;
1544 }
1545
GetPeerAddress(IPEndPoint * address) const1546 int SpdySession::GetPeerAddress(IPEndPoint* address) const {
1547 if (socket_)
1548 return socket_->GetPeerAddress(address);
1549
1550 return ERR_SOCKET_NOT_CONNECTED;
1551 }
1552
GetLocalAddress(IPEndPoint * address) const1553 int SpdySession::GetLocalAddress(IPEndPoint* address) const {
1554 if (socket_)
1555 return socket_->GetLocalAddress(address);
1556
1557 return ERR_SOCKET_NOT_CONNECTED;
1558 }
1559
AddPooledAlias(const SpdySessionKey & alias_key)1560 void SpdySession::AddPooledAlias(const SpdySessionKey& alias_key) {
1561 pooled_aliases_.insert(alias_key);
1562 }
1563
RemovePooledAlias(const SpdySessionKey & alias_key)1564 void SpdySession::RemovePooledAlias(const SpdySessionKey& alias_key) {
1565 pooled_aliases_.erase(alias_key);
1566 }
1567
HasAcceptableTransportSecurity() const1568 bool SpdySession::HasAcceptableTransportSecurity() const {
1569 SSLInfo ssl_info;
1570 CHECK(GetSSLInfo(&ssl_info));
1571
1572 // HTTP/2 requires TLS 1.2+
1573 if (SSLConnectionStatusToVersion(ssl_info.connection_status) <
1574 SSL_CONNECTION_VERSION_TLS1_2) {
1575 return false;
1576 }
1577
1578 if (!IsTLSCipherSuiteAllowedByHTTP2(
1579 SSLConnectionStatusToCipherSuite(ssl_info.connection_status))) {
1580 return false;
1581 }
1582
1583 return true;
1584 }
1585
GetWeakPtr()1586 base::WeakPtr<SpdySession> SpdySession::GetWeakPtr() {
1587 return weak_factory_.GetWeakPtr();
1588 }
1589
CloseOneIdleConnection()1590 bool SpdySession::CloseOneIdleConnection() {
1591 CHECK(!in_io_loop_);
1592 DCHECK(pool_);
1593 if (active_streams_.empty()) {
1594 DoDrainSession(ERR_CONNECTION_CLOSED, "Closing idle connection.");
1595 }
1596 // Return false as the socket wasn't immediately closed.
1597 return false;
1598 }
1599
ValidatePushedStream(spdy::SpdyStreamId stream_id,const GURL & url,const HttpRequestInfo & request_info,const SpdySessionKey & key) const1600 bool SpdySession::ValidatePushedStream(spdy::SpdyStreamId stream_id,
1601 const GURL& url,
1602 const HttpRequestInfo& request_info,
1603 const SpdySessionKey& key) const {
1604 // Proxy server and privacy mode must match.
1605 if (key.proxy_server() != spdy_session_key_.proxy_server() ||
1606 key.privacy_mode() != spdy_session_key_.privacy_mode()) {
1607 return false;
1608 }
1609 // Certificate must match for encrypted schemes only.
1610 if (key != spdy_session_key_ && url.SchemeIsCryptographic() &&
1611 !VerifyDomainAuthentication(key.host_port_pair().host())) {
1612 return false;
1613 }
1614
1615 auto stream_it = active_streams_.find(stream_id);
1616 if (stream_it == active_streams_.end()) {
1617 // Only active streams should be in Http2PushPromiseIndex.
1618 NOTREACHED();
1619 return false;
1620 }
1621 const spdy::SpdyHeaderBlock& request_headers =
1622 stream_it->second->request_headers();
1623 spdy::SpdyHeaderBlock::const_iterator method_it =
1624 request_headers.find(spdy::kHttp2MethodHeader);
1625 if (method_it == request_headers.end()) {
1626 // TryCreatePushStream() would have reset the stream if it had no method.
1627 NOTREACHED();
1628 return false;
1629 }
1630
1631 // Request method must match.
1632 if (request_info.method != method_it->second) {
1633 return false;
1634 }
1635
1636 return true;
1637 }
1638
GetWeakPtrToSession()1639 base::WeakPtr<SpdySession> SpdySession::GetWeakPtrToSession() {
1640 return GetWeakPtr();
1641 }
1642
DumpMemoryStats(StreamSocket::SocketMemoryStats * stats,bool * is_session_active) const1643 size_t SpdySession::DumpMemoryStats(StreamSocket::SocketMemoryStats* stats,
1644 bool* is_session_active) const {
1645 // TODO(xunjieli): Include |pending_create_stream_queues_| when WeakPtr is
1646 // supported in memory_usage_estimator.h.
1647 *is_session_active = is_active();
1648 socket_->DumpMemoryStats(stats);
1649
1650 // |connection_| is estimated in stats->total_size. |read_buffer_| is
1651 // estimated in |read_buffer_size|. TODO(xunjieli): Make them use EMU().
1652 size_t read_buffer_size = read_buffer_ ? kReadBufferSize : 0;
1653 return stats->total_size + read_buffer_size +
1654 base::trace_event::EstimateMemoryUsage(spdy_session_key_) +
1655 base::trace_event::EstimateMemoryUsage(pooled_aliases_) +
1656 base::trace_event::EstimateMemoryUsage(active_streams_) +
1657 base::trace_event::EstimateMemoryUsage(created_streams_) +
1658 base::trace_event::EstimateMemoryUsage(write_queue_) +
1659 base::trace_event::EstimateMemoryUsage(in_flight_write_) +
1660 base::trace_event::EstimateMemoryUsage(buffered_spdy_framer_) +
1661 base::trace_event::EstimateMemoryUsage(initial_settings_) +
1662 base::trace_event::EstimateMemoryUsage(stream_send_unstall_queue_) +
1663 base::trace_event::EstimateMemoryUsage(priority_dependency_state_);
1664 }
1665
ChangeSocketTag(const SocketTag & new_tag)1666 bool SpdySession::ChangeSocketTag(const SocketTag& new_tag) {
1667 if (!IsAvailable() || !socket_)
1668 return false;
1669
1670 // Changing the tag on the underlying socket will affect all streams,
1671 // so only allow changing the tag when there are no active streams.
1672 if (is_active())
1673 return false;
1674
1675 socket_->ApplySocketTag(new_tag);
1676
1677 SpdySessionKey new_key(
1678 spdy_session_key_.host_port_pair(), spdy_session_key_.proxy_server(),
1679 spdy_session_key_.privacy_mode(), spdy_session_key_.is_proxy_session(),
1680 new_tag, spdy_session_key_.network_isolation_key(),
1681 spdy_session_key_.disable_secure_dns());
1682 spdy_session_key_ = new_key;
1683
1684 return true;
1685 }
1686
1687 // static
RecordSpdyPushedStreamFateHistogram(SpdyPushedStreamFate value)1688 void SpdySession::RecordSpdyPushedStreamFateHistogram(
1689 SpdyPushedStreamFate value) {
1690 UMA_HISTOGRAM_ENUMERATION("Net.SpdyPushedStreamFate", value);
1691 }
1692
InitializeInternal(SpdySessionPool * pool)1693 void SpdySession::InitializeInternal(SpdySessionPool* pool) {
1694 CHECK(!in_io_loop_);
1695 DCHECK_EQ(availability_state_, STATE_AVAILABLE);
1696 DCHECK_EQ(read_state_, READ_STATE_DO_READ);
1697 DCHECK_EQ(write_state_, WRITE_STATE_IDLE);
1698
1699 session_send_window_size_ = kDefaultInitialWindowSize;
1700 session_recv_window_size_ = kDefaultInitialWindowSize;
1701
1702 buffered_spdy_framer_ = std::make_unique<BufferedSpdyFramer>(
1703 initial_settings_.find(spdy::SETTINGS_MAX_HEADER_LIST_SIZE)->second,
1704 net_log_, time_func_);
1705 buffered_spdy_framer_->set_visitor(this);
1706 buffered_spdy_framer_->set_debug_visitor(this);
1707 buffered_spdy_framer_->UpdateHeaderDecoderTableSize(max_header_table_size_);
1708
1709 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_INITIALIZED, [&] {
1710 return NetLogSpdyInitializedParams(socket_->NetLog().source());
1711 });
1712
1713 DCHECK_EQ(availability_state_, STATE_AVAILABLE);
1714 if (enable_sending_initial_data_)
1715 SendInitialData();
1716 pool_ = pool;
1717
1718 // Bootstrap the read loop.
1719 base::ThreadTaskRunnerHandle::Get()->PostTask(
1720 FROM_HERE,
1721 base::BindOnce(&SpdySession::PumpReadLoop, weak_factory_.GetWeakPtr(),
1722 READ_STATE_DO_READ, OK));
1723 }
1724
1725 // {,Try}CreateStream() can be called with |in_io_loop_| set if a stream is
1726 // being created in response to another being closed due to received data.
1727
TryCreateStream(const base::WeakPtr<SpdyStreamRequest> & request,base::WeakPtr<SpdyStream> * stream)1728 int SpdySession::TryCreateStream(
1729 const base::WeakPtr<SpdyStreamRequest>& request,
1730 base::WeakPtr<SpdyStream>* stream) {
1731 DCHECK(request);
1732
1733 if (availability_state_ == STATE_GOING_AWAY)
1734 return ERR_FAILED;
1735
1736 if (availability_state_ == STATE_DRAINING)
1737 return ERR_CONNECTION_CLOSED;
1738
1739 // Fail if ChangeSocketTag() has been called.
1740 if (request->socket_tag_ != spdy_session_key_.socket_tag())
1741 return ERR_FAILED;
1742
1743 if ((active_streams_.size() + created_streams_.size() - num_pushed_streams_ <
1744 max_concurrent_streams_)) {
1745 return CreateStream(*request, stream);
1746 }
1747
1748 if (net_log().IsCapturing()) {
1749 net_log().AddEvent(NetLogEventType::HTTP2_SESSION_STALLED_MAX_STREAMS, [&] {
1750 return NetLogSpdySessionStalledParams(
1751 active_streams_.size(), created_streams_.size(), num_pushed_streams_,
1752 max_concurrent_streams_, request->url().spec());
1753 });
1754 }
1755 RequestPriority priority = request->priority();
1756 CHECK_GE(priority, MINIMUM_PRIORITY);
1757 CHECK_LE(priority, MAXIMUM_PRIORITY);
1758 pending_create_stream_queues_[priority].push_back(request);
1759 return ERR_IO_PENDING;
1760 }
1761
CreateStream(const SpdyStreamRequest & request,base::WeakPtr<SpdyStream> * stream)1762 int SpdySession::CreateStream(const SpdyStreamRequest& request,
1763 base::WeakPtr<SpdyStream>* stream) {
1764 DCHECK_GE(request.priority(), MINIMUM_PRIORITY);
1765 DCHECK_LE(request.priority(), MAXIMUM_PRIORITY);
1766
1767 if (availability_state_ == STATE_GOING_AWAY)
1768 return ERR_FAILED;
1769
1770 if (availability_state_ == STATE_DRAINING)
1771 return ERR_CONNECTION_CLOSED;
1772
1773 DCHECK(socket_);
1774 UMA_HISTOGRAM_BOOLEAN("Net.SpdySession.CreateStreamWithSocketConnected",
1775 socket_->IsConnected());
1776 if (!socket_->IsConnected()) {
1777 DoDrainSession(
1778 ERR_CONNECTION_CLOSED,
1779 "Tried to create SPDY stream for a closed socket connection.");
1780 return ERR_CONNECTION_CLOSED;
1781 }
1782
1783 auto new_stream = std::make_unique<SpdyStream>(
1784 request.type(), GetWeakPtr(), request.url(), request.priority(),
1785 stream_initial_send_window_size_, stream_max_recv_window_size_,
1786 request.net_log(), request.traffic_annotation());
1787 *stream = new_stream->GetWeakPtr();
1788 InsertCreatedStream(std::move(new_stream));
1789
1790 return OK;
1791 }
1792
CancelStreamRequest(const base::WeakPtr<SpdyStreamRequest> & request)1793 bool SpdySession::CancelStreamRequest(
1794 const base::WeakPtr<SpdyStreamRequest>& request) {
1795 DCHECK(request);
1796 RequestPriority priority = request->priority();
1797 CHECK_GE(priority, MINIMUM_PRIORITY);
1798 CHECK_LE(priority, MAXIMUM_PRIORITY);
1799
1800 #if DCHECK_IS_ON()
1801 // |request| should not be in a queue not matching its priority.
1802 for (int i = MINIMUM_PRIORITY; i <= MAXIMUM_PRIORITY; ++i) {
1803 if (priority == i)
1804 continue;
1805 PendingStreamRequestQueue* queue = &pending_create_stream_queues_[i];
1806 DCHECK(std::find_if(queue->begin(), queue->end(), RequestEquals(request)) ==
1807 queue->end());
1808 }
1809 #endif
1810
1811 PendingStreamRequestQueue* queue = &pending_create_stream_queues_[priority];
1812 // Remove |request| from |queue| while preserving the order of the
1813 // other elements.
1814 PendingStreamRequestQueue::iterator it =
1815 std::find_if(queue->begin(), queue->end(), RequestEquals(request));
1816 // The request may already be removed if there's a
1817 // CompleteStreamRequest() in flight.
1818 if (it != queue->end()) {
1819 it = queue->erase(it);
1820 // |request| should be in the queue at most once, and if it is
1821 // present, should not be pending completion.
1822 DCHECK(std::find_if(it, queue->end(), RequestEquals(request)) ==
1823 queue->end());
1824 return true;
1825 }
1826 return false;
1827 }
1828
ChangeStreamRequestPriority(const base::WeakPtr<SpdyStreamRequest> & request,RequestPriority priority)1829 void SpdySession::ChangeStreamRequestPriority(
1830 const base::WeakPtr<SpdyStreamRequest>& request,
1831 RequestPriority priority) {
1832 // |request->priority()| is updated by the caller after this returns.
1833 // |request| needs to still have its old priority in order for
1834 // CancelStreamRequest() to find it in the correct queue.
1835 DCHECK_NE(priority, request->priority());
1836 if (CancelStreamRequest(request)) {
1837 pending_create_stream_queues_[priority].push_back(request);
1838 }
1839 }
1840
GetNextPendingStreamRequest()1841 base::WeakPtr<SpdyStreamRequest> SpdySession::GetNextPendingStreamRequest() {
1842 for (int j = MAXIMUM_PRIORITY; j >= MINIMUM_PRIORITY; --j) {
1843 if (pending_create_stream_queues_[j].empty())
1844 continue;
1845
1846 base::WeakPtr<SpdyStreamRequest> pending_request =
1847 pending_create_stream_queues_[j].front();
1848 DCHECK(pending_request);
1849 pending_create_stream_queues_[j].pop_front();
1850 return pending_request;
1851 }
1852 return base::WeakPtr<SpdyStreamRequest>();
1853 }
1854
ProcessPendingStreamRequests()1855 void SpdySession::ProcessPendingStreamRequests() {
1856 size_t max_requests_to_process =
1857 max_concurrent_streams_ -
1858 (active_streams_.size() + created_streams_.size());
1859 for (size_t i = 0; i < max_requests_to_process; ++i) {
1860 base::WeakPtr<SpdyStreamRequest> pending_request =
1861 GetNextPendingStreamRequest();
1862 if (!pending_request)
1863 break;
1864
1865 // Note that this post can race with other stream creations, and it's
1866 // possible that the un-stalled stream will be stalled again if it loses.
1867 // TODO(jgraettinger): Provide stronger ordering guarantees.
1868 base::ThreadTaskRunnerHandle::Get()->PostTask(
1869 FROM_HERE, base::BindOnce(&SpdySession::CompleteStreamRequest,
1870 weak_factory_.GetWeakPtr(), pending_request));
1871 }
1872 }
1873
TryCreatePushStream(spdy::SpdyStreamId stream_id,spdy::SpdyStreamId associated_stream_id,spdy::SpdyHeaderBlock headers)1874 void SpdySession::TryCreatePushStream(spdy::SpdyStreamId stream_id,
1875 spdy::SpdyStreamId associated_stream_id,
1876 spdy::SpdyHeaderBlock headers) {
1877 // Pushed streams are speculative, so they start at an IDLE priority.
1878 // TODO(bnc): Send pushed stream cancellation with higher priority to avoid
1879 // wasting bandwidth.
1880 const RequestPriority request_priority = IDLE;
1881
1882 if (!enable_push_) {
1883 RecordSpdyPushedStreamFateHistogram(SpdyPushedStreamFate::kPushDisabled);
1884 EnqueueResetStreamFrame(stream_id, request_priority,
1885 spdy::ERROR_CODE_REFUSED_STREAM,
1886 "Push is disabled.");
1887 return;
1888 }
1889
1890 if ((stream_id & 0x1) != 0) {
1891 std::string description = base::StringPrintf(
1892 "Received invalid pushed stream id %d (must be even) on stream id %d.",
1893 stream_id, associated_stream_id);
1894 LOG(WARNING) << description;
1895 RecordSpdyPushedStreamFateHistogram(
1896 SpdyPushedStreamFate::kPromisedStreamIdParityError);
1897 CloseSessionOnError(ERR_HTTP2_PROTOCOL_ERROR, description);
1898 return;
1899 }
1900
1901 if ((associated_stream_id & 0x1) != 1) {
1902 std::string description = base::StringPrintf(
1903 "Received pushed stream id %d on invalid stream id %d (must be odd).",
1904 stream_id, associated_stream_id);
1905 LOG(WARNING) << description;
1906 RecordSpdyPushedStreamFateHistogram(
1907 SpdyPushedStreamFate::kAssociatedStreamIdParityError);
1908 CloseSessionOnError(ERR_HTTP2_PROTOCOL_ERROR, description);
1909 return;
1910 }
1911
1912 if (stream_id <= last_accepted_push_stream_id_) {
1913 std::string description = base::StringPrintf(
1914 "Received pushed stream id %d must be larger than last accepted id %d.",
1915 stream_id, last_accepted_push_stream_id_);
1916 LOG(WARNING) << description;
1917 RecordSpdyPushedStreamFateHistogram(
1918 SpdyPushedStreamFate::kStreamIdOutOfOrder);
1919 CloseSessionOnError(ERR_HTTP2_PROTOCOL_ERROR, description);
1920 return;
1921 }
1922
1923 // |last_accepted_push_stream_id_| check above guarantees that this stream has
1924 // not been activated yet.
1925 DCHECK(!IsStreamActive(stream_id));
1926
1927 last_accepted_push_stream_id_ = stream_id;
1928
1929 if (availability_state_ == STATE_GOING_AWAY) {
1930 RecordSpdyPushedStreamFateHistogram(SpdyPushedStreamFate::kGoingAway);
1931 EnqueueResetStreamFrame(stream_id, request_priority,
1932 spdy::ERROR_CODE_REFUSED_STREAM,
1933 "Push stream request received while going away.");
1934 return;
1935 }
1936
1937 streams_pushed_count_++;
1938
1939 // Verify that the response had a URL for us.
1940 GURL gurl(quic::SpdyServerPushUtils::GetPromisedUrlFromHeaders(headers));
1941 if (!gurl.is_valid()) {
1942 RecordSpdyPushedStreamFateHistogram(SpdyPushedStreamFate::kInvalidUrl);
1943 EnqueueResetStreamFrame(stream_id, request_priority,
1944 spdy::ERROR_CODE_REFUSED_STREAM,
1945 "Invalid pushed request headers.");
1946 return;
1947 }
1948
1949 // GetPromisedUrlFromHeaders() guarantees that the scheme is http or https.
1950 DCHECK(gurl.SchemeIs(url::kHttpScheme) || gurl.SchemeIs(url::kHttpsScheme));
1951
1952 // "Promised requests MUST be cacheable and MUST be safe [...]" (RFC7540
1953 // Section 8.2). Only cacheable safe request methods are GET and HEAD.
1954 // GetPromisedUrlFromHeaders() guarantees that the method is GET or HEAD.
1955 spdy::SpdyHeaderBlock::const_iterator it =
1956 headers.find(spdy::kHttp2MethodHeader);
1957 DCHECK(it != headers.end() && (it->second == "GET" || it->second == "HEAD"));
1958
1959 // Verify we have a valid stream association.
1960 auto associated_it = active_streams_.find(associated_stream_id);
1961 if (associated_it == active_streams_.end()) {
1962 RecordSpdyPushedStreamFateHistogram(
1963 SpdyPushedStreamFate::kInactiveAssociatedStream);
1964 EnqueueResetStreamFrame(stream_id, request_priority,
1965 spdy::ERROR_CODE_STREAM_CLOSED,
1966 "Inactive associated stream.");
1967 return;
1968 }
1969
1970 // Cross-origin push validation.
1971 GURL associated_url(associated_it->second->url());
1972 if (associated_url.GetOrigin() != gurl.GetOrigin()) {
1973 if (is_trusted_proxy_) {
1974 if (!gurl.SchemeIs(url::kHttpScheme)) {
1975 RecordSpdyPushedStreamFateHistogram(
1976 SpdyPushedStreamFate::kNonHttpSchemeFromTrustedProxy);
1977 EnqueueResetStreamFrame(
1978 stream_id, request_priority, spdy::ERROR_CODE_REFUSED_STREAM,
1979 "Only http scheme allowed for cross origin push by trusted proxy.");
1980 return;
1981 }
1982 } else {
1983 if (!gurl.SchemeIs(url::kHttpsScheme)) {
1984 RecordSpdyPushedStreamFateHistogram(
1985 SpdyPushedStreamFate::kNonHttpsPushedScheme);
1986 EnqueueResetStreamFrame(stream_id, request_priority,
1987 spdy::ERROR_CODE_REFUSED_STREAM,
1988 "Pushed URL must have https scheme.");
1989 return;
1990 }
1991 if (!associated_url.SchemeIs(url::kHttpsScheme)) {
1992 RecordSpdyPushedStreamFateHistogram(
1993 SpdyPushedStreamFate::kNonHttpsAssociatedScheme);
1994 EnqueueResetStreamFrame(stream_id, request_priority,
1995 spdy::ERROR_CODE_REFUSED_STREAM,
1996 "Associated URL must have https scheme.");
1997 return;
1998 }
1999 SSLInfo ssl_info;
2000 CHECK(GetSSLInfo(&ssl_info));
2001 if (!CanPool(transport_security_state_, ssl_info, *ssl_config_service_,
2002 associated_url.host(), gurl.host())) {
2003 RecordSpdyPushedStreamFateHistogram(
2004 SpdyPushedStreamFate::kCertificateMismatch);
2005 EnqueueResetStreamFrame(stream_id, request_priority,
2006 spdy::ERROR_CODE_REFUSED_STREAM,
2007 "Certificate does not match pushed URL.");
2008 return;
2009 }
2010 }
2011 }
2012
2013 // Insertion fails if there already is a pushed stream with the same path.
2014 if (!pool_->push_promise_index()->RegisterUnclaimedPushedStream(
2015 gurl, stream_id, this)) {
2016 RecordSpdyPushedStreamFateHistogram(SpdyPushedStreamFate::kDuplicateUrl);
2017 EnqueueResetStreamFrame(stream_id, request_priority,
2018 spdy::ERROR_CODE_REFUSED_STREAM,
2019 "Duplicate pushed stream with url: " + gurl.spec());
2020 return;
2021 }
2022
2023 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
2024 FROM_HERE,
2025 base::BindOnce(&SpdySession::CancelPushedStreamIfUnclaimed, GetWeakPtr(),
2026 stream_id),
2027 base::TimeDelta::FromSeconds(kPushedStreamLifetimeSeconds));
2028
2029 net::NetworkTrafficAnnotationTag traffic_annotation =
2030 net::DefineNetworkTrafficAnnotation("spdy_push_stream", R"(
2031 semantics {
2032 sender: "Spdy Session"
2033 description:
2034 "When a web server needs to push a response to a client, an "
2035 "incoming stream is created to reply the client with pushed "
2036 "message instead of a message from the network."
2037 trigger:
2038 "A request by a server to push a response to the client."
2039 data: "None."
2040 destination: OTHER
2041 destination_other:
2042 "This stream is not used for sending data."
2043 }
2044 policy {
2045 cookies_allowed: NO
2046 setting: "This feature cannot be disabled."
2047 policy_exception_justification: "Essential for navigation."
2048 }
2049 )");
2050
2051 auto stream = std::make_unique<SpdyStream>(
2052 SPDY_PUSH_STREAM, GetWeakPtr(), gurl, request_priority,
2053 stream_initial_send_window_size_, stream_max_recv_window_size_, net_log_,
2054 traffic_annotation);
2055 stream->set_stream_id(stream_id);
2056
2057 // Convert RequestPriority to a spdy::SpdyPriority to send in a PRIORITY
2058 // frame.
2059 spdy::SpdyPriority spdy_priority =
2060 ConvertRequestPriorityToSpdyPriority(request_priority);
2061 spdy::SpdyStreamId dependency_id = 0;
2062 int weight = 0;
2063 bool exclusive = false;
2064 priority_dependency_state_.OnStreamCreation(
2065 stream_id, spdy_priority, &dependency_id, &weight, &exclusive);
2066 EnqueuePriorityFrame(stream_id, dependency_id, weight, exclusive);
2067
2068 // PUSH_PROMISE arrives on associated stream.
2069 associated_it->second->AddRawReceivedBytes(last_compressed_frame_len_);
2070 last_compressed_frame_len_ = 0;
2071
2072 InsertActivatedStream(std::move(stream));
2073
2074 auto active_it = active_streams_.find(stream_id);
2075 DCHECK(active_it != active_streams_.end());
2076
2077 // Notify the push_delegate that a push promise has been received.
2078 if (push_delegate_) {
2079 push_delegate_->OnPush(std::make_unique<SpdyServerPushHelper>(
2080 weak_factory_.GetWeakPtr(), gurl),
2081 net_log_);
2082 }
2083
2084 active_it->second->OnPushPromiseHeadersReceived(std::move(headers),
2085 std::move(gurl));
2086 DCHECK(active_it->second->IsReservedRemote());
2087 num_pushed_streams_++;
2088 return;
2089 }
2090
CloseActiveStreamIterator(ActiveStreamMap::iterator it,int status)2091 void SpdySession::CloseActiveStreamIterator(ActiveStreamMap::iterator it,
2092 int status) {
2093 // TODO(mbelshe): We should send a RST_STREAM control frame here
2094 // so that the server can cancel a large send.
2095
2096 std::unique_ptr<SpdyStream> owned_stream(it->second);
2097 active_streams_.erase(it);
2098 priority_dependency_state_.OnStreamDestruction(owned_stream->stream_id());
2099
2100 // TODO(akalin): When SpdyStream was ref-counted (and
2101 // |unclaimed_pushed_streams_| held scoped_refptr<SpdyStream>), this
2102 // was only done when status was not OK. This meant that pushed
2103 // streams can still be claimed after they're closed. This is
2104 // probably something that we still want to support, although server
2105 // push is hardly used. Write tests for this and fix this. (See
2106 // http://crbug.com/261712 .)
2107 if (owned_stream->type() == SPDY_PUSH_STREAM) {
2108 if (pool_->push_promise_index()->UnregisterUnclaimedPushedStream(
2109 owned_stream->url(), owned_stream->stream_id(), this)) {
2110 bytes_pushed_and_unclaimed_count_ += owned_stream->recv_bytes();
2111 }
2112 bytes_pushed_count_ += owned_stream->recv_bytes();
2113 num_pushed_streams_--;
2114 if (!owned_stream->IsReservedRemote())
2115 num_active_pushed_streams_--;
2116 }
2117
2118 DeleteStream(std::move(owned_stream), status);
2119
2120 // If the socket belongs to a socket pool, and there are no active streams,
2121 // and the socket pool is stalled, then close the session to free up a socket
2122 // slot.
2123 if (client_socket_handle_ && active_streams_.empty() &&
2124 created_streams_.empty() && client_socket_handle_->IsPoolStalled()) {
2125 DoDrainSession(ERR_CONNECTION_CLOSED, "Closing idle connection.");
2126 }
2127 }
2128
CloseCreatedStreamIterator(CreatedStreamSet::iterator it,int status)2129 void SpdySession::CloseCreatedStreamIterator(CreatedStreamSet::iterator it,
2130 int status) {
2131 std::unique_ptr<SpdyStream> owned_stream(*it);
2132 created_streams_.erase(it);
2133 DeleteStream(std::move(owned_stream), status);
2134 }
2135
ResetStreamIterator(ActiveStreamMap::iterator it,int error,const std::string & description)2136 void SpdySession::ResetStreamIterator(ActiveStreamMap::iterator it,
2137 int error,
2138 const std::string& description) {
2139 // Send the RST_STREAM frame first as CloseActiveStreamIterator()
2140 // may close us.
2141 spdy::SpdyErrorCode error_code = spdy::ERROR_CODE_PROTOCOL_ERROR;
2142 if (error == ERR_FAILED) {
2143 error_code = spdy::ERROR_CODE_INTERNAL_ERROR;
2144 } else if (error == ERR_ABORTED ||
2145 error == ERR_HTTP2_PUSHED_RESPONSE_DOES_NOT_MATCH) {
2146 error_code = spdy::ERROR_CODE_CANCEL;
2147 } else if (error == ERR_HTTP2_FLOW_CONTROL_ERROR) {
2148 error_code = spdy::ERROR_CODE_FLOW_CONTROL_ERROR;
2149 } else if (error == ERR_TIMED_OUT ||
2150 error == ERR_HTTP2_CLIENT_REFUSED_STREAM) {
2151 error_code = spdy::ERROR_CODE_REFUSED_STREAM;
2152 } else if (error == ERR_HTTP2_STREAM_CLOSED) {
2153 error_code = spdy::ERROR_CODE_STREAM_CLOSED;
2154 }
2155 spdy::SpdyStreamId stream_id = it->first;
2156 RequestPriority priority = it->second->priority();
2157 EnqueueResetStreamFrame(stream_id, priority, error_code, description);
2158
2159 // Removes any pending writes for the stream except for possibly an
2160 // in-flight one.
2161 CloseActiveStreamIterator(it, error);
2162 }
2163
EnqueueResetStreamFrame(spdy::SpdyStreamId stream_id,RequestPriority priority,spdy::SpdyErrorCode error_code,const std::string & description)2164 void SpdySession::EnqueueResetStreamFrame(spdy::SpdyStreamId stream_id,
2165 RequestPriority priority,
2166 spdy::SpdyErrorCode error_code,
2167 const std::string& description) {
2168 DCHECK_NE(stream_id, 0u);
2169
2170 net_log().AddEvent(NetLogEventType::HTTP2_SESSION_SEND_RST_STREAM, [&] {
2171 return NetLogSpdySendRstStreamParams(stream_id, error_code, description);
2172 });
2173
2174 DCHECK(buffered_spdy_framer_.get());
2175 std::unique_ptr<spdy::SpdySerializedFrame> rst_frame(
2176 buffered_spdy_framer_->CreateRstStream(stream_id, error_code));
2177
2178 EnqueueSessionWrite(priority, spdy::SpdyFrameType::RST_STREAM,
2179 std::move(rst_frame));
2180 RecordProtocolErrorHistogram(MapRstStreamStatusToProtocolError(error_code));
2181 }
2182
EnqueuePriorityFrame(spdy::SpdyStreamId stream_id,spdy::SpdyStreamId dependency_id,int weight,bool exclusive)2183 void SpdySession::EnqueuePriorityFrame(spdy::SpdyStreamId stream_id,
2184 spdy::SpdyStreamId dependency_id,
2185 int weight,
2186 bool exclusive) {
2187 net_log().AddEvent(NetLogEventType::HTTP2_STREAM_SEND_PRIORITY, [&] {
2188 return NetLogSpdyPriorityParams(stream_id, dependency_id, weight,
2189 exclusive);
2190 });
2191
2192 DCHECK(buffered_spdy_framer_.get());
2193 std::unique_ptr<spdy::SpdySerializedFrame> frame(
2194 buffered_spdy_framer_->CreatePriority(stream_id, dependency_id, weight,
2195 exclusive));
2196
2197 // PRIORITY frames describe sequenced updates to the tree, so they must
2198 // be serialized. We do this by queueing all PRIORITY frames at HIGHEST
2199 // priority.
2200 EnqueueWrite(HIGHEST, spdy::SpdyFrameType::PRIORITY,
2201 std::make_unique<SimpleBufferProducer>(
2202 std::make_unique<SpdyBuffer>(std::move(frame))),
2203 base::WeakPtr<SpdyStream>(),
2204 kSpdySessionCommandsTrafficAnnotation);
2205 }
2206
PumpReadLoop(ReadState expected_read_state,int result)2207 void SpdySession::PumpReadLoop(ReadState expected_read_state, int result) {
2208 CHECK(!in_io_loop_);
2209 if (availability_state_ == STATE_DRAINING) {
2210 return;
2211 }
2212 ignore_result(DoReadLoop(expected_read_state, result));
2213 }
2214
DoReadLoop(ReadState expected_read_state,int result)2215 int SpdySession::DoReadLoop(ReadState expected_read_state, int result) {
2216 CHECK(!in_io_loop_);
2217 CHECK_EQ(read_state_, expected_read_state);
2218
2219 in_io_loop_ = true;
2220
2221 int bytes_read_without_yielding = 0;
2222 const base::TimeTicks yield_after_time =
2223 time_func_() +
2224 base::TimeDelta::FromMilliseconds(kYieldAfterDurationMilliseconds);
2225
2226 // Loop until the session is draining, the read becomes blocked, or
2227 // the read limit is exceeded.
2228 while (true) {
2229 switch (read_state_) {
2230 case READ_STATE_DO_READ:
2231 CHECK_EQ(result, OK);
2232 result = DoRead();
2233 break;
2234 case READ_STATE_DO_READ_COMPLETE:
2235 if (result > 0)
2236 bytes_read_without_yielding += result;
2237 result = DoReadComplete(result);
2238 break;
2239 default:
2240 NOTREACHED() << "read_state_: " << read_state_;
2241 break;
2242 }
2243
2244 if (availability_state_ == STATE_DRAINING)
2245 break;
2246
2247 if (result == ERR_IO_PENDING)
2248 break;
2249
2250 if (read_state_ == READ_STATE_DO_READ &&
2251 (bytes_read_without_yielding > kYieldAfterBytesRead ||
2252 time_func_() > yield_after_time)) {
2253 base::ThreadTaskRunnerHandle::Get()->PostTask(
2254 FROM_HERE,
2255 base::BindOnce(&SpdySession::PumpReadLoop, weak_factory_.GetWeakPtr(),
2256 READ_STATE_DO_READ, OK));
2257 result = ERR_IO_PENDING;
2258 break;
2259 }
2260 }
2261
2262 CHECK(in_io_loop_);
2263 in_io_loop_ = false;
2264
2265 return result;
2266 }
2267
DoRead()2268 int SpdySession::DoRead() {
2269 DCHECK(!read_buffer_);
2270 CHECK(in_io_loop_);
2271
2272 CHECK(socket_);
2273 read_state_ = READ_STATE_DO_READ_COMPLETE;
2274 read_buffer_ = base::MakeRefCounted<IOBuffer>(kReadBufferSize);
2275 int rv = socket_->ReadIfReady(
2276 read_buffer_.get(), kReadBufferSize,
2277 base::BindOnce(&SpdySession::PumpReadLoop, weak_factory_.GetWeakPtr(),
2278 READ_STATE_DO_READ));
2279 if (rv == ERR_IO_PENDING) {
2280 read_buffer_ = nullptr;
2281 read_state_ = READ_STATE_DO_READ;
2282 return rv;
2283 }
2284 if (rv == ERR_READ_IF_READY_NOT_IMPLEMENTED) {
2285 // Fallback to regular Read().
2286 return socket_->Read(
2287 read_buffer_.get(), kReadBufferSize,
2288 base::BindOnce(&SpdySession::PumpReadLoop, weak_factory_.GetWeakPtr(),
2289 READ_STATE_DO_READ_COMPLETE));
2290 }
2291 return rv;
2292 }
2293
DoReadComplete(int result)2294 int SpdySession::DoReadComplete(int result) {
2295 DCHECK(read_buffer_);
2296 CHECK(in_io_loop_);
2297
2298 // Parse a frame. For now this code requires that the frame fit into our
2299 // buffer (kReadBufferSize).
2300 // TODO(mbelshe): support arbitrarily large frames!
2301
2302 if (result == 0) {
2303 DoDrainSession(ERR_CONNECTION_CLOSED, "Connection closed");
2304 return ERR_CONNECTION_CLOSED;
2305 }
2306
2307 if (result < 0) {
2308 DoDrainSession(
2309 static_cast<Error>(result),
2310 base::StringPrintf("Error %d reading from socket.", -result));
2311 return result;
2312 }
2313 CHECK_LE(result, kReadBufferSize);
2314
2315 last_read_time_ = time_func_();
2316
2317 DCHECK(buffered_spdy_framer_.get());
2318 char* data = read_buffer_->data();
2319 while (result > 0) {
2320 uint32_t bytes_processed =
2321 buffered_spdy_framer_->ProcessInput(data, result);
2322 result -= bytes_processed;
2323 data += bytes_processed;
2324
2325 if (availability_state_ == STATE_DRAINING) {
2326 return ERR_CONNECTION_CLOSED;
2327 }
2328
2329 DCHECK_EQ(buffered_spdy_framer_->spdy_framer_error(),
2330 http2::Http2DecoderAdapter::SPDY_NO_ERROR);
2331 }
2332
2333 read_buffer_ = nullptr;
2334 read_state_ = READ_STATE_DO_READ;
2335 return OK;
2336 }
2337
PumpWriteLoop(WriteState expected_write_state,int result)2338 void SpdySession::PumpWriteLoop(WriteState expected_write_state, int result) {
2339 CHECK(!in_io_loop_);
2340 DCHECK_EQ(write_state_, expected_write_state);
2341
2342 DoWriteLoop(expected_write_state, result);
2343
2344 if (availability_state_ == STATE_DRAINING && !in_flight_write_ &&
2345 write_queue_.IsEmpty()) {
2346 pool_->RemoveUnavailableSession(GetWeakPtr()); // Destroys |this|.
2347 return;
2348 }
2349 }
2350
MaybePostWriteLoop()2351 void SpdySession::MaybePostWriteLoop() {
2352 if (write_state_ == WRITE_STATE_IDLE) {
2353 CHECK(!in_flight_write_);
2354 write_state_ = WRITE_STATE_DO_WRITE;
2355 base::ThreadTaskRunnerHandle::Get()->PostTask(
2356 FROM_HERE,
2357 base::BindOnce(&SpdySession::PumpWriteLoop, weak_factory_.GetWeakPtr(),
2358 WRITE_STATE_DO_WRITE, OK));
2359 }
2360 }
2361
DoWriteLoop(WriteState expected_write_state,int result)2362 int SpdySession::DoWriteLoop(WriteState expected_write_state, int result) {
2363 CHECK(!in_io_loop_);
2364 DCHECK_NE(write_state_, WRITE_STATE_IDLE);
2365 DCHECK_EQ(write_state_, expected_write_state);
2366
2367 in_io_loop_ = true;
2368
2369 // Loop until the session is closed or the write becomes blocked.
2370 while (true) {
2371 switch (write_state_) {
2372 case WRITE_STATE_DO_WRITE:
2373 DCHECK_EQ(result, OK);
2374 result = DoWrite();
2375 break;
2376 case WRITE_STATE_DO_WRITE_COMPLETE:
2377 result = DoWriteComplete(result);
2378 break;
2379 case WRITE_STATE_IDLE:
2380 default:
2381 NOTREACHED() << "write_state_: " << write_state_;
2382 break;
2383 }
2384
2385 if (write_state_ == WRITE_STATE_IDLE) {
2386 DCHECK_EQ(result, ERR_IO_PENDING);
2387 break;
2388 }
2389
2390 if (result == ERR_IO_PENDING)
2391 break;
2392 }
2393
2394 CHECK(in_io_loop_);
2395 in_io_loop_ = false;
2396
2397 return result;
2398 }
2399
DoWrite()2400 int SpdySession::DoWrite() {
2401 CHECK(in_io_loop_);
2402
2403 DCHECK(buffered_spdy_framer_);
2404 if (in_flight_write_) {
2405 DCHECK_GT(in_flight_write_->GetRemainingSize(), 0u);
2406 } else {
2407 // Grab the next frame to send.
2408 spdy::SpdyFrameType frame_type = spdy::SpdyFrameType::DATA;
2409 std::unique_ptr<SpdyBufferProducer> producer;
2410 base::WeakPtr<SpdyStream> stream;
2411 if (!write_queue_.Dequeue(&frame_type, &producer, &stream,
2412 &in_flight_write_traffic_annotation)) {
2413 write_state_ = WRITE_STATE_IDLE;
2414 return ERR_IO_PENDING;
2415 }
2416
2417 if (stream.get())
2418 CHECK(!stream->IsClosed());
2419
2420 // Activate the stream only when sending the HEADERS frame to
2421 // guarantee monotonically-increasing stream IDs.
2422 if (frame_type == spdy::SpdyFrameType::HEADERS) {
2423 CHECK(stream.get());
2424 CHECK_EQ(stream->stream_id(), 0u);
2425 std::unique_ptr<SpdyStream> owned_stream =
2426 ActivateCreatedStream(stream.get());
2427 InsertActivatedStream(std::move(owned_stream));
2428
2429 if (stream_hi_water_mark_ > kLastStreamId) {
2430 CHECK_EQ(stream->stream_id(), kLastStreamId);
2431 // We've exhausted the stream ID space, and no new streams may be
2432 // created after this one.
2433 MakeUnavailable();
2434 StartGoingAway(kLastStreamId, ERR_HTTP2_PROTOCOL_ERROR);
2435 }
2436 }
2437
2438 in_flight_write_ = producer->ProduceBuffer();
2439 if (!in_flight_write_) {
2440 NOTREACHED();
2441 return ERR_UNEXPECTED;
2442 }
2443 in_flight_write_frame_type_ = frame_type;
2444 in_flight_write_frame_size_ = in_flight_write_->GetRemainingSize();
2445 DCHECK_GE(in_flight_write_frame_size_, spdy::kFrameMinimumSize);
2446 in_flight_write_stream_ = stream;
2447 }
2448
2449 write_state_ = WRITE_STATE_DO_WRITE_COMPLETE;
2450
2451 scoped_refptr<IOBuffer> write_io_buffer =
2452 in_flight_write_->GetIOBufferForRemainingData();
2453 return socket_->Write(
2454 write_io_buffer.get(), in_flight_write_->GetRemainingSize(),
2455 base::BindOnce(&SpdySession::PumpWriteLoop, weak_factory_.GetWeakPtr(),
2456 WRITE_STATE_DO_WRITE_COMPLETE),
2457 NetworkTrafficAnnotationTag(in_flight_write_traffic_annotation));
2458 }
2459
DoWriteComplete(int result)2460 int SpdySession::DoWriteComplete(int result) {
2461 CHECK(in_io_loop_);
2462 DCHECK_NE(result, ERR_IO_PENDING);
2463 DCHECK_GT(in_flight_write_->GetRemainingSize(), 0u);
2464
2465 if (result < 0) {
2466 DCHECK_NE(result, ERR_IO_PENDING);
2467 in_flight_write_.reset();
2468 in_flight_write_frame_type_ = spdy::SpdyFrameType::DATA;
2469 in_flight_write_frame_size_ = 0;
2470 in_flight_write_stream_.reset();
2471 in_flight_write_traffic_annotation.reset();
2472 write_state_ = WRITE_STATE_DO_WRITE;
2473 DoDrainSession(static_cast<Error>(result), "Write error");
2474 return OK;
2475 }
2476
2477 // It should not be possible to have written more bytes than our
2478 // in_flight_write_.
2479 DCHECK_LE(static_cast<size_t>(result), in_flight_write_->GetRemainingSize());
2480
2481 if (result > 0) {
2482 in_flight_write_->Consume(static_cast<size_t>(result));
2483 if (in_flight_write_stream_.get())
2484 in_flight_write_stream_->AddRawSentBytes(static_cast<size_t>(result));
2485
2486 // We only notify the stream when we've fully written the pending frame.
2487 if (in_flight_write_->GetRemainingSize() == 0) {
2488 // It is possible that the stream was cancelled while we were
2489 // writing to the socket.
2490 if (in_flight_write_stream_.get()) {
2491 DCHECK_GT(in_flight_write_frame_size_, 0u);
2492 in_flight_write_stream_->OnFrameWriteComplete(
2493 in_flight_write_frame_type_, in_flight_write_frame_size_);
2494 }
2495
2496 // Cleanup the write which just completed.
2497 in_flight_write_.reset();
2498 in_flight_write_frame_type_ = spdy::SpdyFrameType::DATA;
2499 in_flight_write_frame_size_ = 0;
2500 in_flight_write_stream_.reset();
2501 }
2502 }
2503
2504 write_state_ = WRITE_STATE_DO_WRITE;
2505 return OK;
2506 }
2507
NotifyRequestsOfConfirmation(int rv)2508 void SpdySession::NotifyRequestsOfConfirmation(int rv) {
2509 for (auto& callback : waiting_for_confirmation_callbacks_) {
2510 base::ThreadTaskRunnerHandle::Get()->PostTask(
2511 FROM_HERE, base::BindOnce(std::move(callback), rv));
2512 }
2513 waiting_for_confirmation_callbacks_.clear();
2514 in_confirm_handshake_ = false;
2515 }
2516
SendInitialData()2517 void SpdySession::SendInitialData() {
2518 DCHECK(enable_sending_initial_data_);
2519 DCHECK(buffered_spdy_framer_.get());
2520
2521 // Prepare initial SETTINGS frame. Only send settings that have a value
2522 // different from the protocol default value.
2523 spdy::SettingsMap settings_map;
2524 for (auto setting : initial_settings_) {
2525 if (!IsSpdySettingAtDefaultInitialValue(setting.first, setting.second)) {
2526 settings_map.insert(setting);
2527 }
2528 }
2529 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_SEND_SETTINGS, [&] {
2530 return NetLogSpdySendSettingsParams(&settings_map);
2531 });
2532 std::unique_ptr<spdy::SpdySerializedFrame> settings_frame(
2533 buffered_spdy_framer_->CreateSettings(settings_map));
2534
2535 // Prepare initial WINDOW_UPDATE frame.
2536 // Make sure |session_max_recv_window_size_ - session_recv_window_size_|
2537 // does not underflow.
2538 DCHECK_GE(session_max_recv_window_size_, session_recv_window_size_);
2539 DCHECK_GE(session_recv_window_size_, 0);
2540 DCHECK_EQ(0, session_unacked_recv_window_bytes_);
2541 std::unique_ptr<spdy::SpdySerializedFrame> window_update_frame;
2542 const bool send_window_update =
2543 session_max_recv_window_size_ > session_recv_window_size_;
2544 if (send_window_update) {
2545 const int32_t delta_window_size =
2546 session_max_recv_window_size_ - session_recv_window_size_;
2547 session_recv_window_size_ += delta_window_size;
2548 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_UPDATE_RECV_WINDOW, [&] {
2549 return NetLogSpdySessionWindowUpdateParams(delta_window_size,
2550 session_recv_window_size_);
2551 });
2552
2553 session_unacked_recv_window_bytes_ += delta_window_size;
2554 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_SEND_WINDOW_UPDATE, [&] {
2555 return NetLogSpdyWindowUpdateFrameParams(
2556 spdy::kSessionFlowControlStreamId,
2557 session_unacked_recv_window_bytes_);
2558 });
2559 window_update_frame = buffered_spdy_framer_->CreateWindowUpdate(
2560 spdy::kSessionFlowControlStreamId, session_unacked_recv_window_bytes_);
2561 session_unacked_recv_window_bytes_ = 0;
2562 }
2563
2564 // Create a single frame to hold connection prefix, initial SETTINGS frame,
2565 // and optional initial WINDOW_UPDATE frame, so that they are sent on the wire
2566 // in a single packet.
2567 size_t initial_frame_size =
2568 spdy::kHttp2ConnectionHeaderPrefixSize + settings_frame->size();
2569 if (send_window_update)
2570 initial_frame_size += window_update_frame->size();
2571 auto initial_frame_data = std::make_unique<char[]>(initial_frame_size);
2572 size_t offset = 0;
2573
2574 memcpy(initial_frame_data.get() + offset, spdy::kHttp2ConnectionHeaderPrefix,
2575 spdy::kHttp2ConnectionHeaderPrefixSize);
2576 offset += spdy::kHttp2ConnectionHeaderPrefixSize;
2577
2578 memcpy(initial_frame_data.get() + offset, settings_frame->data(),
2579 settings_frame->size());
2580 offset += settings_frame->size();
2581
2582 if (send_window_update) {
2583 memcpy(initial_frame_data.get() + offset, window_update_frame->data(),
2584 window_update_frame->size());
2585 }
2586
2587 auto initial_frame = std::make_unique<spdy::SpdySerializedFrame>(
2588 initial_frame_data.release(), initial_frame_size,
2589 /* owns_buffer = */ true);
2590 EnqueueSessionWrite(HIGHEST, spdy::SpdyFrameType::SETTINGS,
2591 std::move(initial_frame));
2592 }
2593
HandleSetting(uint32_t id,uint32_t value)2594 void SpdySession::HandleSetting(uint32_t id, uint32_t value) {
2595 switch (id) {
2596 case spdy::SETTINGS_MAX_CONCURRENT_STREAMS:
2597 max_concurrent_streams_ =
2598 std::min(static_cast<size_t>(value), kMaxConcurrentStreamLimit);
2599 ProcessPendingStreamRequests();
2600 break;
2601 case spdy::SETTINGS_INITIAL_WINDOW_SIZE: {
2602 if (value > static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) {
2603 net_log().AddEventWithIntParams(
2604 NetLogEventType::HTTP2_SESSION_INITIAL_WINDOW_SIZE_OUT_OF_RANGE,
2605 "initial_window_size", value);
2606 return;
2607 }
2608
2609 // spdy::SETTINGS_INITIAL_WINDOW_SIZE updates initial_send_window_size_
2610 // only.
2611 int32_t delta_window_size =
2612 static_cast<int32_t>(value) - stream_initial_send_window_size_;
2613 stream_initial_send_window_size_ = static_cast<int32_t>(value);
2614 UpdateStreamsSendWindowSize(delta_window_size);
2615 net_log().AddEventWithIntParams(
2616 NetLogEventType::HTTP2_SESSION_UPDATE_STREAMS_SEND_WINDOW_SIZE,
2617 "delta_window_size", delta_window_size);
2618 break;
2619 }
2620 case spdy::SETTINGS_ENABLE_CONNECT_PROTOCOL:
2621 if ((value != 0 && value != 1) || (support_websocket_ && value == 0)) {
2622 DoDrainSession(
2623 ERR_HTTP2_PROTOCOL_ERROR,
2624 "Invalid value for spdy::SETTINGS_ENABLE_CONNECT_PROTOCOL.");
2625 return;
2626 }
2627 if (value == 1) {
2628 support_websocket_ = true;
2629 }
2630 break;
2631 }
2632 }
2633
UpdateStreamsSendWindowSize(int32_t delta_window_size)2634 void SpdySession::UpdateStreamsSendWindowSize(int32_t delta_window_size) {
2635 for (const auto& value : active_streams_) {
2636 if (!value.second->AdjustSendWindowSize(delta_window_size)) {
2637 DoDrainSession(
2638 ERR_HTTP2_FLOW_CONTROL_ERROR,
2639 base::StringPrintf(
2640 "New spdy::SETTINGS_INITIAL_WINDOW_SIZE value overflows "
2641 "flow control window of stream %d.",
2642 value.second->stream_id()));
2643 return;
2644 }
2645 }
2646
2647 for (auto* const stream : created_streams_) {
2648 if (!stream->AdjustSendWindowSize(delta_window_size)) {
2649 DoDrainSession(
2650 ERR_HTTP2_FLOW_CONTROL_ERROR,
2651 base::StringPrintf(
2652 "New spdy::SETTINGS_INITIAL_WINDOW_SIZE value overflows "
2653 "flow control window of stream %d.",
2654 stream->stream_id()));
2655 return;
2656 }
2657 }
2658 }
2659
MaybeSendPrefacePing()2660 void SpdySession::MaybeSendPrefacePing() {
2661 if (ping_in_flight_ || check_ping_status_pending_ ||
2662 !enable_ping_based_connection_checking_) {
2663 return;
2664 }
2665
2666 // If there has been no read activity in the session for some time,
2667 // then send a preface-PING.
2668 if (time_func_() > last_read_time_ + connection_at_risk_of_loss_time_)
2669 WritePingFrame(next_ping_id_, false);
2670 }
2671
SendWindowUpdateFrame(spdy::SpdyStreamId stream_id,uint32_t delta_window_size,RequestPriority priority)2672 void SpdySession::SendWindowUpdateFrame(spdy::SpdyStreamId stream_id,
2673 uint32_t delta_window_size,
2674 RequestPriority priority) {
2675 ActiveStreamMap::const_iterator it = active_streams_.find(stream_id);
2676 if (it != active_streams_.end()) {
2677 CHECK_EQ(it->second->stream_id(), stream_id);
2678 } else {
2679 CHECK_EQ(stream_id, spdy::kSessionFlowControlStreamId);
2680 }
2681
2682 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_SEND_WINDOW_UPDATE, [&] {
2683 return NetLogSpdyWindowUpdateFrameParams(stream_id, delta_window_size);
2684 });
2685
2686 DCHECK(buffered_spdy_framer_.get());
2687 std::unique_ptr<spdy::SpdySerializedFrame> window_update_frame(
2688 buffered_spdy_framer_->CreateWindowUpdate(stream_id, delta_window_size));
2689 EnqueueSessionWrite(priority, spdy::SpdyFrameType::WINDOW_UPDATE,
2690 std::move(window_update_frame));
2691 }
2692
WritePingFrame(spdy::SpdyPingId unique_id,bool is_ack)2693 void SpdySession::WritePingFrame(spdy::SpdyPingId unique_id, bool is_ack) {
2694 DCHECK(buffered_spdy_framer_.get());
2695 std::unique_ptr<spdy::SpdySerializedFrame> ping_frame(
2696 buffered_spdy_framer_->CreatePingFrame(unique_id, is_ack));
2697 EnqueueSessionWrite(HIGHEST, spdy::SpdyFrameType::PING,
2698 std::move(ping_frame));
2699
2700 if (net_log().IsCapturing()) {
2701 net_log().AddEvent(NetLogEventType::HTTP2_SESSION_PING, [&] {
2702 return NetLogSpdyPingParams(unique_id, is_ack, "sent");
2703 });
2704 }
2705 if (!is_ack) {
2706 DCHECK(!ping_in_flight_);
2707
2708 ping_in_flight_ = true;
2709 ++next_ping_id_;
2710 PlanToCheckPingStatus();
2711 last_ping_sent_time_ = time_func_();
2712 }
2713 }
2714
PlanToCheckPingStatus()2715 void SpdySession::PlanToCheckPingStatus() {
2716 if (check_ping_status_pending_)
2717 return;
2718
2719 check_ping_status_pending_ = true;
2720 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
2721 FROM_HERE,
2722 base::BindOnce(&SpdySession::CheckPingStatus, weak_factory_.GetWeakPtr(),
2723 time_func_()),
2724 hung_interval_);
2725 }
2726
CheckPingStatus(base::TimeTicks last_check_time)2727 void SpdySession::CheckPingStatus(base::TimeTicks last_check_time) {
2728 CHECK(!in_io_loop_);
2729 DCHECK(check_ping_status_pending_);
2730
2731 if (!ping_in_flight_) {
2732 // A response has been received for the ping we had sent.
2733 check_ping_status_pending_ = false;
2734 return;
2735 }
2736
2737 const base::TimeTicks now = time_func_();
2738 if (now > last_read_time_ + hung_interval_ ||
2739 last_read_time_ < last_check_time) {
2740 check_ping_status_pending_ = false;
2741 DoDrainSession(ERR_HTTP2_PING_FAILED, "Failed ping.");
2742 return;
2743 }
2744
2745 // Check the status of connection after a delay.
2746 const base::TimeDelta delay = last_read_time_ + hung_interval_ - now;
2747 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
2748 FROM_HERE,
2749 base::BindOnce(&SpdySession::CheckPingStatus, weak_factory_.GetWeakPtr(),
2750 now),
2751 delay);
2752 }
2753
GetNewStreamId()2754 spdy::SpdyStreamId SpdySession::GetNewStreamId() {
2755 CHECK_LE(stream_hi_water_mark_, kLastStreamId);
2756 spdy::SpdyStreamId id = stream_hi_water_mark_;
2757 stream_hi_water_mark_ += 2;
2758 return id;
2759 }
2760
EnqueueSessionWrite(RequestPriority priority,spdy::SpdyFrameType frame_type,std::unique_ptr<spdy::SpdySerializedFrame> frame)2761 void SpdySession::EnqueueSessionWrite(
2762 RequestPriority priority,
2763 spdy::SpdyFrameType frame_type,
2764 std::unique_ptr<spdy::SpdySerializedFrame> frame) {
2765 DCHECK(frame_type == spdy::SpdyFrameType::RST_STREAM ||
2766 frame_type == spdy::SpdyFrameType::SETTINGS ||
2767 frame_type == spdy::SpdyFrameType::WINDOW_UPDATE ||
2768 frame_type == spdy::SpdyFrameType::PING ||
2769 frame_type == spdy::SpdyFrameType::GOAWAY);
2770 DCHECK(IsSpdyFrameTypeWriteCapped(frame_type));
2771 if (write_queue_.num_queued_capped_frames() >
2772 session_max_queued_capped_frames_) {
2773 LOG(WARNING)
2774 << "Draining session due to exceeding max queued capped frames";
2775 // Use ERR_CONNECTION_CLOSED to avoid sending a GOAWAY frame since that
2776 // frame would also exceed the cap.
2777 DoDrainSession(ERR_CONNECTION_CLOSED, "Exceeded max queued capped frames");
2778 return;
2779 }
2780 auto buffer = std::make_unique<SpdyBuffer>(std::move(frame));
2781 EnqueueWrite(priority, frame_type,
2782 std::make_unique<SimpleBufferProducer>(std::move(buffer)),
2783 base::WeakPtr<SpdyStream>(),
2784 kSpdySessionCommandsTrafficAnnotation);
2785 if (greased_http2_frame_ && frame_type == spdy::SpdyFrameType::SETTINGS) {
2786 EnqueueWrite(
2787 priority,
2788 static_cast<spdy::SpdyFrameType>(greased_http2_frame_.value().type),
2789 std::make_unique<GreasedBufferProducer>(base::WeakPtr<SpdyStream>(),
2790 &greased_http2_frame_.value(),
2791 buffered_spdy_framer_.get()),
2792 base::WeakPtr<SpdyStream>(), kSpdySessionCommandsTrafficAnnotation);
2793 }
2794 }
2795
EnqueueWrite(RequestPriority priority,spdy::SpdyFrameType frame_type,std::unique_ptr<SpdyBufferProducer> producer,const base::WeakPtr<SpdyStream> & stream,const NetworkTrafficAnnotationTag & traffic_annotation)2796 void SpdySession::EnqueueWrite(
2797 RequestPriority priority,
2798 spdy::SpdyFrameType frame_type,
2799 std::unique_ptr<SpdyBufferProducer> producer,
2800 const base::WeakPtr<SpdyStream>& stream,
2801 const NetworkTrafficAnnotationTag& traffic_annotation) {
2802 if (availability_state_ == STATE_DRAINING)
2803 return;
2804
2805 write_queue_.Enqueue(priority, frame_type, std::move(producer), stream,
2806 traffic_annotation);
2807 MaybePostWriteLoop();
2808 }
2809
InsertCreatedStream(std::unique_ptr<SpdyStream> stream)2810 void SpdySession::InsertCreatedStream(std::unique_ptr<SpdyStream> stream) {
2811 CHECK_EQ(stream->stream_id(), 0u);
2812 auto it = created_streams_.lower_bound(stream.get());
2813 CHECK(it == created_streams_.end() || *it != stream.get());
2814 created_streams_.insert(it, stream.release());
2815 }
2816
ActivateCreatedStream(SpdyStream * stream)2817 std::unique_ptr<SpdyStream> SpdySession::ActivateCreatedStream(
2818 SpdyStream* stream) {
2819 CHECK_EQ(stream->stream_id(), 0u);
2820 auto it = created_streams_.find(stream);
2821 CHECK(it != created_streams_.end());
2822 stream->set_stream_id(GetNewStreamId());
2823 std::unique_ptr<SpdyStream> owned_stream(stream);
2824 created_streams_.erase(it);
2825 return owned_stream;
2826 }
2827
InsertActivatedStream(std::unique_ptr<SpdyStream> stream)2828 void SpdySession::InsertActivatedStream(std::unique_ptr<SpdyStream> stream) {
2829 spdy::SpdyStreamId stream_id = stream->stream_id();
2830 CHECK_NE(stream_id, 0u);
2831 std::pair<ActiveStreamMap::iterator, bool> result =
2832 active_streams_.insert(std::make_pair(stream_id, stream.get()));
2833 CHECK(result.second);
2834 ignore_result(stream.release());
2835 }
2836
DeleteStream(std::unique_ptr<SpdyStream> stream,int status)2837 void SpdySession::DeleteStream(std::unique_ptr<SpdyStream> stream, int status) {
2838 if (in_flight_write_stream_.get() == stream.get()) {
2839 // If we're deleting the stream for the in-flight write, we still
2840 // need to let the write complete, so we clear
2841 // |in_flight_write_stream_| and let the write finish on its own
2842 // without notifying |in_flight_write_stream_|.
2843 in_flight_write_stream_.reset();
2844 }
2845
2846 write_queue_.RemovePendingWritesForStream(stream.get());
2847 stream->OnClose(status);
2848
2849 if (availability_state_ == STATE_AVAILABLE) {
2850 ProcessPendingStreamRequests();
2851 }
2852 }
2853
RecordHistograms()2854 void SpdySession::RecordHistograms() {
2855 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamsPerSession",
2856 streams_initiated_count_, 1, 300, 50);
2857 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamsPushedPerSession",
2858 streams_pushed_count_, 1, 300, 50);
2859 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamsPushedAndClaimedPerSession",
2860 streams_pushed_and_claimed_count_, 1, 300, 50);
2861 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamsAbandonedPerSession",
2862 streams_abandoned_count_, 1, 300, 50);
2863 UMA_HISTOGRAM_COUNTS_1M("Net.SpdySession.PushedBytes", bytes_pushed_count_);
2864 DCHECK_LE(bytes_pushed_and_unclaimed_count_, bytes_pushed_count_);
2865 UMA_HISTOGRAM_COUNTS_1M("Net.SpdySession.PushedAndUnclaimedBytes",
2866 bytes_pushed_and_unclaimed_count_);
2867 UMA_HISTOGRAM_BOOLEAN("Net.SpdySession.ServerSupportsWebSocket",
2868 support_websocket_);
2869 }
2870
RecordProtocolErrorHistogram(SpdyProtocolErrorDetails details)2871 void SpdySession::RecordProtocolErrorHistogram(
2872 SpdyProtocolErrorDetails details) {
2873 UMA_HISTOGRAM_ENUMERATION("Net.SpdySessionErrorDetails2", details,
2874 NUM_SPDY_PROTOCOL_ERROR_DETAILS);
2875 if (base::EndsWith(host_port_pair().host(), "google.com",
2876 base::CompareCase::INSENSITIVE_ASCII)) {
2877 UMA_HISTOGRAM_ENUMERATION("Net.SpdySessionErrorDetails_Google2", details,
2878 NUM_SPDY_PROTOCOL_ERROR_DETAILS);
2879 }
2880 }
2881
2882 // static
RecordPushedStreamVaryResponseHeaderHistogram(const spdy::SpdyHeaderBlock & headers)2883 void SpdySession::RecordPushedStreamVaryResponseHeaderHistogram(
2884 const spdy::SpdyHeaderBlock& headers) {
2885 UMA_HISTOGRAM_ENUMERATION("Net.PushedStreamVaryResponseHeader",
2886 ParseVaryInPushedResponse(headers),
2887 kNumberOfVaryEntries);
2888 }
2889
DcheckGoingAway() const2890 void SpdySession::DcheckGoingAway() const {
2891 #if DCHECK_IS_ON()
2892 DCHECK_GE(availability_state_, STATE_GOING_AWAY);
2893 for (int i = MINIMUM_PRIORITY; i <= MAXIMUM_PRIORITY; ++i) {
2894 DCHECK(pending_create_stream_queues_[i].empty());
2895 }
2896 DCHECK(created_streams_.empty());
2897 #endif
2898 }
2899
DcheckDraining() const2900 void SpdySession::DcheckDraining() const {
2901 DcheckGoingAway();
2902 DCHECK_EQ(availability_state_, STATE_DRAINING);
2903 DCHECK(active_streams_.empty());
2904 DCHECK_EQ(0u, pool_->push_promise_index()->CountStreamsForSession(this));
2905 }
2906
DoDrainSession(Error err,const std::string & description)2907 void SpdySession::DoDrainSession(Error err, const std::string& description) {
2908 if (availability_state_ == STATE_DRAINING) {
2909 return;
2910 }
2911 MakeUnavailable();
2912
2913 // Notify any requests waiting for handshake confirmation that there is an
2914 // error.
2915 NotifyRequestsOfConfirmation(err);
2916
2917 // Mark host_port_pair requiring HTTP/1.1 for subsequent connections.
2918 if (err == ERR_HTTP_1_1_REQUIRED) {
2919 http_server_properties_->SetHTTP11Required(
2920 url::SchemeHostPort(url::kHttpsScheme, host_port_pair().host(),
2921 host_port_pair().port()),
2922 spdy_session_key_.network_isolation_key());
2923 }
2924
2925 // If |err| indicates an error occurred, inform the peer that we're closing
2926 // and why. Don't GOAWAY on a graceful or idle close, as that may
2927 // unnecessarily wake the radio. We could technically GOAWAY on network errors
2928 // (we'll probably fail to actually write it, but that's okay), however many
2929 // unit-tests would need to be updated.
2930 if (err != OK &&
2931 err != ERR_ABORTED && // Used by SpdySessionPool to close idle sessions.
2932 err != ERR_NETWORK_CHANGED && // Used to deprecate sessions on IP change.
2933 err != ERR_SOCKET_NOT_CONNECTED && err != ERR_HTTP_1_1_REQUIRED &&
2934 err != ERR_CONNECTION_CLOSED && err != ERR_CONNECTION_RESET) {
2935 // Enqueue a GOAWAY to inform the peer of why we're closing the connection.
2936 spdy::SpdyGoAwayIR goaway_ir(last_accepted_push_stream_id_,
2937 MapNetErrorToGoAwayStatus(err), description);
2938 auto frame = std::make_unique<spdy::SpdySerializedFrame>(
2939 buffered_spdy_framer_->SerializeFrame(goaway_ir));
2940 EnqueueSessionWrite(HIGHEST, spdy::SpdyFrameType::GOAWAY, std::move(frame));
2941 }
2942
2943 availability_state_ = STATE_DRAINING;
2944 error_on_close_ = err;
2945
2946 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_CLOSE, [&] {
2947 return NetLogSpdySessionCloseParams(err, description);
2948 });
2949
2950 base::UmaHistogramSparse("Net.SpdySession.ClosedOnError", -err);
2951
2952 if (err == OK) {
2953 // We ought to be going away already, as this is a graceful close.
2954 DcheckGoingAway();
2955 } else {
2956 StartGoingAway(0, err);
2957 }
2958 DcheckDraining();
2959 MaybePostWriteLoop();
2960 }
2961
LogAbandonedStream(SpdyStream * stream,Error status)2962 void SpdySession::LogAbandonedStream(SpdyStream* stream, Error status) {
2963 DCHECK(stream);
2964 stream->LogStreamError(status, "Abandoned.");
2965 // We don't increment the streams abandoned counter here. If the
2966 // stream isn't active (i.e., it hasn't written anything to the wire
2967 // yet) then it's as if it never existed. If it is active, then
2968 // LogAbandonedActiveStream() will increment the counters.
2969 }
2970
LogAbandonedActiveStream(ActiveStreamMap::const_iterator it,Error status)2971 void SpdySession::LogAbandonedActiveStream(ActiveStreamMap::const_iterator it,
2972 Error status) {
2973 DCHECK_GT(it->first, 0u);
2974 LogAbandonedStream(it->second, status);
2975 ++streams_abandoned_count_;
2976 }
2977
CompleteStreamRequest(const base::WeakPtr<SpdyStreamRequest> & pending_request)2978 void SpdySession::CompleteStreamRequest(
2979 const base::WeakPtr<SpdyStreamRequest>& pending_request) {
2980 // Abort if the request has already been cancelled.
2981 if (!pending_request)
2982 return;
2983
2984 base::WeakPtr<SpdyStream> stream;
2985 int rv = TryCreateStream(pending_request, &stream);
2986
2987 if (rv == OK) {
2988 DCHECK(stream);
2989 pending_request->OnRequestCompleteSuccess(stream);
2990 return;
2991 }
2992 DCHECK(!stream);
2993
2994 if (rv != ERR_IO_PENDING) {
2995 pending_request->OnRequestCompleteFailure(rv);
2996 }
2997 }
2998
CancelPushedStreamIfUnclaimed(spdy::SpdyStreamId stream_id)2999 void SpdySession::CancelPushedStreamIfUnclaimed(spdy::SpdyStreamId stream_id) {
3000 auto active_it = active_streams_.find(stream_id);
3001 if (active_it == active_streams_.end())
3002 return;
3003
3004 // Make sure to cancel the correct stream. It is possible that the pushed
3005 // stream |stream_id| is already claimed, and another stream has been pushed
3006 // for the same URL.
3007 const GURL& url = active_it->second->url();
3008 if (pool_->push_promise_index()->FindStream(url, this) != stream_id) {
3009 return;
3010 }
3011
3012 RecordSpdyPushedStreamFateHistogram(SpdyPushedStreamFate::kTimeout);
3013
3014 LogAbandonedActiveStream(active_it, ERR_TIMED_OUT);
3015 // CloseActiveStreamIterator() will remove the stream from
3016 // |pool_->push_promise_index()|.
3017 ResetStreamIterator(active_it, ERR_TIMED_OUT, "Stream not claimed.");
3018 }
3019
OnError(http2::Http2DecoderAdapter::SpdyFramerError spdy_framer_error)3020 void SpdySession::OnError(
3021 http2::Http2DecoderAdapter::SpdyFramerError spdy_framer_error) {
3022 CHECK(in_io_loop_);
3023
3024 RecordProtocolErrorHistogram(
3025 MapFramerErrorToProtocolError(spdy_framer_error));
3026 std::string description = base::StringPrintf(
3027 "Framer error: %d (%s).", spdy_framer_error,
3028 http2::Http2DecoderAdapter::SpdyFramerErrorToString(spdy_framer_error));
3029 DoDrainSession(MapFramerErrorToNetError(spdy_framer_error), description);
3030 }
3031
OnStreamError(spdy::SpdyStreamId stream_id,const std::string & description)3032 void SpdySession::OnStreamError(spdy::SpdyStreamId stream_id,
3033 const std::string& description) {
3034 CHECK(in_io_loop_);
3035
3036 auto it = active_streams_.find(stream_id);
3037 if (it == active_streams_.end()) {
3038 // We still want to send a frame to reset the stream even if we
3039 // don't know anything about it.
3040 EnqueueResetStreamFrame(stream_id, IDLE, spdy::ERROR_CODE_PROTOCOL_ERROR,
3041 description);
3042 return;
3043 }
3044
3045 ResetStreamIterator(it, ERR_HTTP2_PROTOCOL_ERROR, description);
3046 }
3047
OnPing(spdy::SpdyPingId unique_id,bool is_ack)3048 void SpdySession::OnPing(spdy::SpdyPingId unique_id, bool is_ack) {
3049 CHECK(in_io_loop_);
3050
3051 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_PING, [&] {
3052 return NetLogSpdyPingParams(unique_id, is_ack, "received");
3053 });
3054
3055 // Send response to a PING from server.
3056 if (!is_ack) {
3057 WritePingFrame(unique_id, true);
3058 return;
3059 }
3060
3061 if (!ping_in_flight_) {
3062 RecordProtocolErrorHistogram(PROTOCOL_ERROR_UNEXPECTED_PING);
3063 DoDrainSession(ERR_HTTP2_PROTOCOL_ERROR, "Unexpected PING ACK.");
3064 return;
3065 }
3066
3067 ping_in_flight_ = false;
3068
3069 // Record RTT in histogram when there are no more pings in flight.
3070 base::TimeDelta ping_duration = time_func_() - last_ping_sent_time_;
3071 if (network_quality_estimator_) {
3072 network_quality_estimator_->RecordSpdyPingLatency(host_port_pair(),
3073 ping_duration);
3074 }
3075 }
3076
OnRstStream(spdy::SpdyStreamId stream_id,spdy::SpdyErrorCode error_code)3077 void SpdySession::OnRstStream(spdy::SpdyStreamId stream_id,
3078 spdy::SpdyErrorCode error_code) {
3079 CHECK(in_io_loop_);
3080
3081 net_log().AddEvent(NetLogEventType::HTTP2_SESSION_RECV_RST_STREAM, [&] {
3082 return NetLogSpdyRecvRstStreamParams(stream_id, error_code);
3083 });
3084
3085 auto it = active_streams_.find(stream_id);
3086 if (it == active_streams_.end()) {
3087 // NOTE: it may just be that the stream was cancelled.
3088 LOG(WARNING) << "Received RST for invalid stream" << stream_id;
3089 return;
3090 }
3091
3092 DCHECK(it->second);
3093 CHECK_EQ(it->second->stream_id(), stream_id);
3094
3095 if (it->second->ShouldRetryRSTPushStream()) {
3096 CloseActiveStreamIterator(it,
3097 ERR_HTTP2_CLAIMED_PUSHED_STREAM_RESET_BY_SERVER);
3098 } else if (error_code == spdy::ERROR_CODE_NO_ERROR) {
3099 CloseActiveStreamIterator(it, ERR_HTTP2_RST_STREAM_NO_ERROR_RECEIVED);
3100 } else if (error_code == spdy::ERROR_CODE_REFUSED_STREAM) {
3101 CloseActiveStreamIterator(it, ERR_HTTP2_SERVER_REFUSED_STREAM);
3102 } else if (error_code == spdy::ERROR_CODE_HTTP_1_1_REQUIRED) {
3103 // TODO(bnc): Record histogram with number of open streams capped at 50.
3104 if (net_log().IsCapturing()) {
3105 it->second->LogStreamError(ERR_HTTP_1_1_REQUIRED,
3106 "Closing session because server reset stream "
3107 "with ERR_HTTP_1_1_REQUIRED.");
3108 }
3109 DoDrainSession(ERR_HTTP_1_1_REQUIRED, "HTTP_1_1_REQUIRED for stream.");
3110 } else {
3111 RecordProtocolErrorHistogram(
3112 PROTOCOL_ERROR_RST_STREAM_FOR_NON_ACTIVE_STREAM);
3113 if (net_log().IsCapturing()) {
3114 it->second->LogStreamError(ERR_HTTP2_PROTOCOL_ERROR,
3115 "Server reset stream.");
3116 }
3117 // TODO(mbelshe): Map from Spdy-protocol errors to something sensical.
3118 // For now, it doesn't matter much - it is a protocol error.
3119 CloseActiveStreamIterator(it, ERR_HTTP2_PROTOCOL_ERROR);
3120 }
3121 }
3122
OnGoAway(spdy::SpdyStreamId last_accepted_stream_id,spdy::SpdyErrorCode error_code,base::StringPiece debug_data)3123 void SpdySession::OnGoAway(spdy::SpdyStreamId last_accepted_stream_id,
3124 spdy::SpdyErrorCode error_code,
3125 base::StringPiece debug_data) {
3126 CHECK(in_io_loop_);
3127
3128 // TODO(jgraettinger): UMA histogram on |error_code|.
3129
3130 net_log_.AddEvent(
3131 NetLogEventType::HTTP2_SESSION_RECV_GOAWAY,
3132 [&](NetLogCaptureMode capture_mode) {
3133 return NetLogSpdyRecvGoAwayParams(
3134 last_accepted_stream_id, active_streams_.size(),
3135 pool_->push_promise_index()->CountStreamsForSession(this),
3136 error_code, debug_data, capture_mode);
3137 });
3138 MakeUnavailable();
3139 if (error_code == spdy::ERROR_CODE_HTTP_1_1_REQUIRED) {
3140 // TODO(bnc): Record histogram with number of open streams capped at 50.
3141 DoDrainSession(ERR_HTTP_1_1_REQUIRED, "HTTP_1_1_REQUIRED for stream.");
3142 } else if (error_code == spdy::ERROR_CODE_NO_ERROR) {
3143 StartGoingAway(last_accepted_stream_id, ERR_HTTP2_SERVER_REFUSED_STREAM);
3144 } else {
3145 StartGoingAway(last_accepted_stream_id, ERR_ABORTED);
3146 }
3147 // This is to handle the case when we already don't have any active
3148 // streams (i.e., StartGoingAway() did nothing). Otherwise, we have
3149 // active streams and so the last one being closed will finish the
3150 // going away process (see DeleteStream()).
3151 MaybeFinishGoingAway();
3152 }
3153
OnDataFrameHeader(spdy::SpdyStreamId stream_id,size_t length,bool fin)3154 void SpdySession::OnDataFrameHeader(spdy::SpdyStreamId stream_id,
3155 size_t length,
3156 bool fin) {
3157 CHECK(in_io_loop_);
3158
3159 auto it = active_streams_.find(stream_id);
3160
3161 // By the time data comes in, the stream may already be inactive.
3162 if (it == active_streams_.end())
3163 return;
3164
3165 SpdyStream* stream = it->second;
3166 CHECK_EQ(stream->stream_id(), stream_id);
3167
3168 DCHECK(buffered_spdy_framer_);
3169 stream->AddRawReceivedBytes(spdy::kDataFrameMinimumSize);
3170 }
3171
OnStreamFrameData(spdy::SpdyStreamId stream_id,const char * data,size_t len)3172 void SpdySession::OnStreamFrameData(spdy::SpdyStreamId stream_id,
3173 const char* data,
3174 size_t len) {
3175 CHECK(in_io_loop_);
3176 DCHECK_LT(len, 1u << 24);
3177 if (net_log().IsCapturing()) {
3178 net_log().AddEvent(NetLogEventType::HTTP2_SESSION_RECV_DATA, [&] {
3179 return NetLogSpdyDataParams(stream_id, len, false);
3180 });
3181 }
3182
3183 // Build the buffer as early as possible so that we go through the
3184 // session flow control checks and update
3185 // |unacked_recv_window_bytes_| properly even when the stream is
3186 // inactive (since the other side has still reduced its session send
3187 // window).
3188 std::unique_ptr<SpdyBuffer> buffer;
3189 if (data) {
3190 DCHECK_GT(len, 0u);
3191 CHECK_LE(len, static_cast<size_t>(kReadBufferSize));
3192 buffer = std::make_unique<SpdyBuffer>(data, len);
3193
3194 DecreaseRecvWindowSize(static_cast<int32_t>(len));
3195 buffer->AddConsumeCallback(base::BindRepeating(
3196 &SpdySession::OnReadBufferConsumed, weak_factory_.GetWeakPtr()));
3197 } else {
3198 DCHECK_EQ(len, 0u);
3199 }
3200
3201 auto it = active_streams_.find(stream_id);
3202
3203 // By the time data comes in, the stream may already be inactive.
3204 if (it == active_streams_.end())
3205 return;
3206
3207 SpdyStream* stream = it->second;
3208 CHECK_EQ(stream->stream_id(), stream_id);
3209
3210 stream->AddRawReceivedBytes(len);
3211 stream->OnDataReceived(std::move(buffer));
3212 }
3213
OnStreamEnd(spdy::SpdyStreamId stream_id)3214 void SpdySession::OnStreamEnd(spdy::SpdyStreamId stream_id) {
3215 CHECK(in_io_loop_);
3216 if (net_log().IsCapturing()) {
3217 net_log().AddEvent(NetLogEventType::HTTP2_SESSION_RECV_DATA, [&] {
3218 return NetLogSpdyDataParams(stream_id, 0, true);
3219 });
3220 }
3221
3222 auto it = active_streams_.find(stream_id);
3223 // By the time data comes in, the stream may already be inactive.
3224 if (it == active_streams_.end())
3225 return;
3226
3227 SpdyStream* stream = it->second;
3228 CHECK_EQ(stream->stream_id(), stream_id);
3229
3230 stream->OnDataReceived(std::unique_ptr<SpdyBuffer>());
3231 }
3232
OnStreamPadding(spdy::SpdyStreamId stream_id,size_t len)3233 void SpdySession::OnStreamPadding(spdy::SpdyStreamId stream_id, size_t len) {
3234 CHECK(in_io_loop_);
3235
3236 // Decrease window size because padding bytes are received.
3237 // Increase window size because padding bytes are consumed (by discarding).
3238 // Net result: |session_unacked_recv_window_bytes_| increases by |len|,
3239 // |session_recv_window_size_| does not change.
3240 DecreaseRecvWindowSize(static_cast<int32_t>(len));
3241 IncreaseRecvWindowSize(static_cast<int32_t>(len));
3242
3243 auto it = active_streams_.find(stream_id);
3244 if (it == active_streams_.end())
3245 return;
3246 it->second->OnPaddingConsumed(len);
3247 }
3248
OnSettings()3249 void SpdySession::OnSettings() {
3250 CHECK(in_io_loop_);
3251
3252 if (net_log_.IsCapturing()) {
3253 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_RECV_SETTINGS);
3254 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_SEND_SETTINGS_ACK);
3255 }
3256
3257 // Send an acknowledgment of the setting.
3258 spdy::SpdySettingsIR settings_ir;
3259 settings_ir.set_is_ack(true);
3260 auto frame = std::make_unique<spdy::SpdySerializedFrame>(
3261 buffered_spdy_framer_->SerializeFrame(settings_ir));
3262 EnqueueSessionWrite(HIGHEST, spdy::SpdyFrameType::SETTINGS, std::move(frame));
3263 }
3264
OnSettingsAck()3265 void SpdySession::OnSettingsAck() {
3266 CHECK(in_io_loop_);
3267
3268 if (net_log_.IsCapturing())
3269 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_RECV_SETTINGS_ACK);
3270 }
3271
OnSetting(spdy::SpdySettingsId id,uint32_t value)3272 void SpdySession::OnSetting(spdy::SpdySettingsId id, uint32_t value) {
3273 CHECK(in_io_loop_);
3274
3275 HandleSetting(id, value);
3276
3277 // Log the setting.
3278 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_RECV_SETTING,
3279 [&] { return NetLogSpdyRecvSettingParams(id, value); });
3280 }
3281
OnWindowUpdate(spdy::SpdyStreamId stream_id,int delta_window_size)3282 void SpdySession::OnWindowUpdate(spdy::SpdyStreamId stream_id,
3283 int delta_window_size) {
3284 CHECK(in_io_loop_);
3285
3286 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_RECV_WINDOW_UPDATE, [&] {
3287 return NetLogSpdyWindowUpdateFrameParams(stream_id, delta_window_size);
3288 });
3289
3290 if (stream_id == spdy::kSessionFlowControlStreamId) {
3291 // WINDOW_UPDATE for the session.
3292 if (delta_window_size < 1) {
3293 RecordProtocolErrorHistogram(PROTOCOL_ERROR_INVALID_WINDOW_UPDATE_SIZE);
3294 DoDrainSession(
3295 ERR_HTTP2_PROTOCOL_ERROR,
3296 "Received WINDOW_UPDATE with an invalid delta_window_size " +
3297 base::NumberToString(delta_window_size));
3298 return;
3299 }
3300
3301 IncreaseSendWindowSize(delta_window_size);
3302 } else {
3303 // WINDOW_UPDATE for a stream.
3304 auto it = active_streams_.find(stream_id);
3305
3306 if (it == active_streams_.end()) {
3307 // NOTE: it may just be that the stream was cancelled.
3308 LOG(WARNING) << "Received WINDOW_UPDATE for invalid stream " << stream_id;
3309 return;
3310 }
3311
3312 SpdyStream* stream = it->second;
3313 CHECK_EQ(stream->stream_id(), stream_id);
3314
3315 if (delta_window_size < 1) {
3316 ResetStreamIterator(
3317 it, ERR_HTTP2_FLOW_CONTROL_ERROR,
3318 "Received WINDOW_UPDATE with an invalid delta_window_size.");
3319 return;
3320 }
3321
3322 CHECK_EQ(it->second->stream_id(), stream_id);
3323 it->second->IncreaseSendWindowSize(delta_window_size);
3324 }
3325 }
3326
OnPushPromise(spdy::SpdyStreamId stream_id,spdy::SpdyStreamId promised_stream_id,spdy::SpdyHeaderBlock headers)3327 void SpdySession::OnPushPromise(spdy::SpdyStreamId stream_id,
3328 spdy::SpdyStreamId promised_stream_id,
3329 spdy::SpdyHeaderBlock headers) {
3330 CHECK(in_io_loop_);
3331
3332 if (net_log_.IsCapturing()) {
3333 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_RECV_PUSH_PROMISE,
3334 [&](NetLogCaptureMode capture_mode) {
3335 return NetLogSpdyPushPromiseReceivedParams(
3336 &headers, stream_id, promised_stream_id,
3337 capture_mode);
3338 });
3339 }
3340
3341 TryCreatePushStream(promised_stream_id, stream_id, std::move(headers));
3342 }
3343
OnHeaders(spdy::SpdyStreamId stream_id,bool has_priority,int weight,spdy::SpdyStreamId parent_stream_id,bool exclusive,bool fin,spdy::SpdyHeaderBlock headers,base::TimeTicks recv_first_byte_time)3344 void SpdySession::OnHeaders(spdy::SpdyStreamId stream_id,
3345 bool has_priority,
3346 int weight,
3347 spdy::SpdyStreamId parent_stream_id,
3348 bool exclusive,
3349 bool fin,
3350 spdy::SpdyHeaderBlock headers,
3351 base::TimeTicks recv_first_byte_time) {
3352 CHECK(in_io_loop_);
3353
3354 if (net_log().IsCapturing()) {
3355 net_log().AddEvent(NetLogEventType::HTTP2_SESSION_RECV_HEADERS,
3356 [&](NetLogCaptureMode capture_mode) {
3357 return NetLogSpdyHeadersReceivedParams(
3358 &headers, fin, stream_id, capture_mode);
3359 });
3360 }
3361
3362 auto it = active_streams_.find(stream_id);
3363 if (it == active_streams_.end()) {
3364 // NOTE: it may just be that the stream was cancelled.
3365 LOG(WARNING) << "Received HEADERS for invalid stream " << stream_id;
3366 return;
3367 }
3368
3369 SpdyStream* stream = it->second;
3370 CHECK_EQ(stream->stream_id(), stream_id);
3371
3372 if (stream->type() == SPDY_PUSH_STREAM)
3373 RecordPushedStreamVaryResponseHeaderHistogram(headers);
3374
3375 stream->AddRawReceivedBytes(last_compressed_frame_len_);
3376 last_compressed_frame_len_ = 0;
3377
3378 if (it->second->IsReservedRemote()) {
3379 DCHECK_EQ(SPDY_PUSH_STREAM, stream->type());
3380 if (max_concurrent_pushed_streams_ &&
3381 num_active_pushed_streams_ >= max_concurrent_pushed_streams_) {
3382 RecordSpdyPushedStreamFateHistogram(
3383 SpdyPushedStreamFate::kTooManyPushedStreams);
3384 ResetStream(stream_id, ERR_HTTP2_CLIENT_REFUSED_STREAM,
3385 "Stream concurrency limit reached.");
3386 return;
3387 }
3388
3389 // Will be balanced in DeleteStream.
3390 num_active_pushed_streams_++;
3391 }
3392
3393 base::Time response_time = base::Time::Now();
3394 // May invalidate |stream|.
3395 stream->OnHeadersReceived(headers, response_time, recv_first_byte_time);
3396 }
3397
OnAltSvc(spdy::SpdyStreamId stream_id,base::StringPiece origin,const spdy::SpdyAltSvcWireFormat::AlternativeServiceVector & altsvc_vector)3398 void SpdySession::OnAltSvc(
3399 spdy::SpdyStreamId stream_id,
3400 base::StringPiece origin,
3401 const spdy::SpdyAltSvcWireFormat::AlternativeServiceVector& altsvc_vector) {
3402 url::SchemeHostPort scheme_host_port;
3403 if (stream_id == 0) {
3404 if (origin.empty())
3405 return;
3406 const GURL gurl(origin);
3407 if (!gurl.is_valid() || gurl.host().empty())
3408 return;
3409 if (!gurl.SchemeIs(url::kHttpsScheme))
3410 return;
3411 SSLInfo ssl_info;
3412 if (!GetSSLInfo(&ssl_info))
3413 return;
3414 if (!CanPool(transport_security_state_, ssl_info, *ssl_config_service_,
3415 host_port_pair().host(), gurl.host())) {
3416 return;
3417 }
3418 scheme_host_port = url::SchemeHostPort(gurl);
3419 } else {
3420 if (!origin.empty())
3421 return;
3422 const ActiveStreamMap::iterator it = active_streams_.find(stream_id);
3423 if (it == active_streams_.end())
3424 return;
3425 const GURL& gurl(it->second->url());
3426 if (!gurl.SchemeIs(url::kHttpsScheme))
3427 return;
3428 scheme_host_port = url::SchemeHostPort(gurl);
3429 }
3430
3431 http_server_properties_->SetAlternativeServices(
3432 scheme_host_port, spdy_session_key_.network_isolation_key(),
3433 ProcessAlternativeServices(altsvc_vector, is_http2_enabled_,
3434 is_quic_enabled_, quic_supported_versions_));
3435 }
3436
OnUnknownFrame(spdy::SpdyStreamId stream_id,uint8_t frame_type)3437 bool SpdySession::OnUnknownFrame(spdy::SpdyStreamId stream_id,
3438 uint8_t frame_type) {
3439 // Validate stream id.
3440 // Was the frame sent on a stream id that has not been used in this session?
3441 if (stream_id % 2 == 1 && stream_id > stream_hi_water_mark_)
3442 return false;
3443
3444 if (stream_id % 2 == 0 && stream_id > last_accepted_push_stream_id_)
3445 return false;
3446
3447 return true;
3448 }
3449
OnSendCompressedFrame(spdy::SpdyStreamId stream_id,spdy::SpdyFrameType type,size_t payload_len,size_t frame_len)3450 void SpdySession::OnSendCompressedFrame(spdy::SpdyStreamId stream_id,
3451 spdy::SpdyFrameType type,
3452 size_t payload_len,
3453 size_t frame_len) {
3454 if (type != spdy::SpdyFrameType::HEADERS) {
3455 return;
3456 }
3457
3458 DCHECK(buffered_spdy_framer_.get());
3459 size_t compressed_len = frame_len - spdy::kFrameMinimumSize;
3460
3461 if (payload_len) {
3462 // Make sure we avoid early decimal truncation.
3463 int compression_pct = 100 - (100 * compressed_len) / payload_len;
3464 UMA_HISTOGRAM_PERCENTAGE("Net.SpdyHeadersCompressionPercentage",
3465 compression_pct);
3466 }
3467 }
3468
OnReceiveCompressedFrame(spdy::SpdyStreamId stream_id,spdy::SpdyFrameType type,size_t frame_len)3469 void SpdySession::OnReceiveCompressedFrame(spdy::SpdyStreamId stream_id,
3470 spdy::SpdyFrameType type,
3471 size_t frame_len) {
3472 last_compressed_frame_len_ = frame_len;
3473 }
3474
OnWriteBufferConsumed(size_t frame_payload_size,size_t consume_size,SpdyBuffer::ConsumeSource consume_source)3475 void SpdySession::OnWriteBufferConsumed(
3476 size_t frame_payload_size,
3477 size_t consume_size,
3478 SpdyBuffer::ConsumeSource consume_source) {
3479 // We can be called with |in_io_loop_| set if a write SpdyBuffer is
3480 // deleted (e.g., a stream is closed due to incoming data).
3481 if (consume_source == SpdyBuffer::DISCARD) {
3482 // If we're discarding a frame or part of it, increase the send
3483 // window by the number of discarded bytes. (Although if we're
3484 // discarding part of a frame, it's probably because of a write
3485 // error and we'll be tearing down the session soon.)
3486 int remaining_payload_bytes = std::min(consume_size, frame_payload_size);
3487 DCHECK_GT(remaining_payload_bytes, 0);
3488 IncreaseSendWindowSize(remaining_payload_bytes);
3489 }
3490 // For consumed bytes, the send window is increased when we receive
3491 // a WINDOW_UPDATE frame.
3492 }
3493
IncreaseSendWindowSize(int delta_window_size)3494 void SpdySession::IncreaseSendWindowSize(int delta_window_size) {
3495 // We can be called with |in_io_loop_| set if a SpdyBuffer is
3496 // deleted (e.g., a stream is closed due to incoming data).
3497 DCHECK_GE(delta_window_size, 1);
3498
3499 // Check for overflow.
3500 int32_t max_delta_window_size =
3501 std::numeric_limits<int32_t>::max() - session_send_window_size_;
3502 if (delta_window_size > max_delta_window_size) {
3503 RecordProtocolErrorHistogram(PROTOCOL_ERROR_INVALID_WINDOW_UPDATE_SIZE);
3504 DoDrainSession(
3505 ERR_HTTP2_PROTOCOL_ERROR,
3506 "Received WINDOW_UPDATE [delta: " +
3507 base::NumberToString(delta_window_size) +
3508 "] for session overflows session_send_window_size_ [current: " +
3509 base::NumberToString(session_send_window_size_) + "]");
3510 return;
3511 }
3512
3513 session_send_window_size_ += delta_window_size;
3514
3515 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_UPDATE_SEND_WINDOW, [&] {
3516 return NetLogSpdySessionWindowUpdateParams(delta_window_size,
3517 session_send_window_size_);
3518 });
3519
3520 DCHECK(!IsSendStalled());
3521 ResumeSendStalledStreams();
3522 }
3523
DecreaseSendWindowSize(int32_t delta_window_size)3524 void SpdySession::DecreaseSendWindowSize(int32_t delta_window_size) {
3525 // We only call this method when sending a frame. Therefore,
3526 // |delta_window_size| should be within the valid frame size range.
3527 DCHECK_GE(delta_window_size, 1);
3528 DCHECK_LE(delta_window_size, kMaxSpdyFrameChunkSize);
3529
3530 // |send_window_size_| should have been at least |delta_window_size| for
3531 // this call to happen.
3532 DCHECK_GE(session_send_window_size_, delta_window_size);
3533
3534 session_send_window_size_ -= delta_window_size;
3535
3536 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_UPDATE_SEND_WINDOW, [&] {
3537 return NetLogSpdySessionWindowUpdateParams(-delta_window_size,
3538 session_send_window_size_);
3539 });
3540 }
3541
OnReadBufferConsumed(size_t consume_size,SpdyBuffer::ConsumeSource consume_source)3542 void SpdySession::OnReadBufferConsumed(
3543 size_t consume_size,
3544 SpdyBuffer::ConsumeSource consume_source) {
3545 // We can be called with |in_io_loop_| set if a read SpdyBuffer is
3546 // deleted (e.g., discarded by a SpdyReadQueue).
3547 DCHECK_GE(consume_size, 1u);
3548 DCHECK_LE(consume_size,
3549 static_cast<size_t>(std::numeric_limits<int32_t>::max()));
3550
3551 IncreaseRecvWindowSize(static_cast<int32_t>(consume_size));
3552 }
3553
IncreaseRecvWindowSize(int32_t delta_window_size)3554 void SpdySession::IncreaseRecvWindowSize(int32_t delta_window_size) {
3555 DCHECK_GE(session_unacked_recv_window_bytes_, 0);
3556 DCHECK_GE(session_recv_window_size_, session_unacked_recv_window_bytes_);
3557 DCHECK_GE(delta_window_size, 1);
3558 // Check for overflow.
3559 DCHECK_LE(delta_window_size,
3560 std::numeric_limits<int32_t>::max() - session_recv_window_size_);
3561
3562 session_recv_window_size_ += delta_window_size;
3563 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_UPDATE_RECV_WINDOW, [&] {
3564 return NetLogSpdySessionWindowUpdateParams(delta_window_size,
3565 session_recv_window_size_);
3566 });
3567
3568 session_unacked_recv_window_bytes_ += delta_window_size;
3569 if (session_unacked_recv_window_bytes_ > session_max_recv_window_size_ / 2) {
3570 SendWindowUpdateFrame(spdy::kSessionFlowControlStreamId,
3571 session_unacked_recv_window_bytes_, HIGHEST);
3572 session_unacked_recv_window_bytes_ = 0;
3573 }
3574 }
3575
DecreaseRecvWindowSize(int32_t delta_window_size)3576 void SpdySession::DecreaseRecvWindowSize(int32_t delta_window_size) {
3577 CHECK(in_io_loop_);
3578 DCHECK_GE(delta_window_size, 1);
3579
3580 // The receiving window size as the peer knows it is
3581 // |session_recv_window_size_ - session_unacked_recv_window_bytes_|, if more
3582 // data are sent by the peer, that means that the receive window is not being
3583 // respected.
3584 if (delta_window_size >
3585 session_recv_window_size_ - session_unacked_recv_window_bytes_) {
3586 RecordProtocolErrorHistogram(PROTOCOL_ERROR_RECEIVE_WINDOW_VIOLATION);
3587 DoDrainSession(
3588 ERR_HTTP2_FLOW_CONTROL_ERROR,
3589 "delta_window_size is " + base::NumberToString(delta_window_size) +
3590 " in DecreaseRecvWindowSize, which is larger than the receive " +
3591 "window size of " +
3592 base::NumberToString(session_recv_window_size_));
3593 return;
3594 }
3595
3596 session_recv_window_size_ -= delta_window_size;
3597 net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_UPDATE_RECV_WINDOW, [&] {
3598 return NetLogSpdySessionWindowUpdateParams(-delta_window_size,
3599 session_recv_window_size_);
3600 });
3601 }
3602
QueueSendStalledStream(const SpdyStream & stream)3603 void SpdySession::QueueSendStalledStream(const SpdyStream& stream) {
3604 DCHECK(stream.send_stalled_by_flow_control() || IsSendStalled());
3605 RequestPriority priority = stream.priority();
3606 CHECK_GE(priority, MINIMUM_PRIORITY);
3607 CHECK_LE(priority, MAXIMUM_PRIORITY);
3608 stream_send_unstall_queue_[priority].push_back(stream.stream_id());
3609 }
3610
ResumeSendStalledStreams()3611 void SpdySession::ResumeSendStalledStreams() {
3612 // We don't have to worry about new streams being queued, since
3613 // doing so would cause IsSendStalled() to return true. But we do
3614 // have to worry about streams being closed, as well as ourselves
3615 // being closed.
3616
3617 base::circular_deque<SpdyStream*> streams_to_requeue;
3618
3619 while (!IsSendStalled()) {
3620 size_t old_size = 0;
3621 #if DCHECK_IS_ON()
3622 old_size = GetTotalSize(stream_send_unstall_queue_);
3623 #endif
3624
3625 spdy::SpdyStreamId stream_id = PopStreamToPossiblyResume();
3626 if (stream_id == 0)
3627 break;
3628 ActiveStreamMap::const_iterator it = active_streams_.find(stream_id);
3629 // The stream may actually still be send-stalled after this (due
3630 // to its own send window) but that's okay -- it'll then be
3631 // resumed once its send window increases.
3632 if (it != active_streams_.end()) {
3633 if (it->second->PossiblyResumeIfSendStalled() == SpdyStream::Requeue)
3634 streams_to_requeue.push_back(it->second);
3635 }
3636
3637 // The size should decrease unless we got send-stalled again.
3638 if (!IsSendStalled())
3639 DCHECK_LT(GetTotalSize(stream_send_unstall_queue_), old_size);
3640 }
3641 while (!streams_to_requeue.empty()) {
3642 SpdyStream* stream = streams_to_requeue.front();
3643 streams_to_requeue.pop_front();
3644 QueueSendStalledStream(*stream);
3645 }
3646 }
3647
PopStreamToPossiblyResume()3648 spdy::SpdyStreamId SpdySession::PopStreamToPossiblyResume() {
3649 for (int i = MAXIMUM_PRIORITY; i >= MINIMUM_PRIORITY; --i) {
3650 base::circular_deque<spdy::SpdyStreamId>* queue =
3651 &stream_send_unstall_queue_[i];
3652 if (!queue->empty()) {
3653 spdy::SpdyStreamId stream_id = queue->front();
3654 queue->pop_front();
3655 return stream_id;
3656 }
3657 }
3658 return 0;
3659 }
3660
3661 } // namespace net
3662