forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathspdy_session.h
1315 lines (1100 loc) · 55.1 KB
/
spdy_session.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_SPDY_SPDY_SESSION_H_
#define NET_SPDY_SPDY_SESSION_H_
#include <stddef.h>
#include <stdint.h>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "base/containers/circular_deque.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string_piece.h"
#include "base/time/time.h"
#include "net/base/completion_once_callback.h"
#include "net/base/host_port_pair.h"
#include "net/base/io_buffer.h"
#include "net/base/load_states.h"
#include "net/base/load_timing_info.h"
#include "net/base/net_errors.h"
#include "net/base/net_export.h"
#include "net/base/request_priority.h"
#include "net/log/net_log_source.h"
#include "net/socket/client_socket_pool.h"
#include "net/socket/next_proto.h"
#include "net/socket/ssl_client_socket.h"
#include "net/socket/stream_socket.h"
#include "net/spdy/buffered_spdy_framer.h"
#include "net/spdy/http2_priority_dependencies.h"
#include "net/spdy/http2_push_promise_index.h"
#include "net/spdy/multiplexed_session.h"
#include "net/spdy/server_push_delegate.h"
#include "net/spdy/spdy_buffer.h"
#include "net/spdy/spdy_session_pool.h"
#include "net/spdy/spdy_stream.h"
#include "net/spdy/spdy_write_queue.h"
#include "net/ssl/ssl_config_service.h"
#include "net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.h"
#include "net/third_party/quiche/src/spdy/core/spdy_framer.h"
#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h"
#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"
#include "url/origin.h"
#include "url/scheme_host_port.h"
namespace net {
namespace test {
class SpdyStreamTest;
}
// TLS and other layers will chunk data at 16KB. Making the max frame size too
// small will lead to increased CPU/byte cost and overhead on both client/server
// due to excessive frames to process. Making this larger has diminishing
// returns as the data will be chunked elsewhere. We also want to ensure we are
// >= 2860B (~2* MSS => 2 packets) to avoid delayed ACKs. We will also account
// for the frame header size of 9B to prevent fragmentation when this is added.
// As a result we will use a 16KB - 9B max data frame size.
const int kMaxSpdyFrameChunkSize = (16 * 1024) - 9;
// Default value of spdy::SETTINGS_INITIAL_WINDOW_SIZE per protocol
// specification. A session is always created with this initial window size.
const int32_t kDefaultInitialWindowSize = 65535;
// Maximum number of concurrent streams we will create, unless the server
// sends a SETTINGS frame with a different value.
const size_t kInitialMaxConcurrentStreams = 100;
// If more than this many bytes have been read or more than that many
// milliseconds have passed, return ERR_IO_PENDING from ReadLoop.
const int kYieldAfterBytesRead = 32 * 1024;
const int kYieldAfterDurationMilliseconds = 20;
// First and last valid stream IDs. As we always act as the client,
// start at 1 for the first stream id.
const spdy::SpdyStreamId kFirstStreamId = 1;
const spdy::SpdyStreamId kLastStreamId = 0x7fffffff;
// Maximum number of capped frames that can be queued at any time.
// We measured how many queued capped frames were ever in the
// SpdyWriteQueue at one given time between 2019-08 and 2020-02.
// The numbers showed that in 99.94% of cases it would always
// stay below 10, and that it would exceed 1000 only in
// 10^-8 of cases. Therefore we picked 10000 as a number that will
// virtually never be hit in practice, while still preventing an
// attacker from growing this queue unboundedly.
const int kSpdySessionMaxQueuedCappedFrames = 10000;
class NetLog;
class NetworkQualityEstimator;
class SpdyStream;
class SSLInfo;
class TransportSecurityState;
// NOTE: There is an enum called SpdyProtocolErrorDetails2 (also with numeric
// suffixes) in tools/metrics/histograms/enums.xml. Be sure to add new values
// there also.
enum SpdyProtocolErrorDetails {
// http2::Http2DecoderAdapter::SpdyFramerError mappings.
SPDY_ERROR_NO_ERROR = 0,
SPDY_ERROR_INVALID_STREAM_ID = 38,
SPDY_ERROR_INVALID_CONTROL_FRAME = 1,
SPDY_ERROR_CONTROL_PAYLOAD_TOO_LARGE = 2,
SPDY_ERROR_ZLIB_INIT_FAILURE = 3,
SPDY_ERROR_UNSUPPORTED_VERSION = 4,
SPDY_ERROR_DECOMPRESS_FAILURE = 5,
SPDY_ERROR_COMPRESS_FAILURE = 6,
SPDY_ERROR_GOAWAY_FRAME_CORRUPT = 29,
SPDY_ERROR_RST_STREAM_FRAME_CORRUPT = 30,
SPDY_ERROR_INVALID_PADDING = 39,
SPDY_ERROR_INVALID_DATA_FRAME_FLAGS = 8,
SPDY_ERROR_INVALID_CONTROL_FRAME_FLAGS = 9,
SPDY_ERROR_UNEXPECTED_FRAME = 31,
SPDY_ERROR_INTERNAL_FRAMER_ERROR = 41,
SPDY_ERROR_INVALID_CONTROL_FRAME_SIZE = 37,
SPDY_ERROR_OVERSIZED_PAYLOAD = 40,
// HttpDecoder or HttpDecoderAdapter error.
SPDY_ERROR_HPACK_INDEX_VARINT_ERROR = 43,
SPDY_ERROR_HPACK_NAME_LENGTH_VARINT_ERROR = 44,
SPDY_ERROR_HPACK_VALUE_LENGTH_VARINT_ERROR = 45,
SPDY_ERROR_HPACK_NAME_TOO_LONG = 46,
SPDY_ERROR_HPACK_VALUE_TOO_LONG = 47,
SPDY_ERROR_HPACK_NAME_HUFFMAN_ERROR = 48,
SPDY_ERROR_HPACK_VALUE_HUFFMAN_ERROR = 49,
SPDY_ERROR_HPACK_MISSING_DYNAMIC_TABLE_SIZE_UPDATE = 50,
SPDY_ERROR_HPACK_INVALID_INDEX = 51,
SPDY_ERROR_HPACK_INVALID_NAME_INDEX = 52,
SPDY_ERROR_HPACK_DYNAMIC_TABLE_SIZE_UPDATE_NOT_ALLOWED = 53,
SPDY_ERROR_HPACK_INITIAL_DYNAMIC_TABLE_SIZE_UPDATE_IS_ABOVE_LOW_WATER_MARK =
54,
SPDY_ERROR_HPACK_DYNAMIC_TABLE_SIZE_UPDATE_IS_ABOVE_ACKNOWLEDGED_SETTING = 55,
SPDY_ERROR_HPACK_TRUNCATED_BLOCK = 56,
SPDY_ERROR_HPACK_FRAGMENT_TOO_LONG = 57,
SPDY_ERROR_HPACK_COMPRESSED_HEADER_SIZE_EXCEEDS_LIMIT = 58,
// spdy::SpdyErrorCode mappings.
STATUS_CODE_NO_ERROR = 41,
STATUS_CODE_PROTOCOL_ERROR = 11,
STATUS_CODE_INTERNAL_ERROR = 16,
STATUS_CODE_FLOW_CONTROL_ERROR = 17,
STATUS_CODE_SETTINGS_TIMEOUT = 32,
STATUS_CODE_STREAM_CLOSED = 12,
STATUS_CODE_FRAME_SIZE_ERROR = 21,
STATUS_CODE_REFUSED_STREAM = 13,
STATUS_CODE_CANCEL = 15,
STATUS_CODE_COMPRESSION_ERROR = 42,
STATUS_CODE_CONNECT_ERROR = 33,
STATUS_CODE_ENHANCE_YOUR_CALM = 34,
STATUS_CODE_INADEQUATE_SECURITY = 35,
STATUS_CODE_HTTP_1_1_REQUIRED = 36,
// Deprecated SpdyRstStrreamStatus mappings.
STATUS_CODE_UNSUPPORTED_VERSION = 14,
STATUS_CODE_STREAM_IN_USE = 18,
STATUS_CODE_STREAM_ALREADY_CLOSED = 19,
// SpdySession errors
PROTOCOL_ERROR_UNEXPECTED_PING = 22,
PROTOCOL_ERROR_RST_STREAM_FOR_NON_ACTIVE_STREAM = 23,
PROTOCOL_ERROR_SPDY_COMPRESSION_FAILURE = 24,
PROTOCOL_ERROR_REQUEST_FOR_SECURE_CONTENT_OVER_INSECURE_SESSION = 25,
PROTOCOL_ERROR_SYN_REPLY_NOT_RECEIVED = 26,
PROTOCOL_ERROR_INVALID_WINDOW_UPDATE_SIZE = 27,
PROTOCOL_ERROR_RECEIVE_WINDOW_VIOLATION = 28,
// Next free value.
NUM_SPDY_PROTOCOL_ERROR_DETAILS = 59,
};
SpdyProtocolErrorDetails NET_EXPORT_PRIVATE MapFramerErrorToProtocolError(
http2::Http2DecoderAdapter::SpdyFramerError error);
Error NET_EXPORT_PRIVATE
MapFramerErrorToNetError(http2::Http2DecoderAdapter::SpdyFramerError error);
SpdyProtocolErrorDetails NET_EXPORT_PRIVATE
MapRstStreamStatusToProtocolError(spdy::SpdyErrorCode error_code);
spdy::SpdyErrorCode NET_EXPORT_PRIVATE MapNetErrorToGoAwayStatus(Error err);
// There is an enum of the same name in tools/metrics/histograms/enums.xml.
// Be sure to add new values there also.
enum class SpdyPushedStreamFate {
kTooManyPushedStreams = 0,
kTimeout = 1,
kPromisedStreamIdParityError = 2,
kAssociatedStreamIdParityError = 3,
kStreamIdOutOfOrder = 4,
kGoingAway = 5,
kInvalidUrl = 6,
kInactiveAssociatedStream = 7,
kNonHttpSchemeFromTrustedProxy = 8,
kNonHttpsPushedScheme = 9,
kNonHttpsAssociatedScheme = 10,
kCertificateMismatch = 11,
kDuplicateUrl = 12,
kClientRequestNotRange = 13,
kPushedRequestNotRange = 14,
kRangeMismatch = 15,
kVaryMismatch = 16,
kAcceptedNoVary = 17,
kAcceptedMatchingVary = 18,
kPushDisabled = 19,
kAlreadyInCache = 20,
kUnsupportedStatusCode = 21,
kMaxValue = kUnsupportedStatusCode
};
// If these compile asserts fail then SpdyProtocolErrorDetails needs
// to be updated with new values, as do the mapping functions above.
static_assert(33 == http2::Http2DecoderAdapter::LAST_ERROR,
"SpdyProtocolErrorDetails / Spdy Errors mismatch");
static_assert(13 == spdy::SpdyErrorCode::ERROR_CODE_MAX,
"SpdyProtocolErrorDetails / spdy::SpdyErrorCode mismatch");
// A helper class used to manage a request to create a stream.
class NET_EXPORT_PRIVATE SpdyStreamRequest {
public:
SpdyStreamRequest();
// Calls CancelRequest().
~SpdyStreamRequest();
// Returns the time when ConfirmHandshake() completed, if this request had to
// wait for ConfirmHandshake().
base::TimeTicks confirm_handshake_end() const {
return confirm_handshake_end_;
}
// Starts the request to create a stream. If OK is returned, then
// ReleaseStream() may be called. If ERR_IO_PENDING is returned,
// then when the stream is created, |callback| will be called, at
// which point ReleaseStream() may be called. Otherwise, the stream
// is not created, an error is returned, and ReleaseStream() may not
// be called.
//
// If |can_send_early| is true, this request is allowed to be sent over
// TLS 1.3 0RTT without confirming the handshake.
//
// If OK is returned, must not be called again without
// ReleaseStream() being called first. If ERR_IO_PENDING is
// returned, must not be called again without CancelRequest() or
// ReleaseStream() being called first. Otherwise, in case of an
// immediate error, this may be called again.
int StartRequest(SpdyStreamType type,
const base::WeakPtr<SpdySession>& session,
const GURL& url,
bool can_send_early,
RequestPriority priority,
const SocketTag& socket_tag,
const NetLogWithSource& net_log,
CompletionOnceCallback callback,
const NetworkTrafficAnnotationTag& traffic_annotation);
// Cancels any pending stream creation request. May be called
// repeatedly.
void CancelRequest();
// Transfers the created stream (guaranteed to not be NULL) to the
// caller. Must be called at most once after StartRequest() returns
// OK or |callback| is called with OK. The caller must immediately
// set a delegate for the returned stream (except for test code).
base::WeakPtr<SpdyStream> ReleaseStream();
// Changes the priority of the stream, or changes the priority of the queued
// request in the session.
void SetPriority(RequestPriority priority);
// Returns the estimate of dynamically allocated memory in bytes.
size_t EstimateMemoryUsage() const;
const NetworkTrafficAnnotationTag traffic_annotation() const {
return NetworkTrafficAnnotationTag(traffic_annotation_);
}
private:
friend class SpdySession;
void OnConfirmHandshakeComplete(int rv);
// Called by |session_| when the stream attempt has finished
// successfully.
void OnRequestCompleteSuccess(const base::WeakPtr<SpdyStream>& stream);
// Called by |session_| when the stream attempt has finished with an
// error. Also called with ERR_ABORTED if |session_| is destroyed
// while the stream attempt is still pending.
void OnRequestCompleteFailure(int rv);
// Accessors called by |session_|.
SpdyStreamType type() const { return type_; }
const GURL& url() const { return url_; }
RequestPriority priority() const { return priority_; }
const NetLogWithSource& net_log() const { return net_log_; }
void Reset();
SpdyStreamType type_;
base::WeakPtr<SpdySession> session_;
base::WeakPtr<SpdyStream> stream_;
GURL url_;
RequestPriority priority_;
SocketTag socket_tag_;
NetLogWithSource net_log_;
CompletionOnceCallback callback_;
MutableNetworkTrafficAnnotationTag traffic_annotation_;
base::TimeTicks confirm_handshake_end_;
base::WeakPtrFactory<SpdyStreamRequest> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(SpdyStreamRequest);
};
class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface,
public spdy::SpdyFramerDebugVisitorInterface,
public MultiplexedSession,
public HigherLayeredPool,
public Http2PushPromiseIndex::Delegate {
public:
// TODO(akalin): Use base::TickClock when it becomes available.
typedef base::TimeTicks (*TimeFunc)(void);
// Returns true if |new_hostname| can be pooled into an existing connection to
// |old_hostname| associated with |ssl_info|.
static bool CanPool(TransportSecurityState* transport_security_state,
const SSLInfo& ssl_info,
const SSLConfigService& ssl_config_service,
const std::string& old_hostname,
const std::string& new_hostname,
const net::NetworkIsolationKey& network_isolation_key);
// Create a new SpdySession.
// |spdy_session_key| is the host/port that this session connects to, privacy
// and proxy configuration settings that it's using.
// |net_log| is the NetLog that we log network events to.
SpdySession(const SpdySessionKey& spdy_session_key,
HttpServerProperties* http_server_properties,
TransportSecurityState* transport_security_state,
SSLConfigService* ssl_config_service,
const quic::ParsedQuicVersionVector& quic_supported_versions,
bool enable_sending_initial_data,
bool enable_ping_based_connection_checking,
bool is_http_enabled,
bool is_quic_enabled,
size_t session_max_recv_window_size,
int session_max_queued_capped_frames,
const spdy::SettingsMap& initial_settings,
const absl::optional<SpdySessionPool::GreasedHttp2Frame>&
greased_http2_frame,
bool http2_end_stream_with_data_frame,
bool enable_priority_update,
TimeFunc time_func,
ServerPushDelegate* push_delegate,
NetworkQualityEstimator* network_quality_estimator,
NetLog* net_log);
~SpdySession() override;
const HostPortPair& host_port_pair() const {
return spdy_session_key_.host_port_proxy_pair().first;
}
const HostPortProxyPair& host_port_proxy_pair() const {
return spdy_session_key_.host_port_proxy_pair();
}
const SpdySessionKey& spdy_session_key() const { return spdy_session_key_; }
// Get a pushed stream for a given |url| with stream ID |pushed_stream_id|.
// The caller must have already claimed the stream from Http2PushPromiseIndex.
// |pushed_stream_id| must not be kNoPushedStreamFound.
//
// Returns ERR_CONNECTION_CLOSED if the connection is being closed.
// Returns ERR_HTTP2_PUSHED_STREAM_NOT_AVAILABLE if the pushed stream is not
// available any longer, for example, if the server has reset it.
// Returns OK if the stream is still available, and returns the stream in
// |*spdy_stream|. If the stream is still open, updates its priority to
// |priority|.
int GetPushedStream(const GURL& url,
spdy::SpdyStreamId pushed_stream_id,
RequestPriority priority,
SpdyStream** spdy_stream);
// Called when the pushed stream should be cancelled. If the pushed stream is
// not claimed and active, sends RST to the server to cancel the stream.
void CancelPush(const GURL& url);
// Initialize the session with the given connection.
//
// |pool| is the SpdySessionPool that owns us. Its lifetime must
// strictly be greater than |this|.
//
// The session begins reading from |client_socket_handle| on a subsequent
// event loop iteration, so the SpdySession may close immediately afterwards
// if the first read of |client_socket_handle| fails.
void InitializeWithSocketHandle(
std::unique_ptr<ClientSocketHandle> client_socket_handle,
SpdySessionPool* pool);
// Just like InitializeWithSocketHandle(), but for use when the session is not
// on top of a socket pool, but instead directly on top of a socket, which the
// session has sole ownership of, and is responsible for deleting directly
// itself.
void InitializeWithSocket(std::unique_ptr<StreamSocket> stream_socket,
const LoadTimingInfo::ConnectTiming& connect_timing,
SpdySessionPool* pool);
// Parse ALPS application_data from TLS handshake.
// Returns OK on success. Return a net error code on failure, and closes the
// connection with the same error code.
int ParseAlps();
// Check to see if this SPDY session can support an additional domain.
// If the session is un-authenticated, then this call always returns true.
// For SSL-based sessions, verifies that the server certificate in use by
// this session provides authentication for the domain and no client
// certificate or channel ID was sent to the original server during the SSL
// handshake. NOTE: This function can have false negatives on some
// platforms.
// TODO(wtc): rename this function and the Net.SpdyIPPoolDomainMatch
// histogram because this function does more than verifying domain
// authentication now.
bool VerifyDomainAuthentication(const std::string& domain) const;
// Pushes the given producer into the write queue for
// |stream|. |stream| is guaranteed to be activated before the
// producer is used to produce its frame.
void EnqueueStreamWrite(const base::WeakPtr<SpdyStream>& stream,
spdy::SpdyFrameType frame_type,
std::unique_ptr<SpdyBufferProducer> producer);
// Returns true if this session is configured to send greased HTTP/2 frames.
// For more details on greased frames, see
// https://tools.ietf.org/html/draft-bishop-httpbis-grease-00.
bool GreasedFramesEnabled() const;
// Returns true if HEADERS frames on request streams should not have the
// END_STREAM flag set, but instead an empty DATA frame with END_STREAM should
// be sent afterwards to close the stream. Does not apply to bidirectional or
// proxy streams.
bool EndStreamWithDataFrame() const {
return http2_end_stream_with_data_frame_;
}
// Send greased frame, that is, a frame of reserved type.
void EnqueueGreasedFrame(const base::WeakPtr<SpdyStream>& stream);
// Returns whether HTTP/2 style priority information (stream dependency and
// weight fields in HEADERS frames, and PRIORITY frames) should be sent. True
// unless |enable_priority_update_| is true and
// SETTINGS_DEPRECATE_HTTP2_PRIORITIES with value 1 has been received from
// server. In particular, if it returns false, it will always return false
// afterwards.
bool ShouldSendHttp2Priority() const;
// Returns whether PRIORITY_UPDATE frames should be sent. False if
// |enable_priority_update_| is false. Otherwise, true before SETTINGS frame
// is received from server, and true after SETTINGS frame is received if it
// contained SETTINGS_DEPRECATE_HTTP2_PRIORITIES with value 1. In particular,
// if it returns false, it will always return false afterwards.
bool ShouldSendPriorityUpdate() const;
// Runs the handshake to completion to confirm the handshake with the server.
// If ERR_IO_PENDING is returned, then when the handshake is confirmed,
// |callback| will be called.
int ConfirmHandshake(CompletionOnceCallback callback);
// Creates and returns a HEADERS frame for |stream_id|.
std::unique_ptr<spdy::SpdySerializedFrame> CreateHeaders(
spdy::SpdyStreamId stream_id,
RequestPriority priority,
spdy::SpdyControlFlags flags,
spdy::Http2HeaderBlock headers,
NetLogSource source_dependency);
// Creates and returns a SpdyBuffer holding a data frame with the given data.
// Sets |*effective_len| to number of bytes sent, and |*end_stream| to the
// value of the END_STREAM (also known as fin) flag. Returns nullptr if
// session is draining or if session or stream is stalled by flow control.
std::unique_ptr<SpdyBuffer> CreateDataBuffer(spdy::SpdyStreamId stream_id,
IOBuffer* data,
int len,
spdy::SpdyDataFlags flags,
int* effective_len,
bool* end_stream);
// Send PRIORITY frames according to the new priority of an existing stream.
void UpdateStreamPriority(SpdyStream* stream,
RequestPriority old_priority,
RequestPriority new_priority);
// Close the stream with the given ID, which must exist and be
// active. Note that that stream may hold the last reference to the
// session.
void CloseActiveStream(spdy::SpdyStreamId stream_id, int status);
// Close the given created stream, which must exist but not yet be
// active. Note that |stream| may hold the last reference to the
// session.
void CloseCreatedStream(const base::WeakPtr<SpdyStream>& stream, int status);
// Send a RST_STREAM frame with the given status code and close the
// stream with the given ID, which must exist and be active. Note
// that that stream may hold the last reference to the session.
void ResetStream(spdy::SpdyStreamId stream_id,
int error,
const std::string& description);
// Check if a stream is active.
bool IsStreamActive(spdy::SpdyStreamId stream_id) const;
// The LoadState is used for informing the user of the current network
// status, such as "resolving host", "connecting", etc.
LoadState GetLoadState() const;
// MultiplexedSession methods:
bool GetRemoteEndpoint(IPEndPoint* endpoint) override;
bool GetSSLInfo(SSLInfo* ssl_info) const override;
base::StringPiece GetAcceptChViaAlpsForOrigin(
const url::Origin& origin) const override;
// Returns true if ALPN was negotiated for the underlying socket.
bool WasAlpnNegotiated() const;
// Returns the protocol negotiated via ALPN for the underlying socket.
NextProto GetNegotiatedProtocol() const;
// Send a WINDOW_UPDATE frame for a stream. Called by a stream
// whenever receive window size is increased.
void SendStreamWindowUpdate(spdy::SpdyStreamId stream_id,
uint32_t delta_window_size);
// Accessors for the session's availability state.
bool IsAvailable() const { return availability_state_ == STATE_AVAILABLE; }
bool IsGoingAway() const { return availability_state_ == STATE_GOING_AWAY; }
bool IsDraining() const { return availability_state_ == STATE_DRAINING; }
// Closes this session. This will close all active streams and mark
// the session as permanently closed. Callers must assume that the
// session is destroyed after this is called. (However, it may not
// be destroyed right away, e.g. when a SpdySession function is
// present in the call stack.)
//
// |err| should be < ERR_IO_PENDING; this function is intended to be
// called on error.
// |description| indicates the reason for the error.
void CloseSessionOnError(Error err, const std::string& description);
// Mark this session as unavailable, meaning that it will not be used to
// service new streams. Unlike when a GOAWAY frame is received, this function
// will not close any streams.
void MakeUnavailable();
// Closes all active streams with stream id's greater than
// |last_good_stream_id|, as well as any created or pending
// streams. Must be called only when |availability_state_| >=
// STATE_GOING_AWAY. After this function, DcheckGoingAway() will
// pass. May be called multiple times.
void StartGoingAway(spdy::SpdyStreamId last_good_stream_id, Error status);
// Must be called only when going away (i.e., DcheckGoingAway()
// passes). If there are no more active streams and the session
// isn't closed yet, close it.
void MaybeFinishGoingAway();
// Retrieves information on the current state of the SPDY session as a
// Value.
base::Value GetInfoAsValue() const;
// Indicates whether the session is being reused after having successfully
// used to send/receive data in the past or if the underlying socket was idle
// before being used for a SPDY session.
bool IsReused() const;
// Returns true if the underlying transport socket ever had any reads or
// writes.
bool WasEverUsed() const { return socket_->WasEverUsed(); }
// Returns the load timing information from the perspective of the given
// stream. If it's not the first stream, the connection is considered reused
// for that stream.
//
// This uses a different notion of reuse than IsReused(). This function
// sets |socket_reused| to false only if |stream_id| is the ID of the first
// stream using the session. IsReused(), on the other hand, indicates if the
// session has been used to send/receive data at all.
bool GetLoadTimingInfo(spdy::SpdyStreamId stream_id,
LoadTimingInfo* load_timing_info) const;
// Returns true if session is currently active.
bool is_active() const {
return !active_streams_.empty() || !created_streams_.empty();
}
// True if the server supports WebSocket protocol.
bool support_websocket() const { return support_websocket_; }
// Returns true if no stream in the session can send data due to
// session flow control.
bool IsSendStalled() const { return session_send_window_size_ == 0; }
const NetLogWithSource& net_log() const { return net_log_; }
int GetPeerAddress(IPEndPoint* address) const;
int GetLocalAddress(IPEndPoint* address) const;
// Adds |alias| to set of aliases associated with this session.
void AddPooledAlias(const SpdySessionKey& alias_key);
// Removes |alias| from set of aliases associated with this session.
void RemovePooledAlias(const SpdySessionKey& alias_key);
// Returns the set of aliases associated with this session.
const std::set<SpdySessionKey>& pooled_aliases() const {
return pooled_aliases_;
}
// https://http2.github.io/http2-spec/#TLSUsage mandates minimum security
// standards for TLS.
bool HasAcceptableTransportSecurity() const;
// Must be used only by |pool_| (including |pool_.push_promise_index_|).
base::WeakPtr<SpdySession> GetWeakPtr();
// HigherLayeredPool implementation:
bool CloseOneIdleConnection() override;
// Http2PushPromiseIndex::Delegate implementation:
bool ValidatePushedStream(spdy::SpdyStreamId stream_id,
const GURL& url,
const HttpRequestInfo& request_info,
const SpdySessionKey& key) const override;
base::WeakPtr<SpdySession> GetWeakPtrToSession() override;
// Dumps memory allocation stats to |stats|. Sets |*is_session_active| to
// indicate whether session is active.
// |stats| can be assumed as being default initialized upon entry.
// Implementation overrides fields in |stats|.
// Returns the estimate of dynamically allocated memory in bytes, which
// includes the size attributed to the underlying socket.
size_t DumpMemoryStats(StreamSocket::SocketMemoryStats* stats,
bool* is_session_active) const;
// Change this session's socket tag to |new_tag|. Returns true on success.
bool ChangeSocketTag(const SocketTag& new_tag);
static void RecordSpdyPushedStreamFateHistogram(SpdyPushedStreamFate value);
private:
friend class test::SpdyStreamTest;
friend class base::RefCounted<SpdySession>;
friend class HttpNetworkTransactionTest;
friend class HttpProxyClientSocketPoolTest;
friend class SpdyHttpStreamTest;
friend class SpdyNetworkTransactionTest;
friend class SpdyProxyClientSocketTest;
friend class SpdySessionPoolTest;
friend class SpdySessionTest;
friend class SpdyStreamRequest;
FRIEND_TEST_ALL_PREFIXES(RecordPushedStreamHistogramTest, VaryResponseHeader);
using PendingStreamRequestQueue =
base::circular_deque<base::WeakPtr<SpdyStreamRequest>>;
using ActiveStreamMap = std::map<spdy::SpdyStreamId, SpdyStream*>;
using CreatedStreamSet = std::set<SpdyStream*>;
enum AvailabilityState {
// The session is available in its socket pool and can be used
// freely.
STATE_AVAILABLE,
// The session can process data on existing streams but will
// refuse to create new ones.
STATE_GOING_AWAY,
// The session is draining its write queue in preparation of closing.
// Further writes will not be queued, and further reads will not be issued
// (though the remainder of a current read may be processed). The session
// will be destroyed by its write loop once the write queue is drained.
STATE_DRAINING,
};
enum ReadState {
READ_STATE_DO_READ,
READ_STATE_DO_READ_COMPLETE,
};
enum WriteState {
// There is no in-flight write and the write queue is empty.
WRITE_STATE_IDLE,
WRITE_STATE_DO_WRITE,
WRITE_STATE_DO_WRITE_COMPLETE,
};
// Has the shared logic for the other two Initialize methods that call it.
void InitializeInternal(SpdySessionPool* pool);
// Called by SpdyStreamRequest to start a request to create a
// stream. If OK is returned, then |stream| will be filled in with a
// valid stream. If ERR_IO_PENDING is returned, then
// |request->OnRequestComplete{Success,Failure}()| will be called
// when the stream is created (unless it is cancelled). Otherwise,
// no stream is created and the error is returned.
int TryCreateStream(const base::WeakPtr<SpdyStreamRequest>& request,
base::WeakPtr<SpdyStream>* stream);
// Actually create a stream into |stream|. Returns OK if successful;
// otherwise, returns an error and |stream| is not filled.
int CreateStream(const SpdyStreamRequest& request,
base::WeakPtr<SpdyStream>* stream);
// Called by SpdyStreamRequest to remove |request| from the stream
// creation queue. Returns whether a request was removed from the queue.
bool CancelStreamRequest(const base::WeakPtr<SpdyStreamRequest>& request);
// Removes |request| from the stream creation queue and reinserts it into the
// queue at the new |priority|.
void ChangeStreamRequestPriority(
const base::WeakPtr<SpdyStreamRequest>& request,
RequestPriority priority);
// Returns the next pending stream request to process, or NULL if
// there is none.
base::WeakPtr<SpdyStreamRequest> GetNextPendingStreamRequest();
// Called when there is room to create more streams (e.g., a stream
// was closed). Processes as many pending stream requests as
// possible.
void ProcessPendingStreamRequests();
void TryCreatePushStream(spdy::SpdyStreamId stream_id,
spdy::SpdyStreamId associated_stream_id,
spdy::Http2HeaderBlock headers);
// Close the stream pointed to by the given iterator. Note that that
// stream may hold the last reference to the session.
void CloseActiveStreamIterator(ActiveStreamMap::iterator it, int status);
// Close the stream pointed to by the given iterator. Note that that
// stream may hold the last reference to the session.
void CloseCreatedStreamIterator(CreatedStreamSet::iterator it, int status);
// Calls EnqueueResetStreamFrame() and then
// CloseActiveStreamIterator().
void ResetStreamIterator(ActiveStreamMap::iterator it,
int status,
const std::string& description);
// Send a RST_STREAM frame with the given parameters. There should
// either be no active stream with the given ID, or that active
// stream should be closed shortly after this function is called.
void EnqueueResetStreamFrame(spdy::SpdyStreamId stream_id,
RequestPriority priority,
spdy::SpdyErrorCode error_code,
const std::string& description);
// Send a PRIORITY frame with the given parameters.
void EnqueuePriorityFrame(spdy::SpdyStreamId stream_id,
spdy::SpdyStreamId dependency_id,
int weight,
bool exclusive);
// Calls DoReadLoop. Use this function instead of DoReadLoop when
// posting a task to pump the read loop.
void PumpReadLoop(ReadState expected_read_state, int result);
// Advance the ReadState state machine. |expected_read_state| is the
// expected starting read state.
//
// This function must always be called via PumpReadLoop().
int DoReadLoop(ReadState expected_read_state, int result);
// The implementations of the states of the ReadState state machine.
int DoRead();
int DoReadComplete(int result);
// Calls DoWriteLoop. If |availability_state_| is STATE_DRAINING and no
// writes remain, the session is removed from the session pool and
// destroyed.
//
// Use this function instead of DoWriteLoop when posting a task to
// pump the write loop.
void PumpWriteLoop(WriteState expected_write_state, int result);
// Iff the write loop is not currently active, posts a callback into
// PumpWriteLoop().
void MaybePostWriteLoop();
// Advance the WriteState state machine. |expected_write_state| is
// the expected starting write state.
//
// This function must always be called via PumpWriteLoop().
int DoWriteLoop(WriteState expected_write_state, int result);
// The implementations of the states of the WriteState state machine.
int DoWrite();
int DoWriteComplete(int result);
void NotifyRequestsOfConfirmation(int rv);
// TODO(akalin): Rename the Send* and Write* functions below to
// Enqueue*.
// Send initial data. Called when a connection is successfully
// established in InitializeWithSocket() and
// |enable_sending_initial_data_| is true.
void SendInitialData();
// Handle SETTING. Either when we send settings, or when we receive a
// SETTINGS control frame, update our SpdySession accordingly.
void HandleSetting(uint32_t id, uint32_t value);
// Adjust the send window size of all ActiveStreams and PendingStreamRequests.
void UpdateStreamsSendWindowSize(int32_t delta_window_size);
// Send PING frame if all previous PING frames have been ACKed,
// all posted CheckPingStatus() tasks have been executed,
// and too long time has passed since last read from server.
void MaybeSendPrefacePing();
// Send a single WINDOW_UPDATE frame.
void SendWindowUpdateFrame(spdy::SpdyStreamId stream_id,
uint32_t delta_window_size,
RequestPriority priority);
// Send the PING frame.
void WritePingFrame(spdy::SpdyPingId unique_id, bool is_ack);
// Post a CheckPingStatus call after delay. Don't post if there is already
// CheckPingStatus running.
void PlanToCheckPingStatus();
// Check the status of the connection. It calls |CloseSessionOnError| if we
// haven't received any data in |kHungInterval| time period.
void CheckPingStatus(base::TimeTicks last_check_time);
// Get a new stream id.
spdy::SpdyStreamId GetNewStreamId();
// Pushes the given frame with the given priority into the write
// queue for the session.
void EnqueueSessionWrite(RequestPriority priority,
spdy::SpdyFrameType frame_type,
std::unique_ptr<spdy::SpdySerializedFrame> frame);
// Puts |producer| associated with |stream| onto the write queue
// with the given priority.
void EnqueueWrite(RequestPriority priority,
spdy::SpdyFrameType frame_type,
std::unique_ptr<SpdyBufferProducer> producer,
const base::WeakPtr<SpdyStream>& stream,
const NetworkTrafficAnnotationTag& traffic_annotation);
// Inserts a newly-created stream into |created_streams_|.
void InsertCreatedStream(std::unique_ptr<SpdyStream> stream);
// Activates |stream| (which must be in |created_streams_|) by
// assigning it an ID and returns it.
std::unique_ptr<SpdyStream> ActivateCreatedStream(SpdyStream* stream);
// Inserts a newly-activated stream into |active_streams_|.
void InsertActivatedStream(std::unique_ptr<SpdyStream> stream);
// Remove all internal references to |stream|, call OnClose() on it,
// and process any pending stream requests before deleting it. Note
// that |stream| may hold the last reference to the session.
void DeleteStream(std::unique_ptr<SpdyStream> stream, int status);
void RecordHistograms();
void RecordProtocolErrorHistogram(SpdyProtocolErrorDetails details);
static void RecordPushedStreamVaryResponseHeaderHistogram(
const spdy::Http2HeaderBlock& headers);
// DCHECKs that |availability_state_| >= STATE_GOING_AWAY, that
// there are no pending stream creation requests, and that there are
// no created streams.
void DcheckGoingAway() const;
// Calls DcheckGoingAway(), then DCHECKs that |availability_state_|
// == STATE_DRAINING, |error_on_close_| has a valid value, and that there
// are no active streams or unclaimed pushed streams.
void DcheckDraining() const;
// If the session is already draining, does nothing. Otherwise, moves
// the session to the draining state.
void DoDrainSession(Error err, const std::string& description);
// Called right before closing a (possibly-inactive) stream for a
// reason other than being requested to by the stream.
void LogAbandonedStream(SpdyStream* stream, Error status);
// Called right before closing an active stream for a reason other
// than being requested to by the stream.
void LogAbandonedActiveStream(ActiveStreamMap::const_iterator it,
Error status);
// Invokes a user callback for stream creation. We provide this method so it
// can be deferred to the MessageLoop, so we avoid re-entrancy problems.
void CompleteStreamRequest(
const base::WeakPtr<SpdyStreamRequest>& pending_request);
// Cancel pushed stream with |stream_id|, if still unclaimed. Identifying a
// pushed stream by GURL instead of stream ID could result in incorrect
// behavior if a pushed stream was claimed but later another stream was pushed
// for the same GURL.
void CancelPushedStreamIfUnclaimed(spdy::SpdyStreamId stream_id);
// BufferedSpdyFramerVisitorInterface:
void OnError(
http2::Http2DecoderAdapter::SpdyFramerError spdy_framer_error) override;
void OnStreamError(spdy::SpdyStreamId stream_id,
const std::string& description) override;
void OnPing(spdy::SpdyPingId unique_id, bool is_ack) override;
void OnRstStream(spdy::SpdyStreamId stream_id,
spdy::SpdyErrorCode error_code) override;
void OnGoAway(spdy::SpdyStreamId last_accepted_stream_id,
spdy::SpdyErrorCode error_code,
base::StringPiece debug_data) override;
void OnDataFrameHeader(spdy::SpdyStreamId stream_id,
size_t length,
bool fin) override;
void OnStreamFrameData(spdy::SpdyStreamId stream_id,
const char* data,
size_t len) override;
void OnStreamEnd(spdy::SpdyStreamId stream_id) override;
void OnStreamPadding(spdy::SpdyStreamId stream_id, size_t len) override;
void OnSettings() override;
void OnSettingsAck() override;
void OnSetting(spdy::SpdySettingsId id, uint32_t value) override;
void OnSettingsEnd() override;
void OnWindowUpdate(spdy::SpdyStreamId stream_id,
int delta_window_size) override;
void OnPushPromise(spdy::SpdyStreamId stream_id,
spdy::SpdyStreamId promised_stream_id,
spdy::Http2HeaderBlock headers) override;
void OnHeaders(spdy::SpdyStreamId stream_id,
bool has_priority,
int weight,
spdy::SpdyStreamId parent_stream_id,
bool exclusive,
bool fin,
spdy::Http2HeaderBlock headers,
base::TimeTicks recv_first_byte_time) override;
void OnAltSvc(spdy::SpdyStreamId stream_id,
base::StringPiece origin,
const spdy::SpdyAltSvcWireFormat::AlternativeServiceVector&
altsvc_vector) override;
bool OnUnknownFrame(spdy::SpdyStreamId stream_id,
uint8_t frame_type) override;
// spdy::SpdyFramerDebugVisitorInterface
void OnSendCompressedFrame(spdy::SpdyStreamId stream_id,
spdy::SpdyFrameType type,
size_t payload_len,
size_t frame_len) override;
void OnReceiveCompressedFrame(spdy::SpdyStreamId stream_id,
spdy::SpdyFrameType type,
size_t frame_len) override;
// Called when bytes are consumed from a SpdyBuffer for a DATA frame
// that is to be written or is being written. Increases the send
// window size accordingly if some or all of the SpdyBuffer is being
// discarded.
//
// If session flow control is turned off, this must not be called.
void OnWriteBufferConsumed(size_t frame_payload_size,
size_t consume_size,
SpdyBuffer::ConsumeSource consume_source);
// Called by OnWindowUpdate() (which is in turn called by the
// framer) to increase this session's send window size by
// |delta_window_size| from a WINDOW_UPDATE frome, which must be at
// least 1. If |delta_window_size| would cause this session's send
// window size to overflow, does nothing.
//
// If session flow control is turned off, this must not be called.
void IncreaseSendWindowSize(int delta_window_size);
// If session flow control is turned on, called by CreateDataFrame()
// (which is in turn called by a stream) to decrease this session's
// send window size by |delta_window_size|, which must be at least 1
// and at most kMaxSpdyFrameChunkSize. |delta_window_size| must not
// cause this session's send window size to go negative.
//
// If session flow control is turned off, this must not be called.
void DecreaseSendWindowSize(int32_t delta_window_size);
// Called when bytes are consumed by the delegate from a SpdyBuffer
// containing received data. Increases the receive window size
// accordingly.
//
// If session flow control is turned off, this must not be called.
void OnReadBufferConsumed(size_t consume_size,
SpdyBuffer::ConsumeSource consume_source);
// Called by OnReadBufferConsume to increase this session's receive
// window size by |delta_window_size|, which must be at least 1 and
// must not cause this session's receive window size to overflow,
// possibly also sending a WINDOW_UPDATE frame. Also called during
// initialization to set the initial receive window size.