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