OLD | NEW |
| (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 | |
OLD | NEW |