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