diff options
author | danzh <danzh@chromium.org> | 2016-03-04 13:58:16 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-03-04 22:00:06 +0000 |
commit | 33407f13bfd05bc2614fd063bcbc6ad38904ad24 (patch) | |
tree | aa9cbf53a28b2be8eae6b95fbe6beafe70b5715a | |
parent | 5906d7d07155203356125d21f34a349442cea5be (diff) | |
download | chromium_src-33407f13bfd05bc2614fd063bcbc6ad38904ad24.zip chromium_src-33407f13bfd05bc2614fd063bcbc6ad38904ad24.tar.gz chromium_src-33407f13bfd05bc2614fd063bcbc6ad38904ad24.tar.bz2 |
Landing recent QUIC changes until 7:19 PM, Feb 26, 2016 UTC-5.
relnote: Deprecate --FLAGS_quic_use_stream_sequencer_buffer.
Merge internal change: 115699221
https://codereview.chromium.org/1764913002/#ps20001
relnote: Deprecate --FLAGS_enable_quic_fec.
Disable the ability to enable FEC protection via connection options.
First change to remove FEC.
Merge internal change: 115690862
https://codereview.chromium.org/1766623002/#ps20001
relnote: Make QUIC version negotiation stateless. The QUIC dispatcher now detects version mismatch before creating a QUIC session and sends out a version negotiation packet at that point. Protected by --Flag_quic_stateless_version_negotiation
Merge internal change: 115603675
https://codereview.chromium.org/1761253002/#ps20001
relnote: Reserve exactly enough space for QUIC timestamps in QuicAckFrame. received_packet_times. No functional change.
Merge internal change: 115591435
https://codereview.chromium.org/1760973003/#ps20001
relnote: Remove single line virtual methods that are not overridden anywhere.
QUIC client change.
Merge internal change: 115591067
https://codereview.chromium.org/1761643004/#ps20001
relnote: QUIC toy client/server change
Replace custom client packet reading code with call to
QuicPacketReader::ReadAndDispatchSinglePacket
Merge internal change: 115551866
https://codereview.chromium.org/1760423002/#ps20001
relnote: Remove virtual keyword from QuicSpdyStream::WriteTrailers.
Merge internal change: 115456160
https://codereview.chromium.org/1745303003/#ps40001
relnote: Switch to unique_ptr<GURL> for request uri in SpdyBalsaUtils::RequestHeadersToSpdyHeaders(). No function change.
Merge internal change: 115455799
https://codereview.chromium.org/1749303002/#ps40001
relnote: QUIC toy client/server changes
Adds a QuicSocketUtils::CreateUDPSocket method, and rename the existing
client/server CreateUDPSocket methods to be a bit more descriptive.
Merge internal change: 115444705
https://codereview.chromium.org/1752823002/#ps40001
relnote: Remove an unused declaration of QuicAckNotifier.
Merge internal change: 115400573
https://codereview.chromium.org/1751923003/#ps40001
relnote: Added new method OnStreamEnd to SpdyFramerVisitorInterface, stubbed implementations, No functional change.
Added a new visitor interface method to the SpdyFramerVisitorInterface
with signature:
virtual void OnStreamEnd(SpdyStreamId stream_id) = 0;
Stubbed the new method in all implementors.
The eventual goal is to use the new method to handle stream ends
consistently, removing the need for fin boolean arguments in the other
methods in the visitor.
Merge internal change: 115370186
https://codereview.chromium.org/1749203002/#ps40001
relnote: Deprecate --FLAGS_quic_distinguish_incoming_outgoing_streams.
Merge internal change: 115342288
https://codereview.chromium.org/1755663002#ps60001
relnote: Remove unnecessary ReadPacket method.
Merge internal change: 115339832
https://codereview.chromium.org/1750303003/#ps60001
relnote: Deprecate --FLAGS_quic_supports_trailers
Merge internal change: 115244730
https://codereview.chromium.org/1753453003
relnote: Add 3 Quic e2e tests of server push. No behavior change.
Merge internal change: 115228444
https://codereview.chromium.org/1746803003
relnote: QuicSession::CreateIncomingDynamicStream now retains ownership of the created stream. No functional change.
Adds the ActivateStream call to CreateIncomingDynamicStream, which makes
it behave in a similar way to CreateOutgoingDynamicStream. Both methods
now retain ownership of the created stream (ActivateStream adds it to
the dynamic_stream_map_).
Merge internal change: 115218980
https://codereview.chromium.org/1744103003
Review URL: https://codereview.chromium.org/1761263002
Cr-Commit-Position: refs/heads/master@{#379375}
58 files changed, 592 insertions, 433 deletions
diff --git a/net/quic/p2p/quic_p2p_session.cc b/net/quic/p2p/quic_p2p_session.cc index 148efe7..ba7298d 100644 --- a/net/quic/p2p/quic_p2p_session.cc +++ b/net/quic/p2p/quic_p2p_session.cc @@ -55,6 +55,7 @@ QuicP2PStream* QuicP2PSession::CreateIncomingDynamicStream(QuicStreamId id) { if (delegate_) { delegate_->OnIncomingStream(stream); } + ActivateStream(stream); return stream; } diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc index 63c6d10..47f46bd 100644 --- a/net/quic/quic_chromium_client_session.cc +++ b/net/quic/quic_chromium_client_session.cc @@ -651,6 +651,7 @@ QuicChromiumClientSession::CreateIncomingReliableStreamImpl(QuicStreamId id) { QuicChromiumClientStream* stream = new QuicChromiumClientStream(id, this, net_log_); stream->CloseWriteSide(); + ActivateStream(stream); ++num_total_streams_; return stream; } diff --git a/net/quic/quic_client_session_base.cc b/net/quic/quic_client_session_base.cc index 32b3093..7e45d00 100644 --- a/net/quic/quic_client_session_base.cc +++ b/net/quic/quic_client_session_base.cc @@ -28,16 +28,6 @@ QuicClientSessionBase::~QuicClientSessionBase() { void QuicClientSessionBase::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { QuicSession::OnCryptoHandshakeEvent(event); - // Set FEC policy for streams immediately after sending CHLO and before any - // more data is sent. - if (!FLAGS_enable_quic_fec || event != ENCRYPTION_FIRST_ESTABLISHED || - !config()->HasSendConnectionOptions() || - !ContainsQuicTag(config()->SendConnectionOptions(), kFHDR)) { - return; - } - // kFHDR config maps to FEC protection always for headers stream. - // TODO(jri): Add crypto stream in addition to headers for kHDR. - headers_stream()->set_fec_policy(FEC_PROTECT_ALWAYS); } void QuicClientSessionBase::OnPromiseHeaders(QuicStreamId stream_id, diff --git a/net/quic/quic_flags.cc b/net/quic/quic_flags.cc index 66868e7..9878561 100644 --- a/net/quic/quic_flags.cc +++ b/net/quic/quic_flags.cc @@ -11,11 +11,6 @@ bool FLAGS_quic_use_time_loss_detection = false; // CHLO. bool FLAGS_use_early_return_when_verifying_chlo = true; -// If true, QUIC connections will support FEC protection of data while sending -// packets, to reduce latency of data delivery to the application. The client -// must also request FEC protection for the server to use FEC. -bool FLAGS_enable_quic_fec = true; - // When true, defaults to BBR congestion control instead of Cubic. bool FLAGS_quic_use_bbr_congestion_control = false; @@ -71,10 +66,6 @@ bool FLAGS_quic_disable_pacing = false; // even if they are being sent. bool FLAGS_quic_use_new_idle_timeout = true; -// If true, replace QuicFrameList with StreamSequencerBuffer as underlying data -// structure for QuicStreamSequencer bufferring. -bool FLAGS_quic_use_stream_sequencer_buffer = true; - // If true, don't send QUIC packets if the send alarm is set. bool FLAGS_quic_respect_send_alarm2 = true; @@ -96,16 +87,9 @@ bool FLAGS_quic_no_unencrypted_fec = true; // If true, reject any incoming QUIC which does not have the FIXD tag. bool FLAGS_quic_require_fix = true; -// If true, QUIC supports sending trailers from Server to Client. -bool FLAGS_quic_supports_trailers = true; - // If true, headers stream will support receiving PUSH_PROMISE frames. bool FLAGS_quic_supports_push_promise = false; -// Enable counters for incoming/outgoing streams which are used as condition -// check while creating a new stream. -bool FLAGS_quic_distinguish_incoming_outgoing_streams = true; - // If true, QUIC servers will attempt to validate a client's source // address token using the primary config, even if no server config id // is present in the client hello. @@ -153,3 +137,7 @@ bool FLAGS_quic_use_new_tcp_sender = true; // Saves the initial subkey secret in QUIC crypto when deriving keys from the // initial premaster secret. bool FLAGS_quic_save_initial_subkey_secret = true; + +// If true, the QUIC dispatcher will directly send version negotiation packets +// without needing to create a QUIC session first. +bool FLAGS_quic_stateless_version_negotiation = false; diff --git a/net/quic/quic_flags.h b/net/quic/quic_flags.h index a991a59..a110e57 100644 --- a/net/quic/quic_flags.h +++ b/net/quic/quic_flags.h @@ -11,7 +11,6 @@ NET_EXPORT_PRIVATE extern bool FLAGS_quic_use_time_loss_detection; NET_EXPORT_PRIVATE extern bool FLAGS_use_early_return_when_verifying_chlo; -NET_EXPORT_PRIVATE extern bool FLAGS_enable_quic_fec; NET_EXPORT_PRIVATE extern bool FLAGS_quic_use_bbr_congestion_control; NET_EXPORT_PRIVATE extern bool FLAGS_quic_allow_bbr; NET_EXPORT_PRIVATE extern int64_t FLAGS_quic_time_wait_list_seconds; @@ -25,16 +24,15 @@ NET_EXPORT_PRIVATE extern bool FLAGS_shift_quic_cubic_epoch_when_app_limited; NET_EXPORT_PRIVATE extern bool FLAGS_quic_measure_headers_hol_blocking_time; NET_EXPORT_PRIVATE extern bool FLAGS_quic_disable_pacing; NET_EXPORT_PRIVATE extern bool FLAGS_quic_use_new_idle_timeout; -NET_EXPORT_PRIVATE extern bool FLAGS_quic_use_stream_sequencer_buffer; NET_EXPORT_PRIVATE extern bool FLAGS_quic_respect_send_alarm2; NET_EXPORT_PRIVATE extern bool FLAGS_quic_batch_writes; NET_EXPORT_PRIVATE extern bool FLAGS_quic_block_unencrypted_writes; NET_EXPORT_PRIVATE extern bool FLAGS_quic_never_write_unencrypted_data; NET_EXPORT_PRIVATE extern bool FLAGS_quic_no_unencrypted_fec; NET_EXPORT_PRIVATE extern bool FLAGS_quic_require_fix; -NET_EXPORT_PRIVATE extern bool FLAGS_quic_supports_trailers; +NET_EXPORT_PRIVATE extern bool FLAGS_quic_stateless_version_negotiation; +NET_EXPORT_PRIVATE extern bool FLAGS_quic_supports_push_promise; NET_EXPORT_PRIVATE extern bool FLAGS_quic_supports_push_promise; -NET_EXPORT_PRIVATE extern bool FLAGS_quic_distinguish_incoming_outgoing_streams; NET_EXPORT_PRIVATE extern bool FLAGS_quic_validate_stk_without_scid; NET_EXPORT_PRIVATE extern bool FLAGS_quic_use_rfc7539; NET_EXPORT_PRIVATE extern bool FLAGS_require_strike_register_or_server_nonce; diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc index 7d75b8f..ac58d57 100644 --- a/net/quic/quic_framer.cc +++ b/net/quic/quic_framer.cc @@ -975,6 +975,7 @@ bool QuicFramer::ProcessPublicHeader(QuicDataReader* reader, // If the version from the new packet is the same as the version of this // framer, then the public flags should be set to something we understand. // If not, this raises an error. + last_version_tag_ = version_tag; QuicVersion version = QuicTagToQuicVersion(version_tag); if (version == quic_version_ && public_flags > PACKET_PUBLIC_FLAGS_MAX) { set_detailed_error("Illegal public flags value."); @@ -1487,6 +1488,7 @@ bool QuicFramer::ProcessTimestampsInAckFrame(QuicDataReader* reader, } if (num_received_packets > 0) { + ack_frame->received_packet_times.reserve(num_received_packets); uint8_t delta_from_largest_observed; if (!reader->ReadBytes(&delta_from_largest_observed, PACKET_1BYTE_PACKET_NUMBER)) { diff --git a/net/quic/quic_framer.h b/net/quic/quic_framer.h index 456f2b0..3cc2426 100644 --- a/net/quic/quic_framer.h +++ b/net/quic/quic_framer.h @@ -369,6 +369,8 @@ class NET_EXPORT_PRIVATE QuicFramer { // Called when a PATH_CLOSED frame has been sent/received on |path_id|. void OnPathClosed(QuicPathId path_id); + QuicTag last_version_tag() { return last_version_tag_; } + private: friend class test::QuicFramerPeer; @@ -536,6 +538,8 @@ class NET_EXPORT_PRIVATE QuicFramer { QuicPathId last_path_id_; // Updated by WritePacketHeader. QuicConnectionId last_serialized_connection_id_; + // The last QUIC version tag received. + QuicTag last_version_tag_; // Version of the protocol being used. QuicVersion quic_version_; // This vector contains QUIC versions which we currently support. diff --git a/net/quic/quic_headers_stream.cc b/net/quic/quic_headers_stream.cc index 922c703..b62717c 100644 --- a/net/quic/quic_headers_stream.cc +++ b/net/quic/quic_headers_stream.cc @@ -65,6 +65,10 @@ class QuicHeadersStream::SpdyFramerVisitor CloseConnection("SPDY DATA frame received."); } + void OnStreamEnd(SpdyStreamId stream_id) override { + LOG(DFATAL) << "Unimplemented."; + } + void OnStreamPadding(SpdyStreamId stream_id, size_t len) override { CloseConnection("SPDY frame padding received."); } diff --git a/net/quic/quic_headers_stream_test.cc b/net/quic/quic_headers_stream_test.cc index 15e7c8d..a821fb2 100644 --- a/net/quic/quic_headers_stream_test.cc +++ b/net/quic/quic_headers_stream_test.cc @@ -42,6 +42,7 @@ class MockVisitor : public SpdyFramerVisitorInterface { MOCK_METHOD4( OnStreamFrameData, void(SpdyStreamId stream_id, const char* data, size_t len, bool fin)); + MOCK_METHOD1(OnStreamEnd, void(SpdyStreamId stream_id)); MOCK_METHOD2(OnStreamPadding, void(SpdyStreamId stream_id, size_t len)); MOCK_METHOD1(OnHeaderFrameStart, SpdyHeadersHandlerInterface*(SpdyStreamId stream_id)); diff --git a/net/quic/quic_packet_generator.cc b/net/quic/quic_packet_generator.cc index f9d6ed1..75e1c59 100644 --- a/net/quic/quic_packet_generator.cc +++ b/net/quic/quic_packet_generator.cc @@ -14,8 +14,6 @@ using base::StringPiece; namespace net { -class QuicAckNotifier; - QuicPacketGenerator::QuicPacketGenerator(QuicConnectionId connection_id, QuicFramer* framer, QuicRandom* random_generator, diff --git a/net/quic/quic_session.cc b/net/quic/quic_session.cc index e3aad3f..d8ec06c 100644 --- a/net/quic/quic_session.cc +++ b/net/quic/quic_session.cc @@ -675,12 +675,7 @@ ReliableQuicStream* QuicSession::GetOrCreateDynamicStream( } // Check if the new number of open streams would cause the number of // open streams to exceed the limit. - size_t num_open_incoming_streams = - FLAGS_quic_distinguish_incoming_outgoing_streams - ? GetNumOpenIncomingStreams() - : dynamic_stream_map_.size() - draining_streams_.size() + - locally_closed_streams_highest_offset_.size(); - if (num_open_incoming_streams >= max_open_incoming_streams()) { + if (GetNumOpenIncomingStreams() >= max_open_incoming_streams()) { if (connection()->version() <= QUIC_VERSION_27) { connection()->SendConnectionCloseWithDetails( QUIC_TOO_MANY_OPEN_STREAMS, "Old style stream rejection"); @@ -691,12 +686,7 @@ ReliableQuicStream* QuicSession::GetOrCreateDynamicStream( return nullptr; } - ReliableQuicStream* stream = CreateIncomingDynamicStream(stream_id); - if (stream == nullptr) { - return nullptr; - } - ActivateStream(stream); - return stream; + return CreateIncomingDynamicStream(stream_id); } void QuicSession::set_max_open_incoming_streams( diff --git a/net/quic/quic_session.h b/net/quic/quic_session.h index b4d7131..601edee 100644 --- a/net/quic/quic_session.h +++ b/net/quic/quic_session.h @@ -228,13 +228,14 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { protected: typedef std::unordered_map<QuicStreamId, ReliableQuicStream*> StreamMap; - // Creates a new stream, owned by the caller, to handle a peer-initiated - // stream. Returns nullptr and does error handling if the stream can not be - // created. + // Creates a new stream to handle a peer-initiated stream. + // Caller does not own the returned stream. + // Returns nullptr and does error handling if the stream can not be created. virtual ReliableQuicStream* CreateIncomingDynamicStream(QuicStreamId id) = 0; - // Create a new stream, owned by the caller, to handle a locally-initiated - // stream. Returns nullptr if max streams have already been opened. + // Create a new stream to handle a locally-initiated stream. + // Caller does not own the returned stream. + // Returns nullptr if max streams have already been opened. virtual ReliableQuicStream* CreateOutgoingDynamicStream( SpdyPriority priority) = 0; @@ -253,6 +254,7 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { // and |stream_id| is a peer-created id, then a new stream is created and // returned. However if |stream_id| is a locally-created id and no such stream // exists, the connection is closed. + // Caller does not own the returned stream. ReliableQuicStream* GetOrCreateDynamicStream(QuicStreamId stream_id); // Performs the work required to close |stream_id|. If |locally_reset| diff --git a/net/quic/quic_session_test.cc b/net/quic/quic_session_test.cc index d38b32b..a0da6b9 100644 --- a/net/quic/quic_session_test.cc +++ b/net/quic/quic_session_test.cc @@ -136,7 +136,9 @@ class TestSession : public QuicSpdySession { "Too many streams!"); return nullptr; } else { - return new TestStream(id, this); + TestStream* stream = new TestStream(id, this); + ActivateStream(stream); + return stream; } } diff --git a/net/quic/quic_spdy_stream.cc b/net/quic/quic_spdy_stream.cc index 3c68c13..75b160e 100644 --- a/net/quic/quic_spdy_stream.cc +++ b/net/quic/quic_spdy_stream.cc @@ -79,9 +79,6 @@ size_t QuicSpdyStream::WriteHeaders( size_t QuicSpdyStream::WriteTrailers( SpdyHeaderBlock trailer_block, QuicAckListenerInterface* ack_notifier_delegate) { - if (!FLAGS_quic_supports_trailers) { - return 0; - } if (fin_sent()) { QUIC_BUG << "Trailers cannot be sent after a FIN."; return 0; @@ -134,8 +131,7 @@ bool QuicSpdyStream::IsDoneReading() const { bool QuicSpdyStream::HasBytesToRead() const { bool headers_to_read = !decompressed_headers_.empty(); bool body_to_read = sequencer()->HasBytesToRead(); - bool trailers_to_read = - (FLAGS_quic_supports_trailers && !decompressed_trailers_.empty()); + bool trailers_to_read = !decompressed_trailers_.empty(); return headers_to_read || body_to_read || trailers_to_read; } @@ -157,7 +153,7 @@ void QuicSpdyStream::SetPriority(SpdyPriority priority) { } void QuicSpdyStream::OnStreamHeaders(StringPiece headers_data) { - if (!FLAGS_quic_supports_trailers || !headers_decompressed_) { + if (!headers_decompressed_) { headers_data.AppendToString(&decompressed_headers_); } else { DCHECK(!trailers_decompressed_); @@ -171,7 +167,7 @@ void QuicSpdyStream::OnStreamHeadersPriority(SpdyPriority priority) { } void QuicSpdyStream::OnStreamHeadersComplete(bool fin, size_t frame_len) { - if (!FLAGS_quic_supports_trailers || !headers_decompressed_) { + if (!headers_decompressed_) { OnInitialHeadersComplete(fin, frame_len); } else { OnTrailingHeadersComplete(fin, frame_len); @@ -251,9 +247,6 @@ bool QuicSpdyStream::FinishedReadingHeaders() const { } bool QuicSpdyStream::FinishedReadingTrailers() const { - if (!FLAGS_quic_supports_trailers) { - return true; - } // If no further trailing headers are expected, and the decompressed trailers // (if any) have been consumed, then reading of trailers is finished. bool no_more_trailers = fin_received() || trailers_decompressed_; diff --git a/net/quic/quic_spdy_stream.h b/net/quic/quic_spdy_stream.h index e7f640e..65304d6 100644 --- a/net/quic/quic_spdy_stream.h +++ b/net/quic/quic_spdy_stream.h @@ -107,8 +107,8 @@ class NET_EXPORT_PRIVATE QuicSpdyStream : public ReliableQuicStream { // Writes the trailers contained in |trailer_block| to the dedicated // headers stream. Trailers will always have the FIN set. - virtual size_t WriteTrailers(SpdyHeaderBlock trailer_block, - QuicAckListenerInterface* ack_notifier_delegate); + size_t WriteTrailers(SpdyHeaderBlock trailer_block, + QuicAckListenerInterface* ack_notifier_delegate); // Marks |bytes_consumed| of the headers data as consumed. void MarkHeadersConsumed(size_t bytes_consumed); diff --git a/net/quic/quic_spdy_stream_test.cc b/net/quic/quic_spdy_stream_test.cc index 090ad7c..54ebb8a 100644 --- a/net/quic/quic_spdy_stream_test.cc +++ b/net/quic/quic_spdy_stream_test.cc @@ -618,7 +618,6 @@ TEST_P(QuicSpdyStreamTest, StreamFlowControlFinNotBlocked) { TEST_P(QuicSpdyStreamTest, ReceivingTrailers) { // Test that receiving trailing headers from the peer works, and can be read // from the stream and consumed. - ValueRestore<bool> old_flag(&FLAGS_quic_supports_trailers, true); Initialize(kShouldProcessData); // Receive initial headers. @@ -648,7 +647,6 @@ TEST_P(QuicSpdyStreamTest, ReceivingTrailers) { TEST_P(QuicSpdyStreamTest, ReceivingTrailersWithoutFin) { // Test that received Trailers must always have the FIN set. - ValueRestore<bool> old_flag(&FLAGS_quic_supports_trailers, true); Initialize(kShouldProcessData); // Receive initial headers. @@ -669,7 +667,6 @@ TEST_P(QuicSpdyStreamTest, ReceivingTrailersWithoutFin) { TEST_P(QuicSpdyStreamTest, ReceivingTrailersAfterFin) { // If Trailers are sent, neither Headers nor Body should contain a FIN. - ValueRestore<bool> old_flag(&FLAGS_quic_supports_trailers, true); Initialize(kShouldProcessData); // Receive initial headers with FIN set. @@ -690,7 +687,6 @@ TEST_P(QuicSpdyStreamTest, ReceivingTrailersAfterFin) { TEST_P(QuicSpdyStreamTest, ReceivingTrailersAfterBodyWithFin) { // If body data are received with a FIN, no trailers should then arrive. - ValueRestore<bool> old_flag(&FLAGS_quic_supports_trailers, true); Initialize(kShouldProcessData); // Receive initial headers without FIN set. @@ -716,7 +712,6 @@ TEST_P(QuicSpdyStreamTest, ReceivingTrailersAfterBodyWithFin) { TEST_P(QuicSpdyStreamTest, ClosingStreamWithNoTrailers) { // Verify that a stream receiving headers, body, and no trailers is correctly // marked as done reading on consumption of headers and body. - ValueRestore<bool> old_flag(&FLAGS_quic_supports_trailers, true); Initialize(kShouldProcessData); // Receive and consume initial headers with FIN not set. @@ -736,7 +731,6 @@ TEST_P(QuicSpdyStreamTest, ClosingStreamWithNoTrailers) { TEST_P(QuicSpdyStreamTest, WritingTrailersSendsAFin) { // Test that writing trailers will send a FIN, as Trailers are the last thing // to be sent on a stream. - ValueRestore<bool> old_flag(&FLAGS_quic_supports_trailers, true); Initialize(kShouldProcessData); EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) .Times(AnyNumber()) @@ -758,7 +752,6 @@ TEST_P(QuicSpdyStreamTest, WritingTrailersSendsAFin) { TEST_P(QuicSpdyStreamTest, WritingTrailersFinalOffset) { // Test that when writing trailers, the trailers that are actually sent to the // peer contain the final offset field indicating last byte of data. - ValueRestore<bool> old_flag(&FLAGS_quic_supports_trailers, true); Initialize(kShouldProcessData); EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) .Times(AnyNumber()) @@ -786,7 +779,6 @@ TEST_P(QuicSpdyStreamTest, WritingTrailersFinalOffset) { TEST_P(QuicSpdyStreamTest, WritingTrailersClosesWriteSide) { // Test that if trailers are written after all other data has been written // (headers and body), that this closes the stream for writing. - ValueRestore<bool> old_flag(&FLAGS_quic_supports_trailers, true); Initialize(kShouldProcessData); EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) .Times(AnyNumber()) @@ -812,7 +804,6 @@ TEST_P(QuicSpdyStreamTest, WritingTrailersClosesWriteSide) { TEST_P(QuicSpdyStreamTest, WritingTrailersWithQueuedBytes) { // Test that the stream is not closed for writing when trailers are sent // while there are still body bytes queued. - ValueRestore<bool> old_flag(&FLAGS_quic_supports_trailers, true); Initialize(kShouldProcessData); EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) .Times(AnyNumber()) @@ -840,7 +831,6 @@ TEST_P(QuicSpdyStreamTest, WritingTrailersWithQueuedBytes) { TEST_P(QuicSpdyStreamTest, WritingTrailersAfterFIN) { // Test that it is not possible to write Trailers after a FIN has been sent. - ValueRestore<bool> old_flag(&FLAGS_quic_supports_trailers, true); Initialize(kShouldProcessData); EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) .Times(AnyNumber()) diff --git a/net/quic/quic_stream_sequencer.cc b/net/quic/quic_stream_sequencer.cc index 18f40a3..113f4ff 100644 --- a/net/quic/quic_stream_sequencer.cc +++ b/net/quic/quic_stream_sequencer.cc @@ -12,7 +12,6 @@ #include "net/quic/quic_bug_tracker.h" #include "net/quic/quic_clock.h" #include "net/quic/quic_flags.h" -#include "net/quic/quic_frame_list.h" #include "net/quic/quic_protocol.h" #include "net/quic/reliable_quic_stream.h" #include "net/quic/stream_sequencer_buffer.h" @@ -26,20 +25,14 @@ namespace net { QuicStreamSequencer::QuicStreamSequencer(ReliableQuicStream* quic_stream, const QuicClock* clock) : stream_(quic_stream), + buffered_frames_(kStreamReceiveWindowLimit), close_offset_(numeric_limits<QuicStreamOffset>::max()), blocked_(false), num_frames_received_(0), num_duplicate_frames_received_(0), num_early_frames_received_(0), clock_(clock), - ignore_read_data_(false) { - if (FLAGS_quic_use_stream_sequencer_buffer) { - buffered_frames_.reset( - new StreamSequencerBuffer(kStreamReceiveWindowLimit)); - } else { - buffered_frames_.reset(new QuicFrameList()); - } -} + ignore_read_data_(false) {} QuicStreamSequencer::~QuicStreamSequencer() {} @@ -63,7 +56,7 @@ void QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) { } } size_t bytes_written; - QuicErrorCode result = buffered_frames_->OnStreamData( + QuicErrorCode result = buffered_frames_.OnStreamData( byte_offset, StringPiece(frame.frame_buffer, frame.frame_length), clock_->ApproximateNow(), &bytes_written); @@ -80,7 +73,7 @@ void QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) { return; } - if (byte_offset > buffered_frames_->BytesConsumed()) { + if (byte_offset > buffered_frames_.BytesConsumed()) { ++num_early_frames_received_; } @@ -88,7 +81,7 @@ void QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) { return; } - if (byte_offset == buffered_frames_->BytesConsumed()) { + if (byte_offset == buffered_frames_.BytesConsumed()) { if (ignore_read_data_) { FlushBufferedFrames(); } else { @@ -117,7 +110,7 @@ bool QuicStreamSequencer::MaybeCloseStream() { } DVLOG(1) << "Passing up termination, as we've processed " - << buffered_frames_->BytesConsumed() << " of " << close_offset_ + << buffered_frames_.BytesConsumed() << " of " << close_offset_ << " bytes."; // This will cause the stream to consume the FIN. // Technically it's an error if |num_bytes_consumed| isn't exactly @@ -129,39 +122,39 @@ bool QuicStreamSequencer::MaybeCloseStream() { } else { stream_->OnDataAvailable(); } - buffered_frames_->Clear(); + buffered_frames_.Clear(); return true; } int QuicStreamSequencer::GetReadableRegions(iovec* iov, size_t iov_len) const { DCHECK(!blocked_); - return buffered_frames_->GetReadableRegions(iov, iov_len); + return buffered_frames_.GetReadableRegions(iov, iov_len); } bool QuicStreamSequencer::GetReadableRegion(iovec* iov, QuicTime* timestamp) const { DCHECK(!blocked_); - return buffered_frames_->GetReadableRegion(iov, timestamp); + return buffered_frames_.GetReadableRegion(iov, timestamp); } int QuicStreamSequencer::Readv(const struct iovec* iov, size_t iov_len) { DCHECK(!blocked_); - size_t bytes_read = buffered_frames_->Readv(iov, iov_len); + size_t bytes_read = buffered_frames_.Readv(iov, iov_len); stream_->AddBytesConsumed(bytes_read); return static_cast<int>(bytes_read); } bool QuicStreamSequencer::HasBytesToRead() const { - return buffered_frames_->HasBytesToRead(); + return buffered_frames_.HasBytesToRead(); } bool QuicStreamSequencer::IsClosed() const { - return buffered_frames_->BytesConsumed() >= close_offset_; + return buffered_frames_.BytesConsumed() >= close_offset_; } void QuicStreamSequencer::MarkConsumed(size_t num_bytes_consumed) { DCHECK(!blocked_); - bool result = buffered_frames_->MarkConsumed(num_bytes_consumed); + bool result = buffered_frames_.MarkConsumed(num_bytes_consumed); if (!result) { QUIC_BUG << "Invalid argument to MarkConsumed." << " expect to consume: " << num_bytes_consumed @@ -193,20 +186,20 @@ void QuicStreamSequencer::StopReading() { void QuicStreamSequencer::FlushBufferedFrames() { DCHECK(ignore_read_data_); - size_t bytes_flushed = buffered_frames_->FlushBufferedFrames(); + size_t bytes_flushed = buffered_frames_.FlushBufferedFrames(); DVLOG(1) << "Flushing buffered data at offset " - << buffered_frames_->BytesConsumed() << " length " << bytes_flushed + << buffered_frames_.BytesConsumed() << " length " << bytes_flushed << " for stream " << stream_->id(); stream_->AddBytesConsumed(bytes_flushed); MaybeCloseStream(); } size_t QuicStreamSequencer::NumBytesBuffered() const { - return buffered_frames_->BytesBuffered(); + return buffered_frames_.BytesBuffered(); } QuicStreamOffset QuicStreamSequencer::NumBytesConsumed() const { - return buffered_frames_->BytesConsumed(); + return buffered_frames_.BytesConsumed(); } } // namespace net diff --git a/net/quic/quic_stream_sequencer.h b/net/quic/quic_stream_sequencer.h index 65d99ba..1d06d2f 100644 --- a/net/quic/quic_stream_sequencer.h +++ b/net/quic/quic_stream_sequencer.h @@ -11,8 +11,10 @@ #include <string> #include "base/macros.h" -#include "net/quic/quic_frame_list.h" #include "net/quic/quic_protocol.h" +#include "net/quic/stream_sequencer_buffer.h" + +using std::string; namespace net { @@ -114,7 +116,7 @@ class NET_EXPORT_PRIVATE QuicStreamSequencer { ReliableQuicStream* stream_; // Stores received data in offset order. - scoped_ptr<QuicStreamSequencerBufferInterface> buffered_frames_; + StreamSequencerBuffer buffered_frames_; // The offset, if any, we got a stream termination for. When this many bytes // have been processed, the sequencer will be closed. diff --git a/net/quic/quic_stream_sequencer_test.cc b/net/quic/quic_stream_sequencer_test.cc index a7e08ad..4d0f0c0 100644 --- a/net/quic/quic_stream_sequencer_test.cc +++ b/net/quic/quic_stream_sequencer_test.cc @@ -57,13 +57,8 @@ namespace { static const char kPayload[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; -class QuicStreamSequencerTest : public ::testing::TestWithParam<bool> { +class QuicStreamSequencerTest : public ::testing::Test { public: - void SetUp() override { - FLAGS_quic_use_stream_sequencer_buffer = GetParam(); - sequencer_.reset(new QuicStreamSequencer(&stream_, &clock_)); - } - void ConsumeData(size_t num_bytes) { char buffer[1024]; ASSERT_GT(arraysize(buffer), num_bytes); @@ -77,7 +72,8 @@ class QuicStreamSequencerTest : public ::testing::TestWithParam<bool> { QuicStreamSequencerTest() : connection_(new MockConnection(&helper_, Perspective::IS_CLIENT)), session_(connection_), - stream_(&session_, 1) {} + stream_(&session_, 1), + sequencer_(new QuicStreamSequencer(&stream_, &clock_)) {} // Verify that the data in first region match with the expected[0]. bool VerifyReadableRegion(const vector<string>& expected) { @@ -101,27 +97,13 @@ class QuicStreamSequencerTest : public ::testing::TestWithParam<bool> { bool VerifyIovecs(iovec* iovecs, size_t num_iovecs, const vector<string>& expected) { - if (!FLAGS_quic_use_stream_sequencer_buffer) { - if (expected.size() != num_iovecs) { - LOG(ERROR) << "Incorrect number of iovecs. Expected: " - << expected.size() << " Actual: " << num_iovecs; + int start_position = 0; + for (size_t i = 0; i < num_iovecs; ++i) { + if (!VerifyIovec(iovecs[i], + expected[0].substr(start_position, iovecs[i].iov_len))) { return false; } - - for (size_t i = 0; i < num_iovecs; ++i) { - if (!VerifyIovec(iovecs[i], expected[i])) { - return false; - } - } - } else { - int start_position = 0; - for (size_t i = 0; i < num_iovecs; ++i) { - if (!VerifyIovec(iovecs[i], expected[0].substr(start_position, - iovecs[i].iov_len))) { - return false; - } - start_position += iovecs[i].iov_len; - } + start_position += iovecs[i].iov_len; } return true; } @@ -172,13 +154,9 @@ class QuicStreamSequencerTest : public ::testing::TestWithParam<bool> { scoped_ptr<QuicStreamSequencer> sequencer_; }; -INSTANTIATE_TEST_CASE_P(QuicStreamSequencerTests, - QuicStreamSequencerTest, - ::testing::Values(false, true)); - // TODO(rch): reorder these tests so they build on each other. -TEST_P(QuicStreamSequencerTest, RejectOldFrame) { +TEST_F(QuicStreamSequencerTest, RejectOldFrame) { EXPECT_CALL(stream_, OnDataAvailable()) .WillOnce(testing::Invoke( CreateFunctor(&QuicStreamSequencerTest::ConsumeData, @@ -195,7 +173,7 @@ TEST_P(QuicStreamSequencerTest, RejectOldFrame) { EXPECT_EQ(0u, NumBufferedBytes()); } -TEST_P(QuicStreamSequencerTest, RejectBufferedFrame) { +TEST_F(QuicStreamSequencerTest, RejectBufferedFrame) { EXPECT_CALL(stream_, OnDataAvailable()); OnFrame(0, "abc"); @@ -208,7 +186,7 @@ TEST_P(QuicStreamSequencerTest, RejectBufferedFrame) { EXPECT_EQ(3u, NumBufferedBytes()); } -TEST_P(QuicStreamSequencerTest, FullFrameConsumed) { +TEST_F(QuicStreamSequencerTest, FullFrameConsumed) { EXPECT_CALL(stream_, OnDataAvailable()) .WillOnce(testing::Invoke( CreateFunctor(&QuicStreamSequencerTest::ConsumeData, @@ -219,7 +197,7 @@ TEST_P(QuicStreamSequencerTest, FullFrameConsumed) { EXPECT_EQ(3u, sequencer_->NumBytesConsumed()); } -TEST_P(QuicStreamSequencerTest, BlockedThenFullFrameConsumed) { +TEST_F(QuicStreamSequencerTest, BlockedThenFullFrameConsumed) { sequencer_->SetBlockedUntilFlush(); OnFrame(0, "abc"); @@ -243,7 +221,7 @@ TEST_P(QuicStreamSequencerTest, BlockedThenFullFrameConsumed) { EXPECT_TRUE(sequencer_->IsClosed()); } -TEST_P(QuicStreamSequencerTest, BlockedThenFullFrameAndFinConsumed) { +TEST_F(QuicStreamSequencerTest, BlockedThenFullFrameAndFinConsumed) { sequencer_->SetBlockedUntilFlush(); OnFinFrame(0, "abc"); @@ -261,7 +239,7 @@ TEST_P(QuicStreamSequencerTest, BlockedThenFullFrameAndFinConsumed) { EXPECT_EQ(3u, sequencer_->NumBytesConsumed()); } -TEST_P(QuicStreamSequencerTest, EmptyFrame) { +TEST_F(QuicStreamSequencerTest, EmptyFrame) { EXPECT_CALL(stream_, CloseConnectionWithDetails(QUIC_INVALID_STREAM_FRAME, _)); OnFrame(0, ""); @@ -269,14 +247,14 @@ TEST_P(QuicStreamSequencerTest, EmptyFrame) { EXPECT_EQ(0u, sequencer_->NumBytesConsumed()); } -TEST_P(QuicStreamSequencerTest, EmptyFinFrame) { +TEST_F(QuicStreamSequencerTest, EmptyFinFrame) { EXPECT_CALL(stream_, OnDataAvailable()); OnFinFrame(0, ""); EXPECT_EQ(0u, NumBufferedBytes()); EXPECT_EQ(0u, sequencer_->NumBytesConsumed()); } -TEST_P(QuicStreamSequencerTest, PartialFrameConsumed) { +TEST_F(QuicStreamSequencerTest, PartialFrameConsumed) { EXPECT_CALL(stream_, OnDataAvailable()) .WillOnce(testing::Invoke( CreateFunctor(&QuicStreamSequencerTest::ConsumeData, @@ -287,7 +265,7 @@ TEST_P(QuicStreamSequencerTest, PartialFrameConsumed) { EXPECT_EQ(2u, sequencer_->NumBytesConsumed()); } -TEST_P(QuicStreamSequencerTest, NextxFrameNotConsumed) { +TEST_F(QuicStreamSequencerTest, NextxFrameNotConsumed) { EXPECT_CALL(stream_, OnDataAvailable()); OnFrame(0, "abc"); @@ -296,14 +274,14 @@ TEST_P(QuicStreamSequencerTest, NextxFrameNotConsumed) { EXPECT_EQ(0, sequencer_->num_early_frames_received()); } -TEST_P(QuicStreamSequencerTest, FutureFrameNotProcessed) { +TEST_F(QuicStreamSequencerTest, FutureFrameNotProcessed) { OnFrame(3, "abc"); EXPECT_EQ(3u, NumBufferedBytes()); EXPECT_EQ(0u, sequencer_->NumBytesConsumed()); EXPECT_EQ(1, sequencer_->num_early_frames_received()); } -TEST_P(QuicStreamSequencerTest, OutOfOrderFrameProcessed) { +TEST_F(QuicStreamSequencerTest, OutOfOrderFrameProcessed) { // Buffer the first OnFrame(6, "ghi"); EXPECT_EQ(3u, NumBufferedBytes()); @@ -328,7 +306,7 @@ TEST_P(QuicStreamSequencerTest, OutOfOrderFrameProcessed) { EXPECT_EQ(0u, NumBufferedBytes()); } -TEST_P(QuicStreamSequencerTest, BasicHalfCloseOrdered) { +TEST_F(QuicStreamSequencerTest, BasicHalfCloseOrdered) { InSequence s; EXPECT_CALL(stream_, OnDataAvailable()) @@ -340,7 +318,7 @@ TEST_P(QuicStreamSequencerTest, BasicHalfCloseOrdered) { EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get())); } -TEST_P(QuicStreamSequencerTest, BasicHalfCloseUnorderedWithFlush) { +TEST_F(QuicStreamSequencerTest, BasicHalfCloseUnorderedWithFlush) { OnFinFrame(6, ""); EXPECT_EQ(6u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get())); @@ -354,7 +332,7 @@ TEST_P(QuicStreamSequencerTest, BasicHalfCloseUnorderedWithFlush) { EXPECT_TRUE(sequencer_->IsClosed()); } -TEST_P(QuicStreamSequencerTest, BasicHalfUnordered) { +TEST_F(QuicStreamSequencerTest, BasicHalfUnordered) { OnFinFrame(3, ""); EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get())); @@ -367,7 +345,7 @@ TEST_P(QuicStreamSequencerTest, BasicHalfUnordered) { EXPECT_TRUE(sequencer_->IsClosed()); } -TEST_P(QuicStreamSequencerTest, TerminateWithReadv) { +TEST_F(QuicStreamSequencerTest, TerminateWithReadv) { char buffer[3]; OnFinFrame(3, ""); @@ -384,7 +362,7 @@ TEST_P(QuicStreamSequencerTest, TerminateWithReadv) { EXPECT_TRUE(sequencer_->IsClosed()); } -TEST_P(QuicStreamSequencerTest, MutipleOffsets) { +TEST_F(QuicStreamSequencerTest, MutipleOffsets) { OnFinFrame(3, ""); EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get())); @@ -439,7 +417,7 @@ class QuicSequencerRandomTest : public QuicStreamSequencerTest { // All frames are processed as soon as we have sequential data. // Infinite buffering, so all frames are acked right away. -TEST_P(QuicSequencerRandomTest, RandomFramesNoDroppingNoBackup) { +TEST_F(QuicSequencerRandomTest, RandomFramesNoDroppingNoBackup) { InSequence s; EXPECT_CALL(stream_, OnDataAvailable()) .Times(AnyNumber()) @@ -458,7 +436,7 @@ TEST_P(QuicSequencerRandomTest, RandomFramesNoDroppingNoBackup) { EXPECT_EQ(kPayload, output_); } -TEST_P(QuicSequencerRandomTest, RandomFramesNoDroppingBackup) { +TEST_F(QuicSequencerRandomTest, RandomFramesNoDroppingBackup) { char buffer[10]; iovec iov[2]; iov[0].iov_base = &buffer[0]; @@ -504,7 +482,7 @@ TEST_P(QuicSequencerRandomTest, RandomFramesNoDroppingBackup) { } // Same as above, just using a different method for reading. -TEST_P(QuicStreamSequencerTest, MarkConsumed) { +TEST_F(QuicStreamSequencerTest, MarkConsumed) { InSequence s; EXPECT_CALL(stream_, OnDataAvailable()); @@ -516,24 +494,14 @@ TEST_P(QuicStreamSequencerTest, MarkConsumed) { EXPECT_EQ(9u, sequencer_->NumBytesBuffered()); // Peek into the data. - vector<string> expected; - if (FLAGS_quic_use_stream_sequencer_buffer) { - expected = vector<string>{"abcdefghi"}; - } else { - expected = vector<string>{"abc", "def", "ghi"}; - } + vector<string> expected = {"abcdefghi"}; ASSERT_TRUE(VerifyReadableRegions(expected)); // Consume 1 byte. sequencer_->MarkConsumed(1); EXPECT_EQ(1u, stream_.flow_controller()->bytes_consumed()); // Verify data. - vector<string> expected2; - if (FLAGS_quic_use_stream_sequencer_buffer) { - expected2 = vector<string>{"bcdefghi"}; - } else { - expected2 = vector<string>{"bc", "def", "ghi"}; - } + vector<string> expected2 = {"bcdefghi"}; ASSERT_TRUE(VerifyReadableRegions(expected2)); EXPECT_EQ(8u, sequencer_->NumBytesBuffered()); @@ -541,12 +509,7 @@ TEST_P(QuicStreamSequencerTest, MarkConsumed) { sequencer_->MarkConsumed(2); EXPECT_EQ(3u, stream_.flow_controller()->bytes_consumed()); // Verify data. - vector<string> expected3; - if (FLAGS_quic_use_stream_sequencer_buffer) { - expected3 = vector<string>{"defghi"}; - } else { - expected3 = vector<string>{"def", "ghi"}; - } + vector<string> expected3 = {"defghi"}; ASSERT_TRUE(VerifyReadableRegions(expected3)); EXPECT_EQ(6u, sequencer_->NumBytesBuffered()); @@ -559,7 +522,7 @@ TEST_P(QuicStreamSequencerTest, MarkConsumed) { EXPECT_EQ(1u, sequencer_->NumBytesBuffered()); } -TEST_P(QuicStreamSequencerTest, MarkConsumedError) { +TEST_F(QuicStreamSequencerTest, MarkConsumedError) { EXPECT_CALL(stream_, OnDataAvailable()); OnFrame(0, "abc"); @@ -578,7 +541,7 @@ TEST_P(QuicStreamSequencerTest, MarkConsumedError) { " expect to consume: 4, but not enough bytes available."); } -TEST_P(QuicStreamSequencerTest, MarkConsumedWithMissingPacket) { +TEST_F(QuicStreamSequencerTest, MarkConsumedWithMissingPacket) { InSequence s; EXPECT_CALL(stream_, OnDataAvailable()); @@ -587,12 +550,7 @@ TEST_P(QuicStreamSequencerTest, MarkConsumedWithMissingPacket) { // Missing packet: 6, ghi. OnFrame(9, "jkl"); - vector<string> expected; - if (FLAGS_quic_use_stream_sequencer_buffer) { - expected = vector<string>{"abcdef"}; - } else { - expected = vector<string>{"abc", "def"}; - } + vector<string> expected = {"abcdef"}; ASSERT_TRUE(VerifyReadableRegions(expected)); sequencer_->MarkConsumed(6); @@ -646,7 +604,7 @@ TEST(QuicFrameListTest, FrameOverlapsBufferedData) { QuicStreamFrame(1, false, kBufferedOffset + kBufferedDataLength, data))); } -TEST_P(QuicStreamSequencerTest, DontAcceptOverlappingFrames) { +TEST_F(QuicStreamSequencerTest, DontAcceptOverlappingFrames) { // The peer should never send us non-identical stream frames which contain // overlapping byte ranges - if they do, we close the connection. @@ -659,7 +617,7 @@ TEST_P(QuicStreamSequencerTest, DontAcceptOverlappingFrames) { sequencer_->OnStreamFrame(frame2); } -TEST_P(QuicStreamSequencerTest, InOrderTimestamps) { +TEST_F(QuicStreamSequencerTest, InOrderTimestamps) { // This test verifies that timestamps returned by // GetReadableRegion() are in the correct sequence when frames // arrive at the sequencer in order. @@ -700,7 +658,7 @@ TEST_P(QuicStreamSequencerTest, InOrderTimestamps) { EXPECT_EQ(0u, sequencer_->NumBytesBuffered()); } -TEST_P(QuicStreamSequencerTest, OutOfOrderTimestamps) { +TEST_F(QuicStreamSequencerTest, OutOfOrderTimestamps) { // This test verifies that timestamps returned by // GetReadableRegion() are in the correct sequence when frames // arrive at the sequencer out of order. diff --git a/net/quic/test_tools/quic_stream_sequencer_peer.cc b/net/quic/test_tools/quic_stream_sequencer_peer.cc index 290e2ea..1a6eae7 100644 --- a/net/quic/test_tools/quic_stream_sequencer_peer.cc +++ b/net/quic/test_tools/quic_stream_sequencer_peer.cc @@ -15,7 +15,7 @@ namespace test { // static size_t QuicStreamSequencerPeer::GetNumBufferedBytes( QuicStreamSequencer* sequencer) { - return sequencer->buffered_frames_->BytesBuffered(); + return sequencer->buffered_frames_.BytesBuffered(); } // static diff --git a/net/spdy/buffered_spdy_framer.cc b/net/spdy/buffered_spdy_framer.cc index bdad0db..e76b83d 100644 --- a/net/spdy/buffered_spdy_framer.cc +++ b/net/spdy/buffered_spdy_framer.cc @@ -201,6 +201,10 @@ void BufferedSpdyFramer::OnStreamFrameData(SpdyStreamId stream_id, visitor_->OnStreamFrameData(stream_id, data, len, fin); } +void BufferedSpdyFramer::OnStreamEnd(SpdyStreamId stream_id) { + LOG(DFATAL) << "Unimplemented"; +} + void BufferedSpdyFramer::OnStreamPadding(SpdyStreamId stream_id, size_t len) { visitor_->OnStreamPadding(stream_id, len); } diff --git a/net/spdy/buffered_spdy_framer.h b/net/spdy/buffered_spdy_framer.h index f213998..65460bb 100644 --- a/net/spdy/buffered_spdy_framer.h +++ b/net/spdy/buffered_spdy_framer.h @@ -181,6 +181,7 @@ class NET_EXPORT_PRIVATE BufferedSpdyFramer const char* data, size_t len, bool fin) override; + void OnStreamEnd(SpdyStreamId stream_id) override; void OnStreamPadding(SpdyStreamId stream_id, size_t len) override; SpdyHeadersHandlerInterface* OnHeaderFrameStart( SpdyStreamId stream_id) override; diff --git a/net/spdy/mock_spdy_framer_visitor.h b/net/spdy/mock_spdy_framer_visitor.h index 85bf5ba..d330c26 100644 --- a/net/spdy/mock_spdy_framer_visitor.h +++ b/net/spdy/mock_spdy_framer_visitor.h @@ -28,6 +28,7 @@ class MockSpdyFramerVisitor : public SpdyFramerVisitorInterface { const char* data, size_t len, bool fin)); + MOCK_METHOD1(OnStreamEnd, void(SpdyStreamId stream_id)); MOCK_METHOD2(OnStreamPadding, void(SpdyStreamId stream_id, size_t len)); MOCK_METHOD1(OnHeaderFrameStart, SpdyHeadersHandlerInterface*(SpdyStreamId stream_id)); diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h index ac8543a..6fce783 100644 --- a/net/spdy/spdy_framer.h +++ b/net/spdy/spdy_framer.h @@ -119,6 +119,10 @@ class NET_EXPORT_PRIVATE SpdyFramerVisitorInterface { size_t len, bool fin) = 0; + // Called when the other side has finished sending data on this stream. + // |stream_id| The stream that was receivin data. + virtual void OnStreamEnd(SpdyStreamId stream_id) = 0; + // Called when padding is received (padding length field or padding octets). // |stream_id| The stream receiving data. // |len| The number of padding octets. diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_test.cc index 34a8a21..2dcc855 100644 --- a/net/spdy/spdy_framer_test.cc +++ b/net/spdy/spdy_framer_test.cc @@ -97,6 +97,8 @@ class SpdyFramerTestUtil { LOG(FATAL); } + void OnStreamEnd(SpdyStreamId stream_id) override { LOG(FATAL); } + void OnStreamPadding(SpdyStreamId stream_id, size_t len) override { LOG(FATAL); } @@ -325,6 +327,10 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface, LOG(INFO) << "\", " << len << ")\n"; } + void OnStreamEnd(SpdyStreamId stream_id) override { + LOG(DFATAL) << "Unimplemented."; + } + void OnStreamPadding(SpdyStreamId stream_id, size_t len) override { EXPECT_EQ(header_stream_id_, stream_id); data_bytes_ += len; diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc index ff07c60..08e2c8b 100644 --- a/net/tools/quic/end_to_end_test.cc +++ b/net/tools/quic/end_to_end_test.cc @@ -266,7 +266,6 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> { client_supported_versions_ = GetParam().client_supported_versions; server_supported_versions_ = GetParam().server_supported_versions; negotiated_version_ = GetParam().negotiated_version; - FLAGS_enable_quic_fec = GetParam().use_fec; VLOG(1) << "Using Configuration: " << GetParam(); @@ -371,10 +370,6 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> { StartServer(); client_.reset(CreateQuicClient(client_writer_)); - if (GetParam().use_fec) { - // Set FecPolicy to always protect data on all streams. - client_->SetFecPolicy(FEC_PROTECT_ALWAYS); - } static EpollEvent event(EPOLLOUT, false); client_writer_->Initialize( reinterpret_cast<QuicEpollConnectionHelper*>( @@ -998,8 +993,7 @@ TEST_P(EndToEndTest, CorrectlyConfiguredFec) { client_->client()->WaitForCryptoHandshakeConfirmed(); server_thread_->WaitForCryptoHandshakeConfirmed(); - FecPolicy expected_policy = - GetParam().use_fec ? FEC_PROTECT_ALWAYS : FEC_PROTECT_OPTIONAL; + FecPolicy expected_policy = FEC_PROTECT_OPTIONAL; // Verify that server's FEC configuration is correct. server_thread_->Pause(); @@ -1508,7 +1502,7 @@ TEST_P(EndToEndTest, ConnectionMigrationClientPortChanged) { // Create a new socket before closing the old one, which will result in a new // ephemeral port. - QuicClientPeer::CreateUDPSocket(client_->client()); + QuicClientPeer::CreateUDPSocketAndBind(client_->client()); // The packet writer needs to be updated to use the new FD. client_->client()->CreateQuicPacketWriter(); @@ -2153,7 +2147,6 @@ TEST_P(EndToEndTest, LargePostEarlyResponse) { TEST_P(EndToEndTest, Trailers) { // Test sending and receiving HTTP/2 Trailers (trailing HEADERS frames). - ValueRestore<bool> old_flag(&FLAGS_quic_supports_trailers, true); ASSERT_TRUE(Initialize()); client_->client()->WaitForCryptoHandshakeConfirmed(); @@ -2180,8 +2173,55 @@ TEST_P(EndToEndTest, Trailers) { EXPECT_EQ(trailers, client_->response_trailers()); } -TEST_P(EndToEndTest, ServerPush) { - FLAGS_quic_supports_push_promise = true; +class EndToEndTestServerPush : public EndToEndTest { + protected: + const size_t kNumMaxStreams = 10; + + EndToEndTestServerPush() : EndToEndTest() { + FLAGS_quic_supports_push_promise = true; + FLAGS_quic_different_max_num_open_streams = true; + client_config_.SetMaxStreamsPerConnection(kNumMaxStreams, kNumMaxStreams); + } + + // Add a request with its response and |num_resources| push resources into + // cache. + // If |resource_size| == 0, response body of push resources use default string + // concatenating with resource url. Otherwise, generate a string of + // |resource_size| as body. + void AddRequestAndResponseWithServerPush(string host, + string path, + string response_body, + string* push_urls, + const size_t num_resources, + const size_t resource_size) { + bool use_large_response = resource_size != 0; + string large_resource; + if (use_large_response) { + // Generate a response common body larger than flow control window for + // push response. + test::GenerateBody(&large_resource, resource_size); + } + list<QuicInMemoryCache::ServerPushInfo> push_resources; + for (size_t i = 0; i < num_resources; ++i) { + string url = push_urls[i]; + GURL resource_url(url); + string body = use_large_response + ? large_resource + : "This is server push response body for " + url; + SpdyHeaderBlock response_headers; + response_headers[":version"] = "HTTP/1.1"; + response_headers[":status"] = "200"; + response_headers["content-length"] = IntToString(body.size()); + push_resources.push_back(QuicInMemoryCache::ServerPushInfo( + resource_url, response_headers, kV3LowestPriority, body)); + } + + QuicInMemoryCache::GetInstance()->AddSimpleResponseWithServerPushResources( + host, path, 200, response_body, push_resources); + } +}; + +TEST_P(EndToEndTestServerPush, ServerPush) { ASSERT_TRUE(Initialize()); client_->client()->WaitForCryptoHandshakeConfirmed(); @@ -2189,41 +2229,198 @@ TEST_P(EndToEndTest, ServerPush) { SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); SetReorderPercentage(30); - // Add a response with headers, body, and trailers. + // Add a response with headers, body, and push resources. const string kBody = "body content"; - - list<QuicInMemoryCache::ServerPushInfo> push_resources; - + size_t kNumResources = 4; string push_urls[] = { "https://google.com/font.woff", "https://google.com/script.js", "https://fonts.google.com/font.woff", "https://google.com/logo-hires.jpg", }; + AddRequestAndResponseWithServerPush("example.com", "/push_example", kBody, + push_urls, kNumResources, 0); + + client_->client()->set_response_listener(new TestResponseListener); + + DVLOG(1) << "send request for /push_example"; + EXPECT_EQ(kBody, client_->SendSynchronousRequest( + "https://example.com/push_example")); for (const string& url : push_urls) { - GURL resource_url(url); - string body = "This is server push response body for " + url; - SpdyHeaderBlock response_headers; - response_headers[":version"] = "HTTP/1.1"; - response_headers[":status"] = "200"; - response_headers["content-length"] = IntToString(body.size()); - push_resources.push_back(QuicInMemoryCache::ServerPushInfo( - resource_url, response_headers, kV3LowestPriority, body)); + DVLOG(1) << "send request for pushed stream on url " << url; + string expected_body = "This is server push response body for " + url; + string response_body = client_->SendSynchronousRequest(url); + DVLOG(1) << "response body " << response_body; + EXPECT_EQ(expected_body, response_body); } +} + +TEST_P(EndToEndTestServerPush, ServerPushUnderLimit) { + // Tests that sending a request which has 4 push resources will trigger server + // to push those 4 resources and client can handle pushed resources and match + // them with requests later. + ASSERT_TRUE(Initialize()); - QuicInMemoryCache::GetInstance()->AddSimpleResponseWithServerPushResources( - "google.com", "/push_example", 200, kBody, push_resources); + client_->client()->WaitForCryptoHandshakeConfirmed(); + + // Set reordering to ensure that body arriving before PUSH_PROMISE is ok. + SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); + SetReorderPercentage(30); + // Add a response with headers, body, and push resources. + const string kBody = "body content"; + size_t const kNumResources = 4; + string push_urls[] = { + "https://example.com/font.woff", "https://example.com/script.js", + "https://fonts.example.com/font.woff", + "https://example.com/logo-hires.jpg", + }; + AddRequestAndResponseWithServerPush("example.com", "/push_example", kBody, + push_urls, kNumResources, 0); client_->client()->set_response_listener(new TestResponseListener); - DVLOG(1) << "send request for /push_example"; - EXPECT_EQ(kBody, - client_->SendSynchronousRequest("https://google.com/push_example")); - for (const string& url : push_urls) { + // Send the first request: this will trigger the server to send all the push + // resources associated with this request, and these will be cached by the + // client. + EXPECT_EQ(kBody, client_->SendSynchronousRequest( + "https://example.com/push_example")); + EXPECT_EQ(1u + kNumResources, client_->num_responses()); + + for (string url : push_urls) { + // Sending subsequent requesets will not actually send anything on the wire, + // as the responses are already in the client's cache. DVLOG(1) << "send request for pushed stream on url " << url; string expected_body = "This is server push response body for " + url; string response_body = client_->SendSynchronousRequest(url); DVLOG(1) << "response body " << response_body; EXPECT_EQ(expected_body, response_body); } + // Expect only original request has been sent and push responses have been + // received as normal response. + EXPECT_EQ(1u, client_->num_requests()); +} + +TEST_P(EndToEndTestServerPush, ServerPushOverLimitNonBlocking) { + // Tests that when streams are not blocked by flow control or congestion + // control, pushing even more resources than max number of open outgoing + // streams should still work because all response streams get closed + // immediately after pushing resources. + ASSERT_TRUE(Initialize()); + client_->client()->WaitForCryptoHandshakeConfirmed(); + + // Set reordering to ensure that body arriving before PUSH_PROMISE is ok. + SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); + SetReorderPercentage(30); + + // Add a response with headers, body, and push resources. + const string kBody = "body content"; + + // One more resource than max number of outgoing stream of this session. + const size_t kNumResources = 1 + kNumMaxStreams; // 11. + string push_urls[11]; + for (uint32_t i = 0; i < kNumResources; ++i) { + push_urls[i] = "https://example.com/push_resources" + base::UintToString(i); + } + AddRequestAndResponseWithServerPush("example.com", "/push_example", kBody, + push_urls, kNumResources, 0); + client_->client()->set_response_listener(new TestResponseListener); + + // Send the first request: this will trigger the server to send all the push + // resources associated with this request, and these will be cached by the + // client. + EXPECT_EQ(kBody, client_->SendSynchronousRequest( + "https://example.com/push_example")); + // The responses to the original request and all the promised resources + // should have been received. + EXPECT_EQ(12u, client_->num_responses()); + + for (const string& url : push_urls) { + // Sending subsequent requesets will not actually send anything on the wire, + // as the responses are already in the client's cache. + EXPECT_EQ("This is server push response body for " + url, + client_->SendSynchronousRequest(url)); + } + + // Only 1 request should have been sent. + EXPECT_EQ(1u, client_->num_requests()); +} + +TEST_P(EndToEndTestServerPush, ServerPushOverLimitWithBlocking) { + // Tests that when server tries to send more large resources(large enough to + // be blocked by flow control window or congestion control window) than max + // open outgoing streams , server can open upto max number of outgoing + // streams for them, and the rest will be queued up. + + // Reset flow control windows. + size_t kFlowControlWnd = 20 * 1024; // 20KB. + // Response body is larger than 1 flow controlblock window. + size_t kBodySize = kFlowControlWnd * 2; + set_client_initial_stream_flow_control_receive_window(kFlowControlWnd); + // Make sure conntection level flow control window is large enough not to + // block data being sent out though they will be blocked by stream level one. + set_client_initial_session_flow_control_receive_window( + kBodySize * kNumMaxStreams + 1024); + + ASSERT_TRUE(Initialize()); + client_->client()->WaitForCryptoHandshakeConfirmed(); + + // Set reordering to ensure that body arriving before PUSH_PROMISE is ok. + SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); + SetReorderPercentage(30); + + // Add a response with headers, body, and push resources. + const string kBody = "body content"; + + const size_t kNumResources = kNumMaxStreams + 1; + string push_urls[11]; + for (uint32_t i = 0; i < kNumResources; ++i) { + push_urls[i] = "http://example.com/push_resources" + base::UintToString(i); + } + AddRequestAndResponseWithServerPush("example.com", "/push_example", kBody, + push_urls, kNumResources, kBodySize); + + client_->client()->set_response_listener(new TestResponseListener); + + client_->SendRequest("https://example.com/push_example"); + + // Pause after the first response arrives. + while (!client_->response_complete()) { + // Because of priority, the first response arrived should be to original + // request. + client_->WaitForResponse(); + } + + // Check server session to see if it has max number of outgoing streams opened + // though more resources need to be pushed. + server_thread_->Pause(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + ASSERT_EQ(1u, dispatcher->session_map().size()); + QuicSession* session = dispatcher->session_map().begin()->second; + EXPECT_EQ(kNumMaxStreams, session->GetNumOpenOutgoingStreams()); + server_thread_->Resume(); + + EXPECT_EQ(1u, client_->num_requests()); + EXPECT_EQ(1u, client_->num_responses()); + EXPECT_EQ(kBody, client_->response_body()); + + // "Send" request for a promised resources will not really send out it because + // its response is being pushed(but blocked). And the following ack and + // flow control behavior of SendSynchronousRequests() + // will unblock the stream to finish receiving response. + client_->SendSynchronousRequest(push_urls[0]); + EXPECT_EQ(1u, client_->num_requests()); + EXPECT_EQ(2u, client_->num_responses()); + + // Do same thing for the rest 10 resources. + for (uint32_t i = 1; i < kNumResources; ++i) { + client_->SendSynchronousRequest(push_urls[i]); + } + + // Because of server push, client gets all pushed resources without actually + // sending requests for them. + EXPECT_EQ(1u, client_->num_requests()); + // Including response to original request, 12 responses in total were + // recieved. + EXPECT_EQ(12u, client_->num_responses()); } } // namespace diff --git a/net/tools/quic/quic_client.cc b/net/tools/quic/quic_client.cc index b8472d0..56514ae 100644 --- a/net/tools/quic/quic_client.cc +++ b/net/tools/quic/quic_client.cc @@ -74,9 +74,10 @@ QuicClient::QuicClient(IPEndPoint server_address, initialized_(false), packets_dropped_(0), overflow_supported_(false), + use_recvmmsg_(false), store_response_(false), latest_response_code_(-1), - packet_reader_(CreateQuicPacketReader()) {} + packet_reader_(new QuicPacketReader()) {} QuicClient::~QuicClient() { if (connected()) { @@ -93,6 +94,14 @@ QuicClient::~QuicClient() { bool QuicClient::Initialize() { QuicClientBase::Initialize(); +#if MMSG_MORE + use_recvmmsg_ = true; +#endif + + set_num_sent_client_hellos(0); + set_num_stateless_rejects_received(0); + set_connection_error(QUIC_NO_ERROR); + // If an initial flow control window has not explicitly been set, then use the // same values that Chrome uses. const uint32_t kSessionMaxRecvWindowSize = 15 * 1024 * 1024; // 15 MB @@ -109,7 +118,7 @@ bool QuicClient::Initialize() { epoll_server_->set_timeout_in_us(50 * 1000); - if (!CreateUDPSocket()) { + if (!CreateUDPSocketAndBind()) { return false; } @@ -129,41 +138,17 @@ QuicClient::QuicDataToResend::~QuicDataToResend() { } } -bool QuicClient::CreateUDPSocket() { - int address_family = server_address_.GetSockAddrFamily(); - int fd = socket(address_family, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); +bool QuicClient::CreateUDPSocketAndBind() { + int fd = + QuicSocketUtils::CreateUDPSocket(server_address_, &overflow_supported_); if (fd < 0) { - LOG(ERROR) << "CreateSocket() failed: " << strerror(errno); - return false; - } - - int get_overflow = 1; - int rc = setsockopt(fd, SOL_SOCKET, SO_RXQ_OVFL, &get_overflow, - sizeof(get_overflow)); - if (rc < 0) { - DLOG(WARNING) << "Socket overflow detection not supported"; - } else { - overflow_supported_ = true; - } - - if (!QuicSocketUtils::SetReceiveBufferSize(fd, kDefaultSocketReceiveBuffer)) { - return false; - } - - if (!QuicSocketUtils::SetSendBufferSize(fd, kDefaultSocketReceiveBuffer)) { - return false; - } - - rc = QuicSocketUtils::SetGetAddressInfo(fd, address_family); - if (rc < 0) { - LOG(ERROR) << "IP detection not supported" << strerror(errno); return false; } IPEndPoint client_address; if (bind_to_address_.size() != 0) { client_address = IPEndPoint(bind_to_address_, local_port_); - } else if (address_family == AF_INET) { + } else if (server_address_.GetSockAddrFamily() == AF_INET) { client_address = IPEndPoint(IPAddress(0, 0, 0, 0), local_port_); } else { IPAddress any6; @@ -175,7 +160,8 @@ bool QuicClient::CreateUDPSocket() { socklen_t raw_addr_len = sizeof(raw_addr); CHECK(client_address.ToSockAddr(reinterpret_cast<sockaddr*>(&raw_addr), &raw_addr_len)); - rc = bind(fd, reinterpret_cast<const sockaddr*>(&raw_addr), sizeof(raw_addr)); + int rc = + bind(fd, reinterpret_cast<const sockaddr*>(&raw_addr), sizeof(raw_addr)); if (rc < 0) { LOG(ERROR) << "Bind failed: " << strerror(errno); return false; @@ -390,7 +376,7 @@ bool QuicClient::MigrateSocket(const IPAddress& new_host) { CleanUpUDPSocket(GetLatestFD()); bind_to_address_ = new_host; - if (!CreateUDPSocket()) { + if (!CreateUDPSocketAndBind()) { return false; } @@ -408,15 +394,16 @@ void QuicClient::OnEvent(int fd, EpollEvent* event) { DCHECK_EQ(fd, GetLatestFD()); if (event->in_events & EPOLLIN) { - while (connected()) { - if ( -#if MMSG_MORE - !ReadAndProcessPackets() -#else - !ReadAndProcessPacket() -#endif - ) { - break; + bool more_to_read = true; + while (connected() && more_to_read) { + if (use_recvmmsg_) { + more_to_read = packet_reader_->ReadAndDispatchPackets( + GetLatestFD(), QuicClient::GetLatestClientAddress().port(), this, + overflow_supported_ ? &packets_dropped_ : nullptr); + } else { + more_to_read = QuicPacketReader::ReadAndDispatchSinglePacket( + GetLatestFD(), QuicClient::GetLatestClientAddress().port(), this, + overflow_supported_ ? &packets_dropped_ : nullptr); } } } @@ -492,53 +479,6 @@ QuicPacketWriter* QuicClient::CreateQuicPacketWriter() { return new QuicDefaultPacketWriter(GetLatestFD()); } -QuicPacketReader* QuicClient::CreateQuicPacketReader() { - // TODO(rtenneti): Add support for QuicPacketReader. - // return new QuicPacketReader(); - return nullptr; -} - -int QuicClient::ReadPacket(char* buffer, - int buffer_len, - IPEndPoint* server_address, - IPAddress* client_ip) { - return QuicSocketUtils::ReadPacket( - GetLatestFD(), buffer, buffer_len, - overflow_supported_ ? &packets_dropped_ : nullptr, client_ip, - server_address); -} - -bool QuicClient::ReadAndProcessPacket() { - // Allocate some extra space so we can send an error if the server goes over - // the limit. - char buf[2 * kMaxPacketSize]; - - IPEndPoint server_address; - IPAddress client_ip; - - int bytes_read = ReadPacket(buf, arraysize(buf), &server_address, &client_ip); - - if (bytes_read < 0) { - return false; - } - - QuicEncryptedPacket packet(buf, bytes_read, false); - - IPEndPoint client_address(client_ip, - QuicClient::GetLatestClientAddress().port()); - - session()->ProcessUdpPacket(client_address, server_address, packet); - return true; -} - -/* -bool QuicClient::ReadAndProcessPackets() { - return packet_reader_->ReadAndDispatchPackets( - GetLatestFD(), QuicClient::GetLatestClientAddress().port(), this, - overflow_supported_ ? &packets_dropped_ : nullptr); -} -*/ - const IPEndPoint QuicClient::GetLatestClientAddress() const { if (fd_address_map_.empty()) { return IPEndPoint(); diff --git a/net/tools/quic/quic_client.h b/net/tools/quic/quic_client.h index 5dff8cfe..8b57355 100644 --- a/net/tools/quic/quic_client.h +++ b/net/tools/quic/quic_client.h @@ -25,6 +25,7 @@ #include "net/tools/epoll_server/epoll_server.h" #include "net/tools/quic/quic_client_base.h" #include "net/tools/quic/quic_client_session.h" +#include "net/tools/quic/quic_packet_reader.h" #include "net/tools/quic/quic_process_packet_interface.h" namespace net { @@ -32,7 +33,6 @@ namespace net { class QuicServerId; class QuicEpollConnectionHelper; -class QuicPacketReader; namespace test { class QuicClientPeer; @@ -193,14 +193,7 @@ class QuicClient : public QuicClientBase, return &push_promise_index_; } - protected: virtual QuicPacketWriter* CreateQuicPacketWriter(); - virtual QuicPacketReader* CreateQuicPacketReader(); - - virtual int ReadPacket(char* buffer, - int buffer_len, - IPEndPoint* server_address, - IPAddress* client_ip); // If |fd| is an open UDP socket, unregister and close it. Otherwise, do // nothing. @@ -243,19 +236,11 @@ class QuicClient : public QuicClientBase, // Used during initialization: creates the UDP socket FD, sets socket options, // and binds the socket to our address. - bool CreateUDPSocket(); + bool CreateUDPSocketAndBind(); // Actually clean up |fd|. void CleanUpUDPSocketImpl(int fd); - // Read a UDP packet and hand it to the framer. - bool ReadAndProcessPacket(); - - // Read available UDP packets up to kNumPacketsPerReadCall - // and hand them to the connection. - // TODO(rtenneti): Add support for ReadAndProcessPackets(). - // bool ReadAndProcessPackets(); - // If the request URL matches a push promise, bypass sending the // request. bool MaybeHandlePromised(const BalsaHeaders& headers, @@ -296,6 +281,9 @@ class QuicClient : public QuicClientBase, // because the socket would otherwise overflow. bool overflow_supported_; + // If true, use recvmmsg for reading. + bool use_recvmmsg_; + // If true, store the latest response code, headers, and body. bool store_response_; // HTTP response code from most recent response. @@ -319,7 +307,7 @@ class QuicClient : public QuicClientBase, // // TODO(rtenneti): Chromium code doesn't use |packet_reader_|. Add support for // QuicPacketReader - QuicPacketReader* packet_reader_; + scoped_ptr<QuicPacketReader> packet_reader_; std::unique_ptr<ClientQuicDataToResend> push_promise_data_to_resend_; diff --git a/net/tools/quic/quic_client_base.h b/net/tools/quic/quic_client_base.h index 738eb1e..206dd1a 100644 --- a/net/tools/quic/quic_client_base.h +++ b/net/tools/quic/quic_client_base.h @@ -170,6 +170,14 @@ class QuicClientBase { QuicConnectionHelperInterface* helper() { return helper_.get(); } + void set_num_sent_client_hellos(int num_sent_client_hellos) { + num_sent_client_hellos_ = num_sent_client_hellos; + } + + void set_num_stateless_rejects_received(int num_stateless_rejects_received) { + num_stateless_rejects_received_ = num_stateless_rejects_received; + } + private: // |server_id_| is a tuple (hostname, port, is_https) of the server. QuicServerId server_id_; diff --git a/net/tools/quic/quic_client_bin.cc b/net/tools/quic/quic_client_bin.cc index 6b31baa..ead19ba 100644 --- a/net/tools/quic/quic_client_bin.cc +++ b/net/tools/quic/quic_client_bin.cc @@ -160,8 +160,6 @@ int main(int argc, char* argv[]) { settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; CHECK(logging::InitLogging(settings)); - FLAGS_quic_supports_trailers = true; - if (line->HasSwitch("h") || line->HasSwitch("help") || urls.empty()) { const char* help_str = "Usage: quic_client [options] <url>\n" diff --git a/net/tools/quic/quic_client_session.cc b/net/tools/quic/quic_client_session.cc index ba0f0ed..280f3f8 100644 --- a/net/tools/quic/quic_client_session.cc +++ b/net/tools/quic/quic_client_session.cc @@ -110,6 +110,7 @@ QuicSpdyStream* QuicClientSession::CreateIncomingDynamicStream( } QuicSpdyStream* stream = new QuicSpdyClientStream(id, this); stream->CloseWriteSide(); + ActivateStream(stream); return stream; } diff --git a/net/tools/quic/quic_client_session_test.cc b/net/tools/quic/quic_client_session_test.cc index b6cae39..ac65bf5 100644 --- a/net/tools/quic/quic_client_session_test.cc +++ b/net/tools/quic/quic_client_session_test.cc @@ -69,7 +69,9 @@ class TestQuicClientSession : public QuicClientSession { MockQuicSpdyClientStream* CreateIncomingDynamicStream( QuicStreamId id) override { - return new MockQuicSpdyClientStream(id, this); + MockQuicSpdyClientStream* stream = new MockQuicSpdyClientStream(id, this); + ActivateStream(stream); + return stream; } }; @@ -231,28 +233,6 @@ TEST_P(QuicClientSessionTest, GoAwayReceived) { EXPECT_EQ(nullptr, session_->CreateOutgoingDynamicStream(kDefaultPriority)); } -TEST_P(QuicClientSessionTest, SetFecProtectionFromConfig) { - ValueRestore<bool> old_flag(&FLAGS_enable_quic_fec, true); - - // Set FEC config in client's connection options. - QuicTagVector copt; - copt.push_back(kFHDR); - session_->config()->SetConnectionOptionsToSend(copt); - - // Doing the handshake should set up FEC config correctly. - CompleteCryptoHandshake(); - - // Verify that headers stream is always protected and data streams are - // optionally protected. - EXPECT_EQ( - FEC_PROTECT_ALWAYS, - QuicSpdySessionPeer::GetHeadersStream(session_.get())->fec_policy()); - QuicSpdyClientStream* stream = - session_->CreateOutgoingDynamicStream(kDefaultPriority); - ASSERT_TRUE(stream); - EXPECT_EQ(FEC_PROTECT_OPTIONAL, stream->fec_policy()); -} - static bool CheckForDecryptionError(QuicFramer* framer) { return framer->error() == QUIC_DECRYPTION_FAILURE; } diff --git a/net/tools/quic/quic_client_test.cc b/net/tools/quic/quic_client_test.cc index eed3004..373a2d8 100644 --- a/net/tools/quic/quic_client_test.cc +++ b/net/tools/quic/quic_client_test.cc @@ -88,9 +88,9 @@ TEST(QuicClientTest, CreateAndCleanUpUDPSockets) { CreateAndInitializeQuicClient(&eps, net::test::kTestPort)); EXPECT_EQ(number_of_open_fds + 1, NumOpenFDs()); // Create more UDP sockets. - EXPECT_TRUE(QuicClientPeer::CreateUDPSocket(client.get())); + EXPECT_TRUE(QuicClientPeer::CreateUDPSocketAndBind(client.get())); EXPECT_EQ(number_of_open_fds + 2, NumOpenFDs()); - EXPECT_TRUE(QuicClientPeer::CreateUDPSocket(client.get())); + EXPECT_TRUE(QuicClientPeer::CreateUDPSocketAndBind(client.get())); EXPECT_EQ(number_of_open_fds + 3, NumOpenFDs()); // Clean up UDP sockets. diff --git a/net/tools/quic/quic_dispatcher.cc b/net/tools/quic/quic_dispatcher.cc index 28c31a8..9cb1059 100644 --- a/net/tools/quic/quic_dispatcher.cc +++ b/net/tools/quic/quic_dispatcher.cc @@ -90,6 +90,8 @@ void QuicDispatcher::ProcessPacket(const IPEndPoint& server_address, bool QuicDispatcher::OnUnauthenticatedPublicHeader( const QuicPacketPublicHeader& header) { + current_connection_id_ = header.connection_id; + // Port zero is only allowed for unidirectional UDP, so is disallowed by QUIC. // Given that we can't even send a reply rejecting the packet, just drop the // packet. @@ -137,12 +139,24 @@ bool QuicDispatcher::OnUnauthenticatedPublicHeader( if (framer_.IsSupportedVersion(packet_version)) { version = packet_version; } else { - // Packets set to be processed but having an unsupported version will - // cause a connection to be created. The connection will handle - // sending a version negotiation packet. - // TODO(ianswett): This will malfunction if the full header of the packet - // causes a parsing error when parsed using the server's preferred - // version. + if (FLAGS_quic_stateless_version_negotiation) { + if (ShouldCreateSessionForUnknownVersion(framer_.last_version_tag())) { + return true; + } + // Since the version is not supported, send a version negotiation + // packet and stop processing the current packet. + time_wait_list_manager()->SendVersionNegotiationPacket( + connection_id, supported_versions_, current_server_address_, + current_client_address_); + return false; + } else { + // Packets set to be processed but having an unsupported version will + // cause a connection to be created. The connection will handle + // sending a version negotiation packet. + // TODO(ianswett): This will malfunction if the full header of the + // packet causes a parsing error when parsed using the server's + // preferred version. + } } } // Set the framer's version and continue processing. @@ -334,8 +348,21 @@ void QuicDispatcher::OnError(QuicFramer* framer) { DVLOG(1) << QuicUtils::ErrorToString(error); } +bool QuicDispatcher::ShouldCreateSessionForUnknownVersion(QuicTag version_tag) { + return false; +} + bool QuicDispatcher::OnProtocolVersionMismatch( QuicVersion /*received_version*/) { + if (FLAGS_quic_stateless_version_negotiation) { + QUIC_BUG_IF( + !time_wait_list_manager_->IsConnectionIdInTimeWait( + current_connection_id_) && + !ShouldCreateSessionForUnknownVersion(framer_.last_version_tag())) + << "Unexpected version mismatch: " + << QuicUtils::TagToString(framer_.last_version_tag()); + } + // Keep processing after protocol mismatch - this will be dealt with by the // time wait list or connection that we will create. return true; diff --git a/net/tools/quic/quic_dispatcher.h b/net/tools/quic/quic_dispatcher.h index 6b8f5e5..3a96176 100644 --- a/net/tools/quic/quic_dispatcher.h +++ b/net/tools/quic/quic_dispatcher.h @@ -203,6 +203,10 @@ class QuicDispatcher : public QuicServerSessionVisitor, // cleaned up for bug 16950226.) virtual QuicPacketWriter* CreatePerConnectionWriter(); + // Returns true if a session should be created for a connection with an + // unknown version identified by |version_tag|. + virtual bool ShouldCreateSessionForUnknownVersion(QuicTag version_tag); + void SetLastError(QuicErrorCode error); private: @@ -249,6 +253,7 @@ class QuicDispatcher : public QuicServerSessionVisitor, IPEndPoint current_client_address_; IPEndPoint current_server_address_; const QuicEncryptedPacket* current_packet_; + QuicConnectionId current_connection_id_; QuicFramer framer_; diff --git a/net/tools/quic/quic_dispatcher_test.cc b/net/tools/quic/quic_dispatcher_test.cc index 3064261..169dc63 100644 --- a/net/tools/quic/quic_dispatcher_test.cc +++ b/net/tools/quic/quic_dispatcher_test.cc @@ -164,6 +164,9 @@ class QuicDispatcherTest : public ::testing::Test { return reinterpret_cast<MockConnection*>(session2_->connection()); } + // Process a packet with an 8 byte connection id, + // 6 byte packet number, default path id, and packet number 1, + // using the first supported version. void ProcessPacket(IPEndPoint client_address, QuicConnectionId connection_id, bool has_version_flag, @@ -174,6 +177,8 @@ class QuicDispatcherTest : public ::testing::Test { PACKET_6BYTE_PACKET_NUMBER); } + // Process a packet with a default path id, and packet number 1, + // using the first supported version. void ProcessPacket(IPEndPoint client_address, QuicConnectionId connection_id, bool has_version_flag, @@ -186,6 +191,7 @@ class QuicDispatcherTest : public ::testing::Test { packet_number_length, kDefaultPathId, 1); } + // Process a packet using the first supported version. void ProcessPacket(IPEndPoint client_address, QuicConnectionId connection_id, bool has_version_flag, @@ -195,9 +201,25 @@ class QuicDispatcherTest : public ::testing::Test { QuicPacketNumberLength packet_number_length, QuicPathId path_id, QuicPacketNumber packet_number) { + ProcessPacket(client_address, connection_id, has_version_flag, + QuicSupportedVersions().front(), data, connection_id_length, + packet_number_length, packet_number); + } + + // Processes a packet. + void ProcessPacket(IPEndPoint client_address, + QuicConnectionId connection_id, + bool has_version_flag, + QuicVersion version, + const string& data, + QuicConnectionIdLength connection_id_length, + QuicPacketNumberLength packet_number_length, + QuicPacketNumber packet_number) { + QuicVersionVector versions(SupportedVersions(version)); scoped_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket( - connection_id, has_version_flag, has_multipath_flag, false, path_id, - packet_number, data, connection_id_length, packet_number_length)); + connection_id, has_version_flag, false, false, 0, packet_number, data, + connection_id_length, packet_number_length, &versions)); + data_ = string(packet->data(), packet->length()); dispatcher_.ProcessPacket(server_address_, client_address, *packet); } @@ -254,6 +276,31 @@ TEST_F(QuicDispatcherTest, ProcessPackets) { ProcessPacket(client_address, 1, false, false, "eep"); } +TEST_F(QuicDispatcherTest, StatelessVersionNegotiation) { + ValueRestore<bool> old_flag(&FLAGS_quic_stateless_version_negotiation, true); + IPEndPoint client_address(net::test::Loopback4(), 1); + server_address_ = IPEndPoint(net::test::Any4(), 5); + + EXPECT_CALL(dispatcher_, CreateQuicSession(1, client_address)).Times(0); + QuicVersion version = static_cast<QuicVersion>(QuicVersionMin() - 1); + ProcessPacket(client_address, 1, true, version, "foo", + PACKET_8BYTE_CONNECTION_ID, PACKET_6BYTE_PACKET_NUMBER, 1); +} + +TEST_F(QuicDispatcherTest, StatefulVersionNegotiation) { + ValueRestore<bool> old_flag(&FLAGS_quic_stateless_version_negotiation, false); + IPEndPoint client_address(net::test::Loopback4(), 1); + server_address_ = IPEndPoint(net::test::Any4(), 5); + + EXPECT_CALL(dispatcher_, CreateQuicSession(1, client_address)) + .WillOnce(testing::Return(CreateSession(&dispatcher_, config_, 1, + client_address, &mock_helper_, + &crypto_config_, &session1_))); + QuicVersion version = static_cast<QuicVersion>(QuicVersionMin() - 1); + ProcessPacket(client_address, 1, true, version, "foo", + PACKET_8BYTE_CONNECTION_ID, PACKET_6BYTE_PACKET_NUMBER, 1); +} + TEST_F(QuicDispatcherTest, Shutdown) { IPEndPoint client_address(net::test::Loopback4(), 1); diff --git a/net/tools/quic/quic_server.cc b/net/tools/quic/quic_server.cc index 8af7eba..9b14424 100644 --- a/net/tools/quic/quic_server.cc +++ b/net/tools/quic/quic_server.cc @@ -107,51 +107,18 @@ void QuicServer::Initialize() { QuicServer::~QuicServer() {} -bool QuicServer::Listen(const IPEndPoint& address) { - port_ = address.port(); - int address_family = address.GetSockAddrFamily(); - fd_ = socket(address_family, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); +bool QuicServer::CreateUDPSocketAndListen(const IPEndPoint& address) { + fd_ = QuicSocketUtils::CreateUDPSocket(address, &overflow_supported_); if (fd_ < 0) { LOG(ERROR) << "CreateSocket() failed: " << strerror(errno); return false; } - // Enable the socket option that allows the local address to be - // returned if the socket is bound to more than one address. - int rc = QuicSocketUtils::SetGetAddressInfo(fd_, address_family); - - if (rc < 0) { - LOG(ERROR) << "IP detection not supported" << strerror(errno); - return false; - } - - int get_overflow = 1; - rc = setsockopt(fd_, SOL_SOCKET, SO_RXQ_OVFL, &get_overflow, - sizeof(get_overflow)); - - if (rc < 0) { - DLOG(WARNING) << "Socket overflow detection not supported"; - } else { - overflow_supported_ = true; - } - - // These send and receive buffer sizes are sized for a single connection, - // because the default usage of QuicServer is as a test server with one or - // two clients. Adjust higher for use with many clients. - if (!QuicSocketUtils::SetReceiveBufferSize(fd_, - kDefaultSocketReceiveBuffer)) { - return false; - } - - if (!QuicSocketUtils::SetSendBufferSize(fd_, kDefaultSocketReceiveBuffer)) { - return false; - } - sockaddr_storage raw_addr; socklen_t raw_addr_len = sizeof(raw_addr); CHECK(address.ToSockAddr(reinterpret_cast<sockaddr*>(&raw_addr), &raw_addr_len)); - rc = + int rc = bind(fd_, reinterpret_cast<const sockaddr*>(&raw_addr), sizeof(raw_addr)); if (rc < 0) { LOG(ERROR) << "Bind failed: " << strerror(errno); diff --git a/net/tools/quic/quic_server.h b/net/tools/quic/quic_server.h index ca2a2b5..49718db 100644 --- a/net/tools/quic/quic_server.h +++ b/net/tools/quic/quic_server.h @@ -43,7 +43,7 @@ class QuicServer : public EpollCallbackInterface { ~QuicServer() override; // Start listening on the specified address. - bool Listen(const IPEndPoint& address); + bool CreateUDPSocketAndListen(const IPEndPoint& address); // Wait up to 50ms, and handle any events which occur. void WaitForEvents(); @@ -130,6 +130,8 @@ class QuicServer : public EpollCallbackInterface { // skipped as necessary). QuicVersionVector supported_versions_; + // Point to a QuicPacketReader object on the heap. The reader allocates more + // space than allowed on the stack. scoped_ptr<QuicPacketReader> packet_reader_; DISALLOW_COPY_AND_ASSIGN(QuicServer); diff --git a/net/tools/quic/quic_server_bin.cc b/net/tools/quic/quic_server_bin.cc index 1b9e1583..a6a3341 100644 --- a/net/tools/quic/quic_server_bin.cc +++ b/net/tools/quic/quic_server_bin.cc @@ -88,7 +88,7 @@ int main(int argc, char* argv[]) { net::QuicSupportedVersions()); server.SetStrikeRegisterNoStartupPeriod(); - int rc = server.Listen(net::IPEndPoint(ip, FLAGS_port)); + int rc = server.CreateUDPSocketAndListen(net::IPEndPoint(ip, FLAGS_port)); if (rc < 0) { return 1; } diff --git a/net/tools/quic/quic_server_session_base.cc b/net/tools/quic/quic_server_session_base.cc index 2d08313..63618d4 100644 --- a/net/tools/quic/quic_server_session_base.cc +++ b/net/tools/quic/quic_server_session_base.cc @@ -71,13 +71,6 @@ void QuicServerSessionBase::OnConfigNegotiated() { } } } - - if (FLAGS_enable_quic_fec && - ContainsQuicTag(config()->ReceivedConnectionOptions(), kFHDR)) { - // kFHDR config maps to FEC protection always for headers stream. - // TODO(jri): Add crypto stream in addition to headers for kHDR. - headers_stream()->set_fec_policy(FEC_PROTECT_ALWAYS); - } } void QuicServerSessionBase::OnConnectionClosed(QuicErrorCode error, diff --git a/net/tools/quic/quic_server_session_base_test.cc b/net/tools/quic/quic_server_session_base_test.cc index cab3737..1d4ed11 100644 --- a/net/tools/quic/quic_server_session_base_test.cc +++ b/net/tools/quic/quic_server_session_base_test.cc @@ -83,7 +83,9 @@ class TestServerSession : public QuicServerSessionBase { if (!ShouldCreateIncomingDynamicStream(id)) { return nullptr; } - return new QuicSimpleServerStream(id, this); + QuicSpdyStream* stream = new QuicSimpleServerStream(id, this); + ActivateStream(stream); + return stream; } QuicSpdyStream* CreateOutgoingDynamicStream(SpdyPriority priority) override { @@ -325,27 +327,6 @@ TEST_P(QuicServerSessionBaseTest, GetStreamDisconnected) { "ShouldCreateIncomingDynamicStream called when disconnected"); } -TEST_P(QuicServerSessionBaseTest, SetFecProtectionFromConfig) { - ValueRestore<bool> old_flag(&FLAGS_enable_quic_fec, true); - - // Set received config to have FEC connection option. - QuicTagVector copt; - copt.push_back(kFHDR); - QuicConfigPeer::SetReceivedConnectionOptions(session_->config(), copt); - session_->OnConfigNegotiated(); - - // Verify that headers stream is always protected and data streams are - // optionally protected. - EXPECT_EQ( - FEC_PROTECT_ALWAYS, - QuicSpdySessionPeer::GetHeadersStream(session_.get())->fec_policy()); - ReliableQuicStream* stream = - QuicServerSessionBasePeer::GetOrCreateDynamicStream(session_.get(), - kClientDataStreamId1); - ASSERT_TRUE(stream); - EXPECT_EQ(FEC_PROTECT_OPTIONAL, stream->fec_policy()); -} - class MockQuicCryptoServerStream : public QuicCryptoServerStream { public: explicit MockQuicCryptoServerStream( diff --git a/net/tools/quic/quic_simple_client_bin.cc b/net/tools/quic/quic_simple_client_bin.cc index 2baf3eb..d7f5ef5 100644 --- a/net/tools/quic/quic_simple_client_bin.cc +++ b/net/tools/quic/quic_simple_client_bin.cc @@ -160,8 +160,6 @@ int main(int argc, char* argv[]) { settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; CHECK(logging::InitLogging(settings)); - FLAGS_quic_supports_trailers = true; - if (line->HasSwitch("h") || line->HasSwitch("help") || urls.empty()) { const char* help_str = "Usage: quic_client [options] <url>\n" diff --git a/net/tools/quic/quic_simple_server_session.cc b/net/tools/quic/quic_simple_server_session.cc index 5a2619a..e72383a 100644 --- a/net/tools/quic/quic_simple_server_session.cc +++ b/net/tools/quic/quic_simple_server_session.cc @@ -73,7 +73,9 @@ QuicSpdyStream* QuicSimpleServerSession::CreateIncomingDynamicStream( return nullptr; } - return new QuicSimpleServerStream(id, this); + QuicSpdyStream* stream = new QuicSimpleServerStream(id, this); + ActivateStream(stream); + return stream; } QuicSimpleServerStream* QuicSimpleServerSession::CreateOutgoingDynamicStream( diff --git a/net/tools/quic/quic_simple_server_session_test.cc b/net/tools/quic/quic_simple_server_session_test.cc index 6dd65da..e6dcdc9 100644 --- a/net/tools/quic/quic_simple_server_session_test.cc +++ b/net/tools/quic/quic_simple_server_session_test.cc @@ -282,9 +282,9 @@ TEST_P(QuicSimpleServerSessionTest, CreateEvenIncomingDynamicStream) { } TEST_P(QuicSimpleServerSessionTest, CreateIncomingDynamicStream) { - std::unique_ptr<QuicSpdyStream> stream( + QuicSpdyStream* stream = QuicSimpleServerSessionPeer::CreateIncomingDynamicStream( - session_.get(), kClientDataStreamId1)); + session_.get(), kClientDataStreamId1); EXPECT_NE(nullptr, stream); EXPECT_EQ(kClientDataStreamId1, stream->id()); } diff --git a/net/tools/quic/quic_socket_utils.cc b/net/tools/quic/quic_socket_utils.cc index 36c5fb4..42141df 100644 --- a/net/tools/quic/quic_socket_utils.cc +++ b/net/tools/quic/quic_socket_utils.cc @@ -223,4 +223,39 @@ WriteResult QuicSocketUtils::WritePacket(int fd, errno); } +// static +int QuicSocketUtils::CreateUDPSocket(const IPEndPoint& address, + bool* overflow_supported) { + int address_family = address.GetSockAddrFamily(); + int fd = socket(address_family, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); + if (fd < 0) { + LOG(ERROR) << "socket() failed: " << strerror(errno); + return -1; + } + + int get_overflow = 1; + int rc = setsockopt(fd, SOL_SOCKET, SO_RXQ_OVFL, &get_overflow, + sizeof(get_overflow)); + if (rc < 0) { + DLOG(WARNING) << "Socket overflow detection not supported"; + } else { + *overflow_supported = true; + } + + if (!QuicSocketUtils::SetReceiveBufferSize(fd, kDefaultSocketReceiveBuffer)) { + return -1; + } + + if (!QuicSocketUtils::SetSendBufferSize(fd, kDefaultSocketReceiveBuffer)) { + return -1; + } + + rc = QuicSocketUtils::SetGetAddressInfo(fd, address_family); + if (rc < 0) { + LOG(ERROR) << "IP detection not supported" << strerror(errno); + return -1; + } + return fd; +} + } // namespace net diff --git a/net/tools/quic/quic_socket_utils.h b/net/tools/quic/quic_socket_utils.h index 8a0e442..a464089 100644 --- a/net/tools/quic/quic_socket_utils.h +++ b/net/tools/quic/quic_socket_utils.h @@ -72,6 +72,12 @@ class QuicSocketUtils { // Returns the length of the packet info structure used. static size_t SetIpInfoInCmsg(const IPAddress& self_address, cmsghdr* cmsg); + // Creates a UDP socket and sets appropriate socket options for QUIC. + // Returns the created FD if successful, -1 otherwise. + // |overflow_supported| is set to true if the socket supports it. + static int CreateUDPSocket(const IPEndPoint& address, + bool* overflow_supported); + private: DISALLOW_COPY_AND_ASSIGN(QuicSocketUtils); }; diff --git a/net/tools/quic/quic_spdy_client_stream_test.cc b/net/tools/quic/quic_spdy_client_stream_test.cc index f2e6fed..715fda0 100644 --- a/net/tools/quic/quic_spdy_client_stream_test.cc +++ b/net/tools/quic/quic_spdy_client_stream_test.cc @@ -144,8 +144,6 @@ TEST_F(QuicSpdyClientStreamTest, TestNoBidirectionalStreaming) { TEST_F(QuicSpdyClientStreamTest, ReceivingTrailers) { // Test that receiving trailing headers, containing a final offset, results in // the stream being closed at that byte offset. - ValueRestore<bool> old_flag(&FLAGS_quic_supports_trailers, true); - // Send headers as usual. stream_->OnStreamHeaders(headers_string_); stream_->OnStreamHeadersComplete(false, headers_string_.size()); diff --git a/net/tools/quic/quic_time_wait_list_manager.cc b/net/tools/quic/quic_time_wait_list_manager.cc index 3b50f2d..64ff05e 100644 --- a/net/tools/quic/quic_time_wait_list_manager.cc +++ b/net/tools/quic/quic_time_wait_list_manager.cc @@ -190,6 +190,17 @@ void QuicTimeWaitListManager::ProcessPacket( SendPublicReset(server_address, client_address, connection_id, packet_number); } +void QuicTimeWaitListManager::SendVersionNegotiationPacket( + QuicConnectionId connection_id, + const QuicVersionVector& supported_versions, + const IPEndPoint& server_address, + const IPEndPoint& client_address) { + QueuedPacket* packet = new QueuedPacket( + server_address, client_address, QuicFramer::BuildVersionNegotiationPacket( + connection_id, supported_versions)); + SendOrQueuePacket(packet); +} + // Returns true if the number of packets received for this connection_id is a // power of 2 to throttle the number of public reset packets we send to a // client. diff --git a/net/tools/quic/quic_time_wait_list_manager.h b/net/tools/quic/quic_time_wait_list_manager.h index 51df345..52e1d62 100644 --- a/net/tools/quic/quic_time_wait_list_manager.h +++ b/net/tools/quic/quic_time_wait_list_manager.h @@ -98,6 +98,14 @@ class QuicTimeWaitListManager : public QuicBlockedWriterInterface { // The number of connections on the time-wait list. size_t num_connections() const { return connection_id_map_.size(); } + // Sends a version negotiation packet for |connection_id| announcing support + // for |supported_versions| to |client_address| from |server_address|. + virtual void SendVersionNegotiationPacket( + QuicConnectionId connection_id, + const QuicVersionVector& supported_versions, + const IPEndPoint& server_address, + const IPEndPoint& client_address); + protected: virtual QuicEncryptedPacket* BuildPublicReset( const QuicPublicResetPacket& packet); diff --git a/net/tools/quic/quic_time_wait_list_manager_test.cc b/net/tools/quic/quic_time_wait_list_manager_test.cc index 9607847..12cb7fb 100644 --- a/net/tools/quic/quic_time_wait_list_manager_test.cc +++ b/net/tools/quic/quic_time_wait_list_manager_test.cc @@ -218,6 +218,21 @@ TEST_F(QuicTimeWaitListManagerTest, CheckStatelessConnectionIdInTimeWait) { EXPECT_TRUE(IsConnectionIdInTimeWait(connection_id_)); } +TEST_F(QuicTimeWaitListManagerTest, SendVersionNegotiationPacket) { + scoped_ptr<QuicEncryptedPacket> packet( + QuicFramer::BuildVersionNegotiationPacket(connection_id_, + QuicSupportedVersions())); + EXPECT_CALL(writer_, + WritePacket(_, packet->length(), server_address_.address(), + client_address_, _)) + .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1))); + + time_wait_list_manager_.SendVersionNegotiationPacket( + connection_id_, QuicSupportedVersions(), server_address_, + client_address_); + EXPECT_EQ(0u, time_wait_list_manager_.num_connections()); +} + TEST_F(QuicTimeWaitListManagerTest, SendConnectionClose) { const size_t kConnectionCloseLength = 100; EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id_)); diff --git a/net/tools/quic/spdy_balsa_utils.cc b/net/tools/quic/spdy_balsa_utils.cc index d6132f9..dd24c99 100644 --- a/net/tools/quic/spdy_balsa_utils.cc +++ b/net/tools/quic/spdy_balsa_utils.cc @@ -227,16 +227,16 @@ SpdyHeaderBlock SpdyBalsaUtils::RequestHeadersToSpdyHeaders( if (url.empty() || url[0] == '/') { path = url; } else { - GURL request_uri(url); + std::unique_ptr<GURL> request_uri(new GURL(url)); if (request_headers.request_method() == "CONNECT") { path = url; } else { - path = request_uri.path(); - if (!request_uri.query().empty()) { - path = path + "?" + request_uri.query(); + path = request_uri->path(); + if (!request_uri->query().empty()) { + path = path + "?" + request_uri->query(); } - host_and_port = request_uri.host(); - scheme = request_uri.scheme(); + host_and_port = request_uri->host(); + scheme = request_uri->scheme(); } } diff --git a/net/tools/quic/test_tools/mock_quic_time_wait_list_manager.h b/net/tools/quic/test_tools/mock_quic_time_wait_list_manager.h index 2cf5f83..5d5542b 100644 --- a/net/tools/quic/test_tools/mock_quic_time_wait_list_manager.h +++ b/net/tools/quic/test_tools/mock_quic_time_wait_list_manager.h @@ -42,6 +42,12 @@ class MockTimeWaitListManager : public QuicTimeWaitListManager { QuicConnectionId connection_id, QuicPacketNumber packet_number, const QuicEncryptedPacket& packet)); + + MOCK_METHOD4(SendVersionNegotiationPacket, + void(QuicConnectionId connection_id, + const QuicVersionVector& supported_versions, + const IPEndPoint& server_address, + const IPEndPoint& client_address)); }; } // namespace test diff --git a/net/tools/quic/test_tools/quic_client_peer.cc b/net/tools/quic/test_tools/quic_client_peer.cc index f4a1512..71d3520 100644 --- a/net/tools/quic/test_tools/quic_client_peer.cc +++ b/net/tools/quic/test_tools/quic_client_peer.cc @@ -10,8 +10,8 @@ namespace net { namespace test { // static -bool QuicClientPeer::CreateUDPSocket(QuicClient* client) { - return client->CreateUDPSocket(); +bool QuicClientPeer::CreateUDPSocketAndBind(QuicClient* client) { + return client->CreateUDPSocketAndBind(); } // static diff --git a/net/tools/quic/test_tools/quic_client_peer.h b/net/tools/quic/test_tools/quic_client_peer.h index eb163dc..4d249a3 100644 --- a/net/tools/quic/test_tools/quic_client_peer.h +++ b/net/tools/quic/test_tools/quic_client_peer.h @@ -19,7 +19,7 @@ namespace test { class QuicClientPeer { public: - static bool CreateUDPSocket(QuicClient* client); + static bool CreateUDPSocketAndBind(QuicClient* client); static void CleanUpUDPSocket(QuicClient* client, int fd); static void SetClientPort(QuicClient* client, int port); diff --git a/net/tools/quic/test_tools/quic_test_client.cc b/net/tools/quic/test_tools/quic_test_client.cc index 7e6d413..cf1abec 100644 --- a/net/tools/quic/test_tools/quic_test_client.cc +++ b/net/tools/quic/test_tools/quic_test_client.cc @@ -181,7 +181,9 @@ QuicTestClient::QuicTestClient(IPEndPoint server_address, config, supported_versions, &epoll_server_)), - allow_bidirectional_data_(false) { + allow_bidirectional_data_(false), + num_requests_(0), + num_responses_(0) { Initialize(); } @@ -273,6 +275,7 @@ ssize_t QuicTestClient::GetOrCreateStreamAndSendRequest( spdy_headers[":authority"] = client_->server_id().host(); } ret = stream->SendRequest(spdy_headers, body, fin); + ++num_requests_; } else { stream->SendBody(body.as_string(), fin, delegate); ret = body.length(); @@ -576,6 +579,7 @@ void QuicTestClient::OnClose(QuicSpdyStream* stream) { response_header_size_ = headers_.GetSizeForWriteBuffer(); response_body_size_ = stream_->data().size(); stream_ = nullptr; + ++num_responses_; } bool QuicTestClient::CheckVary(const SpdyHeaderBlock& client_request, diff --git a/net/tools/quic/test_tools/quic_test_client.h b/net/tools/quic/test_tools/quic_test_client.h index df38ad5..6dc3ea5 100644 --- a/net/tools/quic/test_tools/quic_test_client.h +++ b/net/tools/quic/test_tools/quic_test_client.h @@ -199,6 +199,10 @@ class QuicTestClient : public test::SimpleClient, bool allow_bidirectional_data() const { return allow_bidirectional_data_; } + size_t num_requests() const { return num_requests_; } + + size_t num_responses() const { return num_responses_; } + protected: QuicTestClient(); @@ -269,6 +273,9 @@ class QuicTestClient : public test::SimpleClient, // For async push promise rendezvous, validation may fail in which // case the request should be retried. std::unique_ptr<TestClientDataToResend> push_promise_data_to_resend_; + // Number of requests/responses this client has sent/received. + size_t num_requests_; + size_t num_responses_; DISALLOW_COPY_AND_ASSIGN(QuicTestClient); }; diff --git a/net/tools/quic/test_tools/quic_test_server.cc b/net/tools/quic/test_tools/quic_test_server.cc index b5ea61a..31ddef8 100644 --- a/net/tools/quic/test_tools/quic_test_server.cc +++ b/net/tools/quic/test_tools/quic_test_server.cc @@ -44,7 +44,9 @@ class CustomStreamSession : public QuicSimpleServerSession { return nullptr; } if (stream_factory_) { - return stream_factory_->CreateStream(id, this); + QuicSpdyStream* stream = stream_factory_->CreateStream(id, this); + ActivateStream(stream); + return stream; } return QuicSimpleServerSession::CreateIncomingDynamicStream(id); } diff --git a/net/tools/quic/test_tools/server_thread.cc b/net/tools/quic/test_tools/server_thread.cc index ee6ceeb..b51dfb1 100644 --- a/net/tools/quic/test_tools/server_thread.cc +++ b/net/tools/quic/test_tools/server_thread.cc @@ -36,7 +36,7 @@ void ServerThread::Initialize() { return; } - server_->Listen(address_); + server_->CreateUDPSocketAndListen(address_); port_lock_.Acquire(); port_ = server_->port(); |