Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(198)

Side by Side Diff: net/spdy/spdy_session.cc

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

Powered by Google App Engine
This is Rietveld 408576698