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