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