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