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