summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorrtenneti <rtenneti@chromium.org>2015-10-19 23:14:20 -0700
committerCommit bot <commit-bot@chromium.org>2015-10-20 06:15:11 +0000
commit1535e3a3936db1c0a2a410fd12ac9c5b920ad1ac (patch)
treebb61063a62323a60ac2da98c4c4807f1071a9a4b /net
parent271e821edda950d1f54af8bc3d6d8217ddfb0427 (diff)
downloadchromium_src-1535e3a3936db1c0a2a410fd12ac9c5b920ad1ac.zip
chromium_src-1535e3a3936db1c0a2a410fd12ac9c5b920ad1ac.tar.gz
chromium_src-1535e3a3936db1c0a2a410fd12ac9c5b920ad1ac.tar.bz2
Landing Recent QUIC changes until 10/11/2015 18:20 UTC
relnote: Split QuicFecGroup interface into QuicFecGroupInterface. Should cause no behavior change. Implementing 'sliding window' FEC, part 1a. Merge internal change: 105168235 https://codereview.chromium.org/1410593004/ relnote: n/a (no changes to binary). Cleanup changes to tests, comments, and formatting. Merge internal change: 105084706 https://codereview.chromium.org/1418493004/ relnote: Reorganize how the read side of QUIC connections is closed down when the consumer doesn't want any more data. Flag protected by FLAG_quic_implement_stop_reading; default true. Remove CloseReadSide from the subclass interface provided by ReliableQuicStream. Subclasses (in particular QuicDataStream and ...QuicServerStream) now have an internal StopReading method which causes all further incoming data to be ignored in the subclass code. This prevents the stream object from being deleted until the stream is properly closed. It is necessary to keep the stream object functioning until a FIN or RST_STREAM is received in order to send flow-control window updates for the stream. (When we define the RST_STREAM/NO_ERROR mechanism to tell the peer to abort sending more data we will send RST_STREAM/NO_ERROR as soon as this consumer is done writing as well.) Re-install QuicSpdyServerStream::kErrorResponseBody as it is now used in two places. FIXED=21369841 Merge internal change: 105079850 https://codereview.chromium.org/1415603003/ relnote: n/a (test change only). Call SetSpdyStreamCreator on the test server before starting its thread, to avoid a data race. Merge internal change: 104964040 https://codereview.chromium.org/1415783002/ relnote: No functional change. Flag protected by default-enabled FLAGS_quic_fix_fin_accounting. Revise processing of incoming stream frames for streams for which reading is closed: Record the FIN bit correctly. This prevents spurious entries in locally_closed_streams_highest_offset_[] and consequent log messages "Surprisingly high number of locally closed streams still waiting for final byte offset". Investigating b/16010251. Merge internal change: 104913996 https://codereview.chromium.org/1418553003/ relnote: minor additions to quic-to-backend (not in production), change fatal log message. Add bidirectional data capability to the QUIC libraries. Add accessors to the peer classes. Merge internal change: 104332818 https://codereview.chromium.org/1417503004/ R=rch@chromium.org Review URL: https://codereview.chromium.org/1414143002 Cr-Commit-Position: refs/heads/master@{#354994}
Diffstat (limited to 'net')
-rw-r--r--net/net.gypi4
-rw-r--r--net/quic/quic_connection.cc2
-rw-r--r--net/quic/quic_data_stream_test.cc1
-rw-r--r--net/quic/quic_fec_group.cc44
-rw-r--r--net/quic/quic_fec_group.h66
-rw-r--r--net/quic/quic_fec_group_interface.cc50
-rw-r--r--net/quic/quic_fec_group_interface.h82
-rw-r--r--net/quic/quic_fec_group_test.cc8
-rw-r--r--net/quic/quic_flags.cc9
-rw-r--r--net/quic/quic_flags.h2
-rw-r--r--net/quic/quic_frame_list.cc12
-rw-r--r--net/quic/quic_frame_list.h4
-rw-r--r--net/quic/quic_packet_creator.cc4
-rw-r--r--net/quic/quic_protocol.cc5
-rw-r--r--net/quic/quic_session.h2
-rw-r--r--net/quic/quic_session_test.cc36
-rw-r--r--net/quic/quic_stream_sequencer.cc55
-rw-r--r--net/quic/quic_stream_sequencer.h14
-rw-r--r--net/quic/reliable_quic_stream.cc28
-rw-r--r--net/quic/reliable_quic_stream.h26
-rw-r--r--net/quic/reliable_quic_stream_test.cc80
-rw-r--r--net/quic/test_tools/quic_session_peer.cc10
-rw-r--r--net/quic/test_tools/quic_session_peer.h3
-rw-r--r--net/quic/test_tools/reliable_quic_stream_peer.cc25
-rw-r--r--net/quic/test_tools/reliable_quic_stream_peer.h6
-rw-r--r--net/tools/quic/end_to_end_test.cc171
-rw-r--r--net/tools/quic/quic_spdy_client_stream.cc6
-rw-r--r--net/tools/quic/quic_spdy_client_stream.h9
-rw-r--r--net/tools/quic/quic_spdy_server_stream.cc16
-rw-r--r--net/tools/quic/quic_spdy_server_stream.h11
-rw-r--r--net/tools/quic/quic_spdy_server_stream_test.cc34
-rw-r--r--net/tools/quic/test_tools/quic_dispatcher_peer.cc6
-rw-r--r--net/tools/quic/test_tools/quic_dispatcher_peer.h3
-rw-r--r--net/tools/quic/test_tools/quic_test_client.cc11
-rw-r--r--net/tools/quic/test_tools/quic_test_client.h9
-rw-r--r--net/tools/quic/test_tools/quic_test_server.cc145
-rw-r--r--net/tools/quic/test_tools/quic_test_server.h73
37 files changed, 925 insertions, 147 deletions
diff --git a/net/net.gypi b/net/net.gypi
index 79b1e2c..3aea591 100644
--- a/net/net.gypi
+++ b/net/net.gypi
@@ -329,6 +329,8 @@
'quic/quic_default_packet_writer.h',
'quic/quic_fec_group.cc',
'quic/quic_fec_group.h',
+ 'quic/quic_fec_group_interface.cc',
+ 'quic/quic_fec_group_interface.h',
'quic/quic_flags.cc',
'quic/quic_flags.h',
'quic/quic_flow_controller.cc',
@@ -1789,6 +1791,8 @@
'tools/quic/test_tools/quic_server_peer.h',
'tools/quic/test_tools/quic_test_client.cc',
'tools/quic/test_tools/quic_test_client.h',
+ 'tools/quic/test_tools/quic_test_server.cc',
+ 'tools/quic/test_tools/quic_test_server.h',
'tools/quic/test_tools/server_thread.cc',
'tools/quic/test_tools/server_thread.h',
'tools/quic/test_tools/simple_client.cc',
diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc
index 2ac4ddb..6dc5e2b 100644
--- a/net/quic/quic_connection.cc
+++ b/net/quic/quic_connection.cc
@@ -1907,7 +1907,7 @@ void QuicConnection::MaybeProcessRevivedPacket() {
revived_header.is_in_fec_group = NOT_IN_FEC_GROUP;
revived_header.fec_group = 0;
group_map_.erase(last_header_.fec_group);
- last_decrypted_packet_level_ = group->effective_encryption_level();
+ last_decrypted_packet_level_ = group->EffectiveEncryptionLevel();
DCHECK_LT(last_decrypted_packet_level_, NUM_ENCRYPTION_LEVELS);
delete group;
diff --git a/net/quic/quic_data_stream_test.cc b/net/quic/quic_data_stream_test.cc
index 6b61e07..3ca05b7 100644
--- a/net/quic/quic_data_stream_test.cc
+++ b/net/quic/quic_data_stream_test.cc
@@ -50,7 +50,6 @@ class TestStream : public QuicDataStream {
}
using ReliableQuicStream::WriteOrBufferData;
- using ReliableQuicStream::CloseReadSide;
using ReliableQuicStream::CloseWriteSide;
const string& data() const { return data_; }
diff --git a/net/quic/quic_fec_group.cc b/net/quic/quic_fec_group.cc
index 8bc7601..72fa536 100644
--- a/net/quic/quic_fec_group.cc
+++ b/net/quic/quic_fec_group.cc
@@ -17,7 +17,8 @@ using std::set;
namespace net {
QuicFecGroup::QuicFecGroup()
- : min_protected_packet_(kInvalidPacketNumber),
+ : QuicFecGroupInterface(),
+ min_protected_packet_(kInvalidPacketNumber),
max_protected_packet_(kInvalidPacketNumber),
payload_parity_len_(0),
effective_encryption_level_(NUM_ENCRYPTION_LEVELS) {}
@@ -163,39 +164,20 @@ QuicPacketCount QuicFecGroup::NumMissingPackets() const {
received_packets_.size());
}
-void QuicFecGroup::XorBuffers(const char* input,
- size_t size_in_bytes,
- char* output) {
-#if defined(__i386__) || defined(__x86_64__)
- // On x86, alignment is not required and casting bytes to words is safe.
-
- // size_t is a reasonable approximation of how large a general-purpose
- // register is for the platforms and compilers Chrome is built on.
- typedef size_t platform_word;
- const size_t size_in_words = size_in_bytes / sizeof(platform_word);
+const StringPiece QuicFecGroup::PayloadParity() const {
+ return StringPiece(payload_parity_, payload_parity_len_);
+}
- const platform_word* input_words =
- reinterpret_cast<const platform_word*>(input);
- platform_word* output_words = reinterpret_cast<platform_word*>(output);
+QuicPacketNumber QuicFecGroup::MinProtectedPacket() const {
+ return min_protected_packet_;
+}
- // Handle word-sized part of the buffer.
- size_t offset_in_words = 0;
- for (; offset_in_words < size_in_words; offset_in_words++) {
- output_words[offset_in_words] ^= input_words[offset_in_words];
- }
+QuicPacketCount QuicFecGroup::NumReceivedPackets() const {
+ return received_packets_.size();
+}
- // Handle the tail which does not fit into the word.
- for (size_t offset_in_bytes = offset_in_words * sizeof(platform_word);
- offset_in_bytes < size_in_bytes; offset_in_bytes++) {
- output[offset_in_bytes] ^= input[offset_in_bytes];
- }
-#else
- // On ARM and most other plaforms, the code above could fail due to the
- // alignment errors. Stick to byte-by-byte comparison.
- for (size_t offset = 0; offset < size_in_bytes; offset++) {
- output[offset] ^= input[offset];
- }
-#endif /* defined(__i386__) || defined(__x86_64__) */
+EncryptionLevel QuicFecGroup::EffectiveEncryptionLevel() const {
+ return effective_encryption_level_;
}
} // namespace net
diff --git a/net/quic/quic_fec_group.h b/net/quic/quic_fec_group.h
index 2a1f53f..3afd46c 100644
--- a/net/quic/quic_fec_group.h
+++ b/net/quic/quic_fec_group.h
@@ -3,7 +3,7 @@
// found in the LICENSE file.
//
// Tracks information about an FEC group, including the packets
-// that have been seen, and the running parity. Provided the ability
+// that have been seen, and the running parity. Provides the ability
// to revive a dropped packet.
#ifndef NET_QUIC_QUIC_FEC_GROUP_H_
@@ -12,69 +12,33 @@
#include <cstddef>
#include "base/strings/string_piece.h"
+#include "net/quic/quic_fec_group_interface.h"
#include "net/quic/quic_protocol.h"
namespace net {
-class NET_EXPORT_PRIVATE QuicFecGroup {
+class NET_EXPORT_PRIVATE QuicFecGroup : public QuicFecGroupInterface {
public:
QuicFecGroup();
- ~QuicFecGroup();
+ virtual ~QuicFecGroup();
- // Updates the FEC group based on the delivery of a data packet decrypted at
- // |encryption_level|. Returns false if this packet has already been seen,
- // true otherwise.
+ // Implementation of QuicFecGroupInterface.
bool Update(EncryptionLevel encryption_level,
const QuicPacketHeader& header,
- base::StringPiece decrypted_payload);
-
- // Updates the FEC group based on the delivery of an FEC packet decrypted at
- // |encryption_level|. Returns false if this packet has already been seen or
- // if it does not claim to protect all the packets previously seen in this
- // group.
+ base::StringPiece decrypted_payload) override;
bool UpdateFec(EncryptionLevel encryption_level,
QuicPacketNumber fec_packet_packet_number,
- const QuicFecData& fec);
-
- // Returns true if a packet can be revived from this FEC group.
- bool CanRevive() const;
-
- // Returns true if all packets (FEC and data) from this FEC group have been
- // seen or revived
- bool IsFinished() const;
-
- // Revives the missing packet from this FEC group. This may return a packet
- // that is null padded to a greater length than the original packet, but
- // the framer will handle it correctly. Returns the length of the data
- // written to |decrypted_payload|, or 0 if the packet could not be revived.
+ const QuicFecData& fec) override;
+ bool CanRevive() const override;
+ bool IsFinished() const override;
size_t Revive(QuicPacketHeader* header,
char* decrypted_payload,
- size_t decrypted_payload_len);
-
- // Returns true of this FEC group protects any packets with sequence
- // numbers less than |num|.
- bool ProtectsPacketsBefore(QuicPacketNumber num) const;
-
- const base::StringPiece payload_parity() const {
- return base::StringPiece(payload_parity_, payload_parity_len_);
- }
-
- QuicPacketNumber min_protected_packet() const {
- return min_protected_packet_;
- }
-
- QuicPacketCount NumReceivedPackets() const {
- return received_packets_.size();
- }
-
- // Returns the effective encryption level of the FEC group.
- EncryptionLevel effective_encryption_level() const {
- return effective_encryption_level_;
- }
-
- // An optimized version of running |output| ^= |input|, where ^ is
- // byte-by-byte XOR and both |output| and |input| are of size |size_in_bytes|.
- static void XorBuffers(const char* input, size_t size_in_bytes, char* output);
+ size_t decrypted_payload_len) override;
+ bool ProtectsPacketsBefore(QuicPacketNumber num) const override;
+ const base::StringPiece PayloadParity() const override;
+ QuicPacketNumber MinProtectedPacket() const override;
+ QuicPacketCount NumReceivedPackets() const override;
+ EncryptionLevel EffectiveEncryptionLevel() const override;
private:
bool UpdateParity(base::StringPiece payload);
diff --git a/net/quic/quic_fec_group_interface.cc b/net/quic/quic_fec_group_interface.cc
new file mode 100644
index 0000000..65224b0
--- /dev/null
+++ b/net/quic/quic_fec_group_interface.cc
@@ -0,0 +1,50 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_fec_group_interface.h"
+
+#include <limits>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+
+namespace net {
+
+void QuicFecGroupInterface::XorBuffers(const char* input,
+ size_t size_in_bytes,
+ char* output) {
+#if defined(__i386__) || defined(__x86_64__)
+ // On x86, alignment is not required and casting bytes to words is safe.
+
+ // size_t is a reasonable approximation of how large a general-purpose
+ // register is for the platforms and compilers Chrome is built on.
+ typedef size_t platform_word;
+ const size_t size_in_words = size_in_bytes / sizeof(platform_word);
+
+ const platform_word* input_words =
+ reinterpret_cast<const platform_word*>(input);
+ platform_word* output_words = reinterpret_cast<platform_word*>(output);
+
+ // Handle word-sized part of the buffer.
+ size_t offset_in_words = 0;
+ for (; offset_in_words < size_in_words; offset_in_words++) {
+ output_words[offset_in_words] ^= input_words[offset_in_words];
+ }
+
+ // Handle the tail which does not fit into the word.
+ for (size_t offset_in_bytes = offset_in_words * sizeof(platform_word);
+ offset_in_bytes < size_in_bytes; offset_in_bytes++) {
+ output[offset_in_bytes] ^= input[offset_in_bytes];
+ }
+#else
+ // On ARM and most other plaforms, the code above could fail due to the
+ // alignment errors. Stick to byte-by-byte comparison.
+ for (size_t offset = 0; offset < size_in_bytes; offset++) {
+ output[offset] ^= input[offset];
+ }
+#endif /* defined(__i386__) || defined(__x86_64__) */
+}
+
+} // namespace net
diff --git a/net/quic/quic_fec_group_interface.h b/net/quic/quic_fec_group_interface.h
new file mode 100644
index 0000000..8f15175
--- /dev/null
+++ b/net/quic/quic_fec_group_interface.h
@@ -0,0 +1,82 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Tracks information about an FEC group, including the packets
+// that have been seen, and the running parity. Provides the ability
+// to revive a dropped packet.
+
+#ifndef NET_QUIC_QUIC_FEC_GROUP_INTERFACE_H_
+#define NET_QUIC_QUIC_FEC_GROUP_INTERFACE_H_
+
+#include "base/basictypes.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE QuicFecGroupInterface {
+ public:
+ ~QuicFecGroupInterface() {}
+
+ // Updates the FEC group based on the delivery of a data packet decrypted at
+ // |encryption_level|. Returns false if this packet has already been seen,
+ // true otherwise.
+ virtual bool Update(EncryptionLevel encryption_level,
+ const QuicPacketHeader& header,
+ base::StringPiece decrypted_payload) = 0;
+
+ // Updates the FEC group based on the delivery of an FEC packet decrypted at
+ // |encryption_level|. Returns false if this packet has already been seen or
+ // if it does not claim to protect all the packets previously seen in this
+ // group.
+ virtual bool UpdateFec(EncryptionLevel encryption_level,
+ QuicPacketNumber fec_packet_packet_number,
+ const QuicFecData& fec) = 0;
+
+ // Returns true if a packet can be revived from this FEC group.
+ virtual bool CanRevive() const = 0;
+
+ // Returns true if all packets (FEC and data) from this FEC group have been
+ // seen or revived.
+ virtual bool IsFinished() const = 0;
+
+ // Revives the missing packet from this FEC group. This may return a packet
+ // that is null padded to a greater length than the original packet, but
+ // the framer will handle it correctly. Returns the length of the data
+ // written to |decrypted_payload|, or 0 if the packet could not be revived.
+ virtual size_t Revive(QuicPacketHeader* header,
+ char* decrypted_payload,
+ size_t decrypted_payload_len) = 0;
+
+ // Returns true of this FEC group protects any packets with sequence
+ // numbers less than |num|.
+ virtual bool ProtectsPacketsBefore(QuicPacketNumber num) const = 0;
+
+ // The FEC data in the FEC packet.
+ virtual const base::StringPiece PayloadParity() const = 0;
+
+ // The FEC group number to be used on the FEC packet.
+ virtual QuicPacketNumber MinProtectedPacket() const = 0;
+
+ // Number of packets in the group.
+ virtual QuicPacketCount NumReceivedPackets() const = 0;
+
+ // Returns the effective encryption level of the FEC group.
+ virtual EncryptionLevel EffectiveEncryptionLevel() const = 0;
+
+ // An optimized version of running |output| ^= |input|, where ^ is
+ // byte-by-byte XOR and both |output| and |input| are of size |size_in_bytes|.
+ static void XorBuffers(const char* input, size_t size_in_bytes, char* output);
+
+ protected:
+ QuicFecGroupInterface() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicFecGroupInterface);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_FEC_GROUP_INTERFACE_H_
diff --git a/net/quic/quic_fec_group_test.cc b/net/quic/quic_fec_group_test.cc
index 8207631..9258186 100644
--- a/net/quic/quic_fec_group_test.cc
+++ b/net/quic/quic_fec_group_test.cc
@@ -232,22 +232,22 @@ TEST_F(QuicFecGroupTest, ProtectsPacketsBeforeWithFecData) {
TEST_F(QuicFecGroupTest, EffectiveEncryptionLevel) {
QuicFecGroup group;
- EXPECT_EQ(NUM_ENCRYPTION_LEVELS, group.effective_encryption_level());
+ EXPECT_EQ(NUM_ENCRYPTION_LEVELS, group.EffectiveEncryptionLevel());
QuicPacketHeader header;
header.packet_packet_number = 5;
ASSERT_TRUE(group.Update(ENCRYPTION_INITIAL, header, kDataSingle));
- EXPECT_EQ(ENCRYPTION_INITIAL, group.effective_encryption_level());
+ EXPECT_EQ(ENCRYPTION_INITIAL, group.EffectiveEncryptionLevel());
QuicFecData fec;
fec.fec_group = 1u;
fec.redundancy = kDataSingle;
ASSERT_TRUE(group.UpdateFec(ENCRYPTION_FORWARD_SECURE, 7, fec));
- EXPECT_EQ(ENCRYPTION_INITIAL, group.effective_encryption_level());
+ EXPECT_EQ(ENCRYPTION_INITIAL, group.EffectiveEncryptionLevel());
header.packet_packet_number = 3;
ASSERT_TRUE(group.Update(ENCRYPTION_NONE, header, kDataSingle));
- EXPECT_EQ(ENCRYPTION_NONE, group.effective_encryption_level());
+ EXPECT_EQ(ENCRYPTION_NONE, group.EffectiveEncryptionLevel());
}
// Test the code assuming it is going to be operating in 128-bit chunks (which
diff --git a/net/quic/quic_flags.cc b/net/quic/quic_flags.cc
index 8639aec..0ed0c95 100644
--- a/net/quic/quic_flags.cc
+++ b/net/quic/quic_flags.cc
@@ -94,3 +94,12 @@ bool FLAGS_quic_stop_checking_for_mismatch_ids = true;
// Disable QUIC's userspace pacing.
bool FLAGS_quic_disable_pacing = false;
+
+// If true, a FIN received on a stream with read_side_closed_ true will be
+// recorded correctly.
+bool FLAGS_quic_fix_fin_accounting = true;
+
+// If true, ReliableQuicStream::StopReading (formerly CloseReadSide) causes
+// incoming data to be ignored but the read side of the stream object is not
+// closed.
+bool FLAGS_quic_implement_stop_reading = true;
diff --git a/net/quic/quic_flags.h b/net/quic/quic_flags.h
index dbeab3a..58354ee 100644
--- a/net/quic/quic_flags.h
+++ b/net/quic/quic_flags.h
@@ -31,5 +31,7 @@ NET_EXPORT_PRIVATE extern bool FLAGS_quic_read_packets_full_recvmmsg;
NET_EXPORT_PRIVATE extern bool FLAGS_quic_measure_headers_hol_blocking_time;
NET_EXPORT_PRIVATE extern bool FLAGS_quic_stop_checking_for_mismatch_ids;
NET_EXPORT_PRIVATE extern bool FLAGS_quic_disable_pacing;
+NET_EXPORT_PRIVATE extern bool FLAGS_quic_fix_fin_accounting;
+NET_EXPORT_PRIVATE extern bool FLAGS_quic_implement_stop_reading;
#endif // NET_QUIC_QUIC_FLAGS_H_
diff --git a/net/quic/quic_frame_list.cc b/net/quic/quic_frame_list.cc
index 61c15d4..5a6209e 100644
--- a/net/quic/quic_frame_list.cc
+++ b/net/quic/quic_frame_list.cc
@@ -210,6 +210,18 @@ size_t QuicFrameList::ReadvAndInvalidate(const struct iovec* iov,
return total_bytes_read_ - initial_bytes_consumed;
}
+size_t QuicFrameList::FlushBufferedFrames() {
+ QuicStreamOffset initial_bytes_consumed = total_bytes_read_;
+ if (!frame_list_.empty()) {
+ // Consume all of the bytes up to the last byte yet seen, including the
+ // ones that haven't arrived yet.
+ auto it = frame_list_.back();
+ total_bytes_read_ = it.offset + it.segment.length();
+ frame_list_.clear();
+ }
+ return total_bytes_read_ - initial_bytes_consumed;
+}
+
bool QuicFrameList::HasBytesToRead() const {
return !frame_list_.empty() &&
frame_list_.begin()->offset == total_bytes_read_;
diff --git a/net/quic/quic_frame_list.h b/net/quic/quic_frame_list.h
index 7491cb7..7738686 100644
--- a/net/quic/quic_frame_list.h
+++ b/net/quic/quic_frame_list.h
@@ -58,6 +58,10 @@ class NET_EXPORT_PRIVATE QuicFrameList {
// Returns the number of bytes read into iov.
size_t ReadvAndInvalidate(const struct iovec* iov, size_t iov_len);
+ // Invalidate all currently readable bytes.
+ // Returns the number of bytes invalidated.
+ size_t FlushBufferedFrames();
+
// Returns the readable region of valid data in iovec format. The readable
// region is the buffer region where there is valid data not yet read by
// client. ReadAndInvalidate() and WriteAtOffset() change the readable region.
diff --git a/net/quic/quic_packet_creator.cc b/net/quic/quic_packet_creator.cc
index 8e4f174..8ec8d88 100644
--- a/net/quic/quic_packet_creator.cc
+++ b/net/quic/quic_packet_creator.cc
@@ -527,8 +527,8 @@ SerializedPacket QuicPacketCreator::SerializeFec(char* buffer,
QuicPacketHeader header;
FillPacketHeader(fec_group_number_, true, &header);
QuicFecData fec_data;
- fec_data.fec_group = fec_group_->min_protected_packet();
- fec_data.redundancy = fec_group_->payload_parity();
+ fec_data.fec_group = fec_group_->MinProtectedPacket();
+ fec_data.redundancy = fec_group_->PayloadParity();
scoped_ptr<QuicPacket> packet(framer_->BuildFecPacket(header, fec_data));
fec_group_.reset(nullptr);
packet_size_ = 0;
diff --git a/net/quic/quic_protocol.cc b/net/quic/quic_protocol.cc
index fedc16d..2038971 100644
--- a/net/quic/quic_protocol.cc
+++ b/net/quic/quic_protocol.cc
@@ -239,9 +239,8 @@ QuicAckFrame::QuicAckFrame()
QuicAckFrame::~QuicAckFrame() {}
-QuicRstStreamErrorCode AdjustErrorForVersion(
- QuicRstStreamErrorCode error_code,
- QuicVersion version) {
+QuicRstStreamErrorCode AdjustErrorForVersion(QuicRstStreamErrorCode error_code,
+ QuicVersion /*version*/) {
return error_code;
}
diff --git a/net/quic/quic_session.h b/net/quic/quic_session.h
index fd30fc2..4ef7389 100644
--- a/net/quic/quic_session.h
+++ b/net/quic/quic_session.h
@@ -69,7 +69,7 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
void OnWriteBlocked() override {}
void OnSuccessfulVersionNegotiation(const QuicVersion& version) override;
void OnCanWrite() override;
- void OnCongestionWindowChange(QuicTime now) override {}
+ void OnCongestionWindowChange(QuicTime /*now*/) override {}
void OnConnectionMigration() override {}
bool WillingAndAbleToWrite() const override;
bool HasPendingHandshake() const override;
diff --git a/net/quic/quic_session_test.cc b/net/quic/quic_session_test.cc
index de74711..6f57514 100644
--- a/net/quic/quic_session_test.cc
+++ b/net/quic/quic_session_test.cc
@@ -885,6 +885,7 @@ TEST_P(QuicSessionTestServer, ConnectionFlowControlAccountingRstAfterRst) {
TestStream* stream = session_.CreateOutgoingDynamicStream();
EXPECT_CALL(*connection_, SendRstStream(stream->id(), _, _));
stream->Reset(QUIC_STREAM_CANCELLED);
+ EXPECT_TRUE(ReliableQuicStreamPeer::read_side_closed(stream));
// Now receive a RST from the peer. We should handle this by adjusting the
// connection level flow control receive window to take into account the total
@@ -1041,6 +1042,41 @@ TEST_P(QuicSessionTestClient, AvailableStreamsClient) {
EXPECT_FALSE(QuicSessionPeer::IsStreamAvailable(&session_, 5));
}
+TEST_P(QuicSessionTestClient, RecordFinAfterReadSideClosed) {
+ // Verify that an incoming FIN is recorded in a stream object even if the read
+ // side has been closed. This prevents an entry from being made in
+ // locally_closed_streams_highest_offset_ (which will never be deleted).
+ TestStream* stream = session_.CreateOutgoingDynamicStream();
+ QuicStreamId stream_id = stream->id();
+
+ // Close the read side manually.
+ ReliableQuicStreamPeer::CloseReadSide(stream);
+
+ // Receive a stream data frame with FIN.
+ QuicStreamFrame frame(stream_id, true, 0, StringPiece());
+ session_.OnStreamFrame(frame);
+ if (FLAGS_quic_fix_fin_accounting) {
+ EXPECT_TRUE(stream->fin_received());
+ }
+
+ // Reset stream locally.
+ EXPECT_CALL(*connection_, SendRstStream(stream->id(), _, _));
+ stream->Reset(QUIC_STREAM_CANCELLED);
+ EXPECT_TRUE(ReliableQuicStreamPeer::read_side_closed(stream));
+
+ // Allow the session to delete the stream object.
+ session_.PostProcessAfterData();
+ EXPECT_TRUE(connection_->connected());
+ EXPECT_TRUE(QuicSessionPeer::IsStreamClosed(&session_, stream_id));
+ EXPECT_EQ(nullptr, QuicSessionPeer::dynamic_streams(&session_)[stream_id]);
+
+ // Verify that there is no entry for the stream in
+ // locally_closed_streams_highest_offset_.
+ EXPECT_EQ(
+ FLAGS_quic_fix_fin_accounting ? 0u : 1u,
+ QuicSessionPeer::GetLocallyClosedStreamsHighestOffset(&session_).size());
+}
+
} // namespace
} // namespace test
} // namespace net
diff --git a/net/quic/quic_stream_sequencer.cc b/net/quic/quic_stream_sequencer.cc
index e916b21..a237397 100644
--- a/net/quic/quic_stream_sequencer.cc
+++ b/net/quic/quic_stream_sequencer.cc
@@ -10,9 +10,9 @@
#include "base/logging.h"
#include "net/quic/quic_clock.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/quic_frame_list.h"
#include "net/quic/reliable_quic_stream.h"
-#include "net/quic/reliable_quic_stream.h"
using std::min;
using std::numeric_limits;
@@ -30,7 +30,8 @@ QuicStreamSequencer::QuicStreamSequencer(ReliableQuicStream* quic_stream,
num_frames_received_(0),
num_duplicate_frames_received_(0),
num_early_frames_received_(0),
- clock_(clock) {}
+ clock_(clock),
+ ignore_read_data_(false) {}
QuicStreamSequencer::~QuicStreamSequencer() {}
@@ -77,7 +78,11 @@ void QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) {
}
if (byte_offset == num_bytes_consumed_) {
- stream_->OnDataAvailable();
+ if (FLAGS_quic_implement_stop_reading && ignore_read_data_) {
+ FlushBufferedFrames();
+ } else {
+ stream_->OnDataAvailable();
+ }
}
}
@@ -96,18 +101,25 @@ void QuicStreamSequencer::CloseStreamAtOffset(QuicStreamOffset offset) {
}
bool QuicStreamSequencer::MaybeCloseStream() {
- if (!blocked_ && IsClosed()) {
- DVLOG(1) << "Passing up termination, as we've processed "
- << num_bytes_consumed_ << " 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
- // equal, but error handling seems silly at this point.
+ if (blocked_ || !IsClosed()) {
+ return false;
+ }
+
+ DVLOG(1) << "Passing up termination, as we've processed "
+ << num_bytes_consumed_ << " 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
+ // equal to |close_offset|, but error handling seems silly at this point.
+ if (FLAGS_quic_implement_stop_reading && ignore_read_data_) {
+ // The sequencer is discarding stream data and must notify the stream on
+ // receipt of a FIN because the consumer won't.
+ stream_->OnFinRead();
+ } else {
stream_->OnDataAvailable();
- buffered_frames_.Clear();
- num_bytes_buffered_ = 0;
- return true;
}
- return false;
+ buffered_frames_.Clear();
+ num_bytes_buffered_ = 0;
+ return true;
}
int QuicStreamSequencer::GetReadableRegions(iovec* iov, size_t iov_len) const {
@@ -161,6 +173,23 @@ void QuicStreamSequencer::SetUnblocked() {
}
}
+void QuicStreamSequencer::StopReading() {
+ if (ignore_read_data_) {
+ return;
+ }
+ ignore_read_data_ = true;
+ FlushBufferedFrames();
+}
+
+void QuicStreamSequencer::FlushBufferedFrames() {
+ DCHECK(ignore_read_data_);
+ size_t bytes_flushed = buffered_frames_.FlushBufferedFrames();
+ DVLOG(1) << "Flushing buffered data at offset " << num_bytes_consumed_
+ << " length " << bytes_flushed << " for stream " << stream_->id();
+ RecordBytesConsumed(bytes_flushed);
+ MaybeCloseStream();
+}
+
void QuicStreamSequencer::RecordBytesConsumed(size_t bytes_consumed) {
num_bytes_consumed_ += bytes_consumed;
num_bytes_buffered_ -= bytes_consumed;
diff --git a/net/quic/quic_stream_sequencer.h b/net/quic/quic_stream_sequencer.h
index c73ed47..722305f 100644
--- a/net/quic/quic_stream_sequencer.h
+++ b/net/quic/quic_stream_sequencer.h
@@ -73,6 +73,11 @@ class NET_EXPORT_PRIVATE QuicStreamSequencer {
// Blocks processing of frames until |SetUnblocked| is called.
void SetBlockedUntilFlush();
+ // Sets the sequencer to discard all incoming data itself and not call
+ // |stream_->OnDataAvailable()|. |stream_->OnFinRead()| will be called
+ // automatically when the FIN is consumed (which may be immediately).
+ void StopReading();
+
size_t num_bytes_buffered() const { return num_bytes_buffered_; }
QuicStreamOffset num_bytes_consumed() const { return num_bytes_consumed_; }
@@ -84,9 +89,15 @@ class NET_EXPORT_PRIVATE QuicStreamSequencer {
int num_early_frames_received() const { return num_early_frames_received_; }
+ bool ignore_read_data() const { return ignore_read_data_; }
+
private:
friend class test::QuicStreamSequencerPeer;
+ // Deletes and records as consumed any buffered data that is now in-sequence.
+ // (To be called only after StopReading has been called.)
+ void FlushBufferedFrames();
+
// Wait until we've seen 'offset' bytes, and then terminate the stream.
void CloseStreamAtOffset(QuicStreamOffset offset);
@@ -131,6 +142,9 @@ class NET_EXPORT_PRIVATE QuicStreamSequencer {
// Not owned.
const QuicClock* clock_;
+ // If true, all incoming data will be discarded.
+ bool ignore_read_data_;
+
DISALLOW_COPY_AND_ASSIGN(QuicStreamSequencer);
};
diff --git a/net/quic/reliable_quic_stream.cc b/net/quic/reliable_quic_stream.cc
index df2972c..0c063fb 100644
--- a/net/quic/reliable_quic_stream.cc
+++ b/net/quic/reliable_quic_stream.cc
@@ -174,10 +174,13 @@ void ReliableQuicStream::SetFromConfig() {
void ReliableQuicStream::OnStreamFrame(const QuicStreamFrame& frame) {
DCHECK_EQ(frame.stream_id, id_);
- if (read_side_closed_) {
- DVLOG(1) << ENDPOINT << "Ignoring frame " << frame.stream_id;
- // The subclass does not want read data: blackhole the data.
- return;
+ bool flag_value = FLAGS_quic_fix_fin_accounting;
+ if (!flag_value) {
+ if (read_side_closed_) {
+ DVLOG(1) << ENDPOINT << "Ignoring frame " << frame.stream_id;
+ // The subclass does not want to read data: blackhole the data.
+ return;
+ }
}
if (!FLAGS_quic_stop_checking_for_mismatch_ids && frame.stream_id != id_) {
@@ -192,6 +195,14 @@ void ReliableQuicStream::OnStreamFrame(const QuicStreamFrame& frame) {
}
}
+ if (flag_value) {
+ if (read_side_closed_) {
+ DVLOG(1) << ENDPOINT << "Ignoring data in frame " << frame.stream_id;
+ // The subclass does not want to read data: blackhole the data.
+ return;
+ }
+ }
+
// This count includes duplicate data received.
size_t frame_payload_size = frame.data.size();
stream_bytes_read_ += frame_payload_size;
@@ -480,6 +491,15 @@ QuicVersion ReliableQuicStream::version() const {
return session_->connection()->version();
}
+void ReliableQuicStream::StopReading() {
+ if (!FLAGS_quic_implement_stop_reading) {
+ CloseReadSide();
+ return;
+ }
+ DVLOG(1) << ENDPOINT << "Stop reading from stream " << id();
+ sequencer_.StopReading();
+}
+
void ReliableQuicStream::OnClose() {
CloseReadSide();
CloseWriteSide();
diff --git a/net/quic/reliable_quic_stream.h b/net/quic/reliable_quic_stream.h
index e6206df..200647c 100644
--- a/net/quic/reliable_quic_stream.h
+++ b/net/quic/reliable_quic_stream.h
@@ -101,7 +101,9 @@ class NET_EXPORT_PRIVATE ReliableQuicStream {
QuicRstStreamErrorCode stream_error() const { return stream_error_; }
QuicErrorCode connection_error() const { return connection_error_; }
- bool read_side_closed() const { return read_side_closed_; }
+ bool reading_stopped() const {
+ return sequencer_.ignore_read_data() || read_side_closed_;
+ }
bool write_side_closed() const { return write_side_closed_; }
uint64 stream_bytes_read() const { return stream_bytes_read_; }
@@ -138,7 +140,7 @@ class NET_EXPORT_PRIVATE ReliableQuicStream {
// it was blocked before.
void UpdateSendWindowOffset(QuicStreamOffset new_offset);
- // Returns true if the stream have received either a RST_STREAM or a FIN -
+ // Returns true if the stream has received either a RST_STREAM or a FIN -
// either of which gives a definitive number of bytes which the peer has
// sent. If this is not true on deletion of the stream object, the session
// must keep track of the stream's byte offset until a definitive final value
@@ -155,6 +157,14 @@ class NET_EXPORT_PRIVATE ReliableQuicStream {
bool fin_received() const { return fin_received_; }
+ // Sets the sequencer to consume all incoming data itself and not call
+ // OnDataAvailable().
+ // When the FIN is received, the stream will be notified automatically (via
+ // OnFinRead()) (which may happen during the call of StopReading()).
+ // TODO(dworley): There should be machinery to send a RST_STREAM/NO_ERROR and
+ // stop sending stream-level flow-control updates when this end sends FIN.
+ virtual void StopReading();
+
protected:
// Sends as much of 'data' to the connection as the connection will consume,
// and then buffers any remaining data in queued_data_.
@@ -174,11 +184,6 @@ class NET_EXPORT_PRIVATE ReliableQuicStream {
bool fin,
QuicAckListenerInterface* ack_notifier_delegate);
- // Close the read side of the stream. Further incoming stream frames will be
- // discarded. Can be called by the subclass or internally.
- // May cause the stream to be closed.
- virtual void CloseReadSide();
-
// Close the write side of the socket. Further writes will fail.
// Can be called by the subclass or internally.
// Does not send a FIN. May cause the stream to be closed.
@@ -204,6 +209,13 @@ class NET_EXPORT_PRIVATE ReliableQuicStream {
friend class QuicStreamUtils;
class ProxyAckNotifierDelegate;
+ // Close the read side of the socket. May cause the stream to be closed.
+ // Subclasses and consumers should use StopReading to terminate reading early.
+ void CloseReadSide();
+
+ // Subclasses and consumers should use reading_stopped.
+ bool read_side_closed() const { return read_side_closed_; }
+
struct PendingData {
PendingData(std::string data_in,
scoped_refptr<ProxyAckNotifierDelegate> delegate_in);
diff --git a/net/quic/reliable_quic_stream_test.cc b/net/quic/reliable_quic_stream_test.cc
index e49b275..fdc2e54 100644
--- a/net/quic/reliable_quic_stream_test.cc
+++ b/net/quic/reliable_quic_stream_test.cc
@@ -6,6 +6,7 @@
#include "net/quic/quic_ack_notifier.h"
#include "net/quic/quic_connection.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/quic_utils.h"
#include "net/quic/quic_write_blocked_list.h"
#include "net/quic/spdy_utils.h"
@@ -23,6 +24,7 @@ using base::StringPiece;
using std::min;
using std::string;
using testing::AnyNumber;
+using testing::AtLeast;
using testing::CreateFunctor;
using testing::InSequence;
using testing::Invoke;
@@ -63,7 +65,6 @@ class TestStream : public ReliableQuicStream {
}
using ReliableQuicStream::WriteOrBufferData;
- using ReliableQuicStream::CloseReadSide;
using ReliableQuicStream::CloseWriteSide;
using ReliableQuicStream::OnClose;
@@ -315,7 +316,7 @@ TEST_F(ReliableQuicStreamTest, WriteOrBufferDataWithFecProtectOptional) {
TEST_F(ReliableQuicStreamTest, ConnectionCloseAfterStreamClose) {
Initialize(kShouldProcessData);
- stream_->CloseReadSide();
+ ReliableQuicStreamPeer::CloseReadSide(stream_);
stream_->CloseWriteSide();
EXPECT_EQ(QUIC_STREAM_NO_ERROR, stream_->stream_error());
EXPECT_EQ(QUIC_NO_ERROR, stream_->connection_error());
@@ -623,6 +624,35 @@ TEST_F(ReliableQuicStreamTest,
stream_->OnStreamFrame(frame);
}
+// Verify that after the consumer calls StopReading(), the stream still sends
+// flow control updates.
+TEST_F(ReliableQuicStreamTest, StopReadingSendsFlowControl) {
+ if (!FLAGS_quic_implement_stop_reading) {
+ return;
+ }
+
+ Initialize(kShouldProcessData);
+
+ stream_->StopReading();
+
+ // Connection should not get terminated due to flow control errors.
+ EXPECT_CALL(*connection_,
+ SendConnectionClose(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA))
+ .Times(0);
+ EXPECT_CALL(*connection_, SendWindowUpdate(_, _)).Times(AtLeast(1));
+
+ string data(1000, 'x');
+ for (QuicStreamOffset offset = 0;
+ offset < 2 * kInitialStreamFlowControlWindowForTest;
+ offset += data.length()) {
+ QuicStreamFrame frame(stream_->id(), false, offset, data);
+ stream_->OnStreamFrame(frame);
+ }
+ EXPECT_LT(
+ kInitialStreamFlowControlWindowForTest,
+ QuicFlowControllerPeer::ReceiveWindowOffset(stream_->flow_controller()));
+}
+
TEST_F(ReliableQuicStreamTest, FinalByteOffsetFromFin) {
Initialize(kShouldProcessData);
@@ -658,7 +688,8 @@ TEST_F(ReliableQuicStreamTest, SetDrainingIncomingOutgoing) {
stream_->OnStreamFrame(stream_frame_with_fin);
// The FIN has been received but not consumed.
EXPECT_TRUE(stream_->HasFinalReceivedByteOffset());
- EXPECT_FALSE(stream_->read_side_closed());
+ EXPECT_FALSE(ReliableQuicStreamPeer::read_side_closed(stream_));
+ EXPECT_FALSE(stream_->reading_stopped());
EXPECT_EQ(1u, session_->GetNumOpenStreams());
@@ -691,7 +722,8 @@ TEST_F(ReliableQuicStreamTest, SetDrainingOutgoingIncoming) {
stream_->OnStreamFrame(stream_frame_with_fin);
// The FIN has been received but not consumed.
EXPECT_TRUE(stream_->HasFinalReceivedByteOffset());
- EXPECT_FALSE(stream_->read_side_closed());
+ EXPECT_FALSE(ReliableQuicStreamPeer::read_side_closed(stream_));
+ EXPECT_FALSE(stream_->reading_stopped());
EXPECT_EQ(1u, QuicSessionPeer::GetDrainingStreams(session_.get())
->count(kTestStreamId));
@@ -711,6 +743,46 @@ TEST_F(ReliableQuicStreamTest, FecSendPolicyReceivedConnectionOption) {
EXPECT_EQ(FEC_PROTECT_ALWAYS, stream_->fec_policy());
}
+static QuicConsumedData ConsumeAllData(
+ QuicStreamId id,
+ const QuicIOVector& data,
+ QuicStreamOffset offset,
+ bool fin,
+ FecProtection fec_protection,
+ QuicAckListenerInterface* ack_notifier_delegate) {
+ return QuicConsumedData(data.total_length, fin);
+}
+
+TEST_F(ReliableQuicStreamTest, EarlyResponseFinHandling) {
+ // Verify that if the server completes the response before reading the end of
+ // the request, the received FIN is recorded.
+
+ Initialize(kShouldProcessData);
+ EXPECT_CALL(*connection_, SendConnectionClose(_)).Times(0);
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _))
+ .WillRepeatedly(Invoke(ConsumeAllData));
+
+ // Receive data for the request.
+ QuicStreamFrame frame1(stream_->id(), false, 0, StringPiece("Start"));
+ stream_->OnStreamFrame(frame1);
+ // When QuicSpdyServerStream sends the response, it calls
+ // ReliableQuicStream::CloseReadSide() first.
+ ReliableQuicStreamPeer::CloseReadSide(stream_);
+ // Send data and FIN for the response.
+ stream_->WriteOrBufferData(kData1, false, nullptr);
+ EXPECT_TRUE(ReliableQuicStreamPeer::read_side_closed(stream_));
+ // Receive remaining data and FIN for the request.
+ QuicStreamFrame frame2(stream_->id(), true, 0, StringPiece("End"));
+ stream_->OnStreamFrame(frame2);
+ if (FLAGS_quic_fix_fin_accounting) {
+ EXPECT_TRUE(stream_->fin_received());
+ EXPECT_TRUE(stream_->HasFinalReceivedByteOffset());
+ } else {
+ EXPECT_FALSE(stream_->fin_received());
+ EXPECT_FALSE(stream_->HasFinalReceivedByteOffset());
+ }
+}
+
} // namespace
} // namespace test
} // namespace net
diff --git a/net/quic/test_tools/quic_session_peer.cc b/net/quic/test_tools/quic_session_peer.cc
index 05655d1..3a8d019 100644
--- a/net/quic/test_tools/quic_session_peer.cc
+++ b/net/quic/test_tools/quic_session_peer.cc
@@ -54,6 +54,16 @@ QuicSessionPeer::GetLocallyClosedStreamsHighestOffset(QuicSession* session) {
}
// static
+QuicSession::StreamMap& QuicSessionPeer::static_streams(QuicSession* session) {
+ return session->static_streams();
+}
+
+// static
+QuicSession::StreamMap& QuicSessionPeer::dynamic_streams(QuicSession* session) {
+ return session->dynamic_streams();
+}
+
+// static
base::hash_set<QuicStreamId>* QuicSessionPeer::GetDrainingStreams(
QuicSession* session) {
return &session->draining_streams_;
diff --git a/net/quic/test_tools/quic_session_peer.h b/net/quic/test_tools/quic_session_peer.h
index 7b2fed2..190d2c9 100644
--- a/net/quic/test_tools/quic_session_peer.h
+++ b/net/quic/test_tools/quic_session_peer.h
@@ -9,6 +9,7 @@
#include "base/containers/hash_tables.h"
#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_session.h"
#include "net/quic/quic_write_blocked_list.h"
namespace net {
@@ -32,6 +33,8 @@ class QuicSessionPeer {
QuicStreamId stream_id);
static std::map<QuicStreamId, QuicStreamOffset>&
GetLocallyClosedStreamsHighestOffset(QuicSession* session);
+ static QuicSession::StreamMap& static_streams(QuicSession* session);
+ static QuicSession::StreamMap& dynamic_streams(QuicSession* session);
static base::hash_set<QuicStreamId>* GetDrainingStreams(QuicSession* session);
// Discern the state of a stream. Exactly one of these should be true at a
diff --git a/net/quic/test_tools/reliable_quic_stream_peer.cc b/net/quic/test_tools/reliable_quic_stream_peer.cc
index 6218814..a4a52be 100644
--- a/net/quic/test_tools/reliable_quic_stream_peer.cc
+++ b/net/quic/test_tools/reliable_quic_stream_peer.cc
@@ -27,6 +27,11 @@ void ReliableQuicStreamPeer::SetStreamBytesWritten(
}
// static
+bool ReliableQuicStreamPeer::read_side_closed(ReliableQuicStream* stream) {
+ return stream->read_side_closed();
+}
+
+// static
void ReliableQuicStreamPeer::CloseReadSide(ReliableQuicStream* stream) {
stream->CloseReadSide();
}
@@ -37,11 +42,31 @@ bool ReliableQuicStreamPeer::FinSent(ReliableQuicStream* stream) {
}
// static
+bool ReliableQuicStreamPeer::FinReceived(ReliableQuicStream* stream) {
+ return stream->fin_received_;
+}
+
+// static
bool ReliableQuicStreamPeer::RstSent(ReliableQuicStream* stream) {
return stream->rst_sent_;
}
// static
+bool ReliableQuicStreamPeer::RstReceived(ReliableQuicStream* stream) {
+ return stream->rst_received_;
+}
+
+// static
+bool ReliableQuicStreamPeer::ReadSideClosed(ReliableQuicStream* stream) {
+ return stream->read_side_closed_;
+}
+
+// static
+bool ReliableQuicStreamPeer::WriteSideClosed(ReliableQuicStream* stream) {
+ return stream->write_side_closed_;
+}
+
+// static
uint32 ReliableQuicStreamPeer::SizeOfQueuedData(ReliableQuicStream* stream) {
uint32 total = 0;
std::list<ReliableQuicStream::PendingData>::iterator it =
diff --git a/net/quic/test_tools/reliable_quic_stream_peer.h b/net/quic/test_tools/reliable_quic_stream_peer.h
index cfeb4e6..7a2f611 100644
--- a/net/quic/test_tools/reliable_quic_stream_peer.h
+++ b/net/quic/test_tools/reliable_quic_stream_peer.h
@@ -21,10 +21,16 @@ class ReliableQuicStreamPeer {
static void SetWriteSideClosed(bool value, ReliableQuicStream* stream);
static void SetStreamBytesWritten(QuicStreamOffset stream_bytes_written,
ReliableQuicStream* stream);
+ static bool read_side_closed(ReliableQuicStream* stream);
static void CloseReadSide(ReliableQuicStream* stream);
static bool FinSent(ReliableQuicStream* stream);
+ static bool FinReceived(ReliableQuicStream* stream);
static bool RstSent(ReliableQuicStream* stream);
+ static bool RstReceived(ReliableQuicStream* stream);
+
+ static bool ReadSideClosed(ReliableQuicStream* stream);
+ static bool WriteSideClosed(ReliableQuicStream* stream);
static uint32 SizeOfQueuedData(ReliableQuicStream* stream);
diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc
index 6f16de5..40480d6 100644
--- a/net/tools/quic/end_to_end_test.cc
+++ b/net/tools/quic/end_to_end_test.cc
@@ -39,6 +39,7 @@
#include "net/tools/quic/quic_server.h"
#include "net/tools/quic/quic_socket_utils.h"
#include "net/tools/quic/quic_spdy_client_stream.h"
+#include "net/tools/quic/quic_spdy_server_stream.h"
#include "net/tools/quic/test_tools/http_message.h"
#include "net/tools/quic/test_tools/packet_dropping_test_writer.h"
#include "net/tools/quic/test_tools/quic_client_peer.h"
@@ -46,6 +47,7 @@
#include "net/tools/quic/test_tools/quic_in_memory_cache_peer.h"
#include "net/tools/quic/test_tools/quic_server_peer.h"
#include "net/tools/quic/test_tools/quic_test_client.h"
+#include "net/tools/quic/test_tools/quic_test_server.h"
#include "net/tools/quic/test_tools/server_thread.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -237,7 +239,8 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> {
server_address_(IPEndPoint(Loopback4(), 0)),
server_hostname_("example.com"),
server_started_(false),
- strike_register_no_startup_period_(false) {
+ strike_register_no_startup_period_(false),
+ stream_creator_(nullptr) {
client_supported_versions_ = GetParam().client_supported_versions;
server_supported_versions_ = GetParam().server_supported_versions;
negotiated_version_ = GetParam().negotiated_version;
@@ -334,6 +337,7 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> {
// Start the server first, because CreateQuicClient() attempts
// to connect to the server.
StartServer();
+
client_.reset(CreateQuicClient(client_writer_));
if (GetParam().use_fec) {
// Set FecPolicy to always protect data on all streams.
@@ -364,7 +368,7 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> {
void StartServer() {
server_thread_.reset(new ServerThread(
- new QuicServer(server_config_, server_supported_versions_),
+ new QuicTestServer(server_config_, server_supported_versions_),
/*is_secure=*/true, server_address_,
strike_register_no_startup_period_));
server_thread_->Initialize();
@@ -390,6 +394,11 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> {
server_writer_->Initialize(
QuicDispatcherPeer::GetHelper(dispatcher),
new ServerDelegate(packet_writer_factory, dispatcher));
+ if (stream_creator_ != nullptr) {
+ static_cast<QuicTestServer*>(server_thread_->server())
+ ->SetSpdyStreamCreator(stream_creator_);
+ }
+
server_thread_->Start();
server_started_ = true;
}
@@ -487,6 +496,11 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> {
QuicFlowControllerPeer::SendWindowSize(server));
}
+ // Must be called before Initialize to have effect.
+ void SetSpdyStreamCreator(QuicTestServer::StreamCreationFunction function) {
+ stream_creator_ = function;
+ }
+
bool initialized_;
IPEndPoint server_address_;
string server_hostname_;
@@ -501,6 +515,7 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> {
QuicVersionVector server_supported_versions_;
QuicVersion negotiated_version_;
bool strike_register_no_startup_period_;
+ QuicTestServer::StreamCreationFunction stream_creator_;
};
// Run all end to end tests with all supported versions.
@@ -615,7 +630,7 @@ TEST_P(EndToEndTest, PostMissingBytes) {
// This should be detected as stream fin without complete request,
// triggering an error response.
client_->SendCustomSynchronousRequest(request);
- EXPECT_EQ("bad", client_->response_body());
+ EXPECT_EQ(QuicSpdyServerStream::kErrorResponseBody, client_->response_body());
EXPECT_EQ(500u, client_->response_headers()->parsed_response_code());
}
@@ -1375,7 +1390,7 @@ class WrongAddressWriter : public QuicPacketWriterWrapper {
WriteResult WritePacket(const char* buffer,
size_t buf_len,
- const IPAddressNumber& real_self_address,
+ const IPAddressNumber& /*real_self_address*/,
const IPEndPoint& peer_address) override {
// Use wrong address!
return QuicPacketWriterWrapper::WritePacket(
@@ -1923,6 +1938,154 @@ TEST_P(EndToEndTest, BadEncryptedData) {
EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
}
+// A test stream that gives |response_body_| as an error response body.
+class ServerStreamWithErrorResponseBody : public QuicSpdyServerStream {
+ public:
+ ServerStreamWithErrorResponseBody(QuicStreamId id,
+ QuicSpdySession* session,
+ string response_body)
+ : QuicSpdyServerStream(id, session), response_body_(response_body) {}
+
+ ~ServerStreamWithErrorResponseBody() override {}
+
+ protected:
+ void SendErrorResponse() override {
+ DVLOG(1) << "Sending error response for stream " << id();
+ SpdyHeaderBlock headers;
+ headers[":status"] = "500";
+ headers["content-length"] = base::UintToString(response_body_.size());
+ // This method must call CloseReadSide to cause the test case, StopReading
+ // is not sufficient.
+ ReliableQuicStreamPeer::CloseReadSide(this);
+ SendHeadersAndBody(headers, response_body_);
+ }
+
+ string response_body_;
+};
+
+TEST_P(EndToEndTest, EarlyResponseFinRecording) {
+ // Verify that an incoming FIN is recorded in a stream object even if the read
+ // side has been closed. This prevents an entry from being made in
+ // locally_close_streams_highest_offset_ (which will never be deleted).
+ // To set up the test condition, the server must do the following in order:
+ // start sending the response and call CloseReadSide
+ // receive the FIN of the request
+ // send the FIN of the response
+
+ string response_body;
+ // The response body must be larger than the flow control window so the server
+ // must receive a window update from the client before it can finish sending
+ // it.
+ uint32 response_body_size =
+ 2 * client_config_.GetInitialStreamFlowControlWindowToSend();
+ GenerateBody(&response_body, response_body_size);
+ SetSpdyStreamCreator([response_body](QuicStreamId id,
+ QuicSpdySession* session) {
+ return new ServerStreamWithErrorResponseBody(id, session, response_body);
+ });
+
+ ASSERT_TRUE(Initialize());
+
+ client_->client()->WaitForCryptoHandshakeConfirmed();
+
+ // A POST that gets an early error response, after the headers are received
+ // and before the body is received, due to invalid content-length.
+ HTTPMessage request(HttpConstants::HTTP_1_1, HttpConstants::POST, "/garbage");
+ // The body must be large enough that the FIN will be in a different packet
+ // than the end of the headers, but short enough to not require a flow control
+ // update. This allows headers processing to trigger the error response
+ // before the request FIN is processed but receive the request FIN before the
+ // response is sent completely.
+ const uint32 kRequestBodySize = kMaxPacketSize + 10;
+ string request_body;
+ GenerateBody(&request_body, kRequestBodySize);
+ request.AddBody(request_body, false);
+ // Set an invalid content-length, so the request will receive an early 500
+ // response. Must be done after AddBody, which also sets content-length.
+ request.AddHeader("content-length", "-1");
+ request.set_skip_message_validation(true);
+
+ // Send the request.
+ client_->SendMessage(request);
+ client_->WaitForResponse();
+ EXPECT_EQ(500u, client_->response_headers()->parsed_response_code());
+
+ // Pause the server so we can access the server's internals without races.
+ server_thread_->Pause();
+
+ QuicDispatcher* dispatcher =
+ QuicServerPeer::GetDispatcher(server_thread_->server());
+ QuicDispatcher::SessionMap const& map =
+ QuicDispatcherPeer::session_map(dispatcher);
+ QuicDispatcher::SessionMap::const_iterator it = map.begin();
+ EXPECT_TRUE(it != map.end());
+ QuicServerSession* server_session = it->second;
+
+ // Verify that the stream is not pending the arrival of the peer's final
+ // offset.
+ if (FLAGS_quic_fix_fin_accounting) {
+ EXPECT_EQ(0u, QuicSessionPeer::GetLocallyClosedStreamsHighestOffset(
+ server_session)
+ .size());
+ } else {
+ EXPECT_EQ(1u, QuicSessionPeer::GetLocallyClosedStreamsHighestOffset(
+ server_session)
+ .size());
+ }
+
+ server_thread_->Resume();
+}
+
+TEST_P(EndToEndTest, LargePostEarlyResponse) {
+ const uint32 kWindowSize = 65536;
+ set_client_initial_stream_flow_control_receive_window(kWindowSize);
+ set_client_initial_session_flow_control_receive_window(kWindowSize);
+ set_server_initial_stream_flow_control_receive_window(kWindowSize);
+ set_server_initial_session_flow_control_receive_window(kWindowSize);
+
+ ASSERT_TRUE(Initialize());
+
+ client_->client()->WaitForCryptoHandshakeConfirmed();
+
+ // POST to a URL that gets an early error response, after the headers are
+ // received and before the body is received.
+ HTTPMessage request(HttpConstants::HTTP_1_1, HttpConstants::POST, "/garbage");
+ const uint32 kBodySize = 2 * kWindowSize;
+ // Invalid content-length so the request will receive an early 500 response.
+ request.AddHeader("content-length", "-1");
+ request.set_skip_message_validation(true);
+ request.set_has_complete_message(false);
+
+ // Tell the client to not close the stream if it receives an early response.
+ client_->set_allow_bidirectional_data(true);
+ // Send the headers.
+ client_->SendMessage(request);
+ // Receive the response and let the server close writing.
+ client_->WaitForInitialResponse();
+ EXPECT_EQ(500u, client_->response_headers()->parsed_response_code());
+ // Send a body larger than the stream flow control window.
+ string body;
+ GenerateBody(&body, kBodySize);
+ client_->SendData(body, true);
+
+ if (FLAGS_quic_implement_stop_reading) {
+ // Run the client to let any buffered data be sent.
+ // (This is OK despite already waiting for a response.)
+ client_->WaitForResponse();
+ // There should be no buffered data to write in the client's stream.
+ ReliableQuicStream* stream = client_->client()->session()->GetStream(5);
+ EXPECT_FALSE(stream != nullptr && stream->HasBufferedData());
+ } else {
+ // Run the client for 0.1 second to let any buffered data be sent.
+ // Must have a timeout, as the stream will not close and cause a return.
+ // (This is OK despite already waiting for a response.)
+ client_->WaitForResponseForMs(static_cast<int64>(100));
+ // There will be buffered data to write in the client's stream.
+ ReliableQuicStream* stream = client_->client()->session()->GetStream(5);
+ EXPECT_TRUE(stream != nullptr && stream->HasBufferedData());
+ }
+}
+
} // namespace
} // namespace test
} // namespace tools
diff --git a/net/tools/quic/quic_spdy_client_stream.cc b/net/tools/quic/quic_spdy_client_stream.cc
index 7e95414..5113c48 100644
--- a/net/tools/quic/quic_spdy_client_stream.cc
+++ b/net/tools/quic/quic_spdy_client_stream.cc
@@ -25,14 +25,14 @@ QuicSpdyClientStream::QuicSpdyClientStream(QuicStreamId id,
content_length_(-1),
response_code_(0),
header_bytes_read_(0),
- header_bytes_written_(0) {
-}
+ header_bytes_written_(0),
+ allow_bidirectional_data_(false) {}
QuicSpdyClientStream::~QuicSpdyClientStream() {
}
void QuicSpdyClientStream::OnStreamFrame(const QuicStreamFrame& frame) {
- if (!write_side_closed()) {
+ if (!allow_bidirectional_data_ && !write_side_closed()) {
DVLOG(1) << "Got a response before the request was complete. "
<< "Aborting request.";
CloseWriteSide();
diff --git a/net/tools/quic/quic_spdy_client_stream.h b/net/tools/quic/quic_spdy_client_stream.h
index 6f4e9a9..1823369 100644
--- a/net/tools/quic/quic_spdy_client_stream.h
+++ b/net/tools/quic/quic_spdy_client_stream.h
@@ -67,6 +67,12 @@ class QuicSpdyClientStream : public QuicDataStream {
// of client-side streams should be able to set the priority.
using QuicDataStream::set_priority;
+ void set_allow_bidirectional_data(bool value) {
+ allow_bidirectional_data_ = value;
+ }
+
+ bool allow_bidirectional_data() const { return allow_bidirectional_data_; }
+
private:
bool ParseResponseHeaders(const char* data, uint32 data_len);
@@ -78,6 +84,9 @@ class QuicSpdyClientStream : public QuicDataStream {
std::string data_;
size_t header_bytes_read_;
size_t header_bytes_written_;
+ // When true allows the sending of a request to continue while the response is
+ // arriving.
+ bool allow_bidirectional_data_;
DISALLOW_COPY_AND_ASSIGN(QuicSpdyClientStream);
};
diff --git a/net/tools/quic/quic_spdy_server_stream.cc b/net/tools/quic/quic_spdy_server_stream.cc
index 6925b27..7510f7d 100644
--- a/net/tools/quic/quic_spdy_server_stream.cc
+++ b/net/tools/quic/quic_spdy_server_stream.cc
@@ -10,6 +10,7 @@
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
#include "net/quic/quic_data_stream.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/quic_spdy_session.h"
#include "net/quic/spdy_utils.h"
#include "net/spdy/spdy_protocol.h"
@@ -167,16 +168,19 @@ void QuicSpdyServerStream::SendErrorResponse() {
DVLOG(1) << "Sending error response for stream " << id();
SpdyHeaderBlock headers;
headers[":status"] = "500";
- headers["content-length"] = "3";
- SendHeadersAndBody(headers, "bad");
+ headers["content-length"] = base::UintToString(strlen(kErrorResponseBody));
+ SendHeadersAndBody(headers, kErrorResponseBody);
}
void QuicSpdyServerStream::SendHeadersAndBody(
const SpdyHeaderBlock& response_headers,
StringPiece body) {
- // We only support SPDY and HTTP, and neither handles bidirectional streaming.
- if (!read_side_closed()) {
- CloseReadSide();
+ // This server only supports SPDY and HTTP, and neither handles bidirectional
+ // streaming.
+ if (!reading_stopped()) {
+ // If FLAGS_quic_implement_stop_reading is false,
+ // behaves as ReliableQuicStream::CloseReadSide().
+ StopReading();
}
WriteHeaders(response_headers, body.empty(), nullptr);
@@ -186,5 +190,7 @@ void QuicSpdyServerStream::SendHeadersAndBody(
}
}
+const char* const QuicSpdyServerStream::kErrorResponseBody = "bad";
+
} // namespace tools
} // namespace net
diff --git a/net/tools/quic/quic_spdy_server_stream.h b/net/tools/quic/quic_spdy_server_stream.h
index 0377ef5..8f81728 100644
--- a/net/tools/quic/quic_spdy_server_stream.h
+++ b/net/tools/quic/quic_spdy_server_stream.h
@@ -36,11 +36,18 @@ class QuicSpdyServerStream : public QuicDataStream {
// data (or a FIN) to be read.
void OnDataAvailable() override;
+ // The response body of error responses.
+ static const char* const kErrorResponseBody;
+
protected:
// Sends a basic 200 response using SendHeaders for the headers and WriteData
// for the body.
virtual void SendResponse();
+ // Sends a basic 500 response using SendHeaders for the headers and WriteData
+ // for the body
+ virtual void SendErrorResponse();
+
void SendHeadersAndBody(const SpdyHeaderBlock& response_headers,
base::StringPiece body);
@@ -55,10 +62,6 @@ class QuicSpdyServerStream : public QuicDataStream {
// Returns false if there was an error parsing the headers.
bool ParseRequestHeaders(const char* data, uint32 data_len);
- // Sends a basic 500 response using SendHeaders for the headers and WriteData
- // for the body
- void SendErrorResponse();
-
// The parsed headers received from the client.
SpdyHeaderBlock request_headers_;
int content_length_;
diff --git a/net/tools/quic/quic_spdy_server_stream_test.cc b/net/tools/quic/quic_spdy_server_stream_test.cc
index 4b3e316..23e507c 100644
--- a/net/tools/quic/quic_spdy_server_stream_test.cc
+++ b/net/tools/quic/quic_spdy_server_stream_test.cc
@@ -7,10 +7,12 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "net/quic/quic_connection.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_utils.h"
#include "net/quic/spdy_utils.h"
#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/reliable_quic_stream_peer.h"
#include "net/tools/epoll_server/epoll_server.h"
#include "net/tools/quic/quic_in_memory_cache.h"
#include "net/tools/quic/spdy_balsa_utils.h"
@@ -22,6 +24,7 @@ using base::StringPiece;
using net::test::MockConnection;
using net::test::MockHelper;
using net::test::MockQuicSpdySession;
+using net::test::ReliableQuicStreamPeer;
using net::test::SupportedVersions;
using net::test::kInitialSessionFlowControlWindowForTest;
using net::test::kInitialStreamFlowControlWindowForTest;
@@ -205,7 +208,12 @@ TEST_F(QuicSpdyServerStreamTest, TestSendResponse) {
WillOnce(Return(QuicConsumedData(3, true)));
QuicSpdyServerStreamPeer::SendResponse(stream_.get());
- EXPECT_TRUE(stream_->read_side_closed());
+ if (!FLAGS_quic_implement_stop_reading) {
+ EXPECT_TRUE(ReliableQuicStreamPeer::read_side_closed(stream_.get()));
+ } else {
+ EXPECT_FALSE(ReliableQuicStreamPeer::read_side_closed(stream_.get()));
+ }
+ EXPECT_TRUE(stream_->reading_stopped());
EXPECT_TRUE(stream_->write_side_closed());
}
@@ -220,7 +228,12 @@ TEST_F(QuicSpdyServerStreamTest, TestSendErrorResponse) {
WillOnce(Return(QuicConsumedData(3, true)));
QuicSpdyServerStreamPeer::SendErrorResponse(stream_.get());
- EXPECT_TRUE(stream_->read_side_closed());
+ if (!FLAGS_quic_implement_stop_reading) {
+ EXPECT_TRUE(ReliableQuicStreamPeer::read_side_closed(stream_.get()));
+ } else {
+ EXPECT_FALSE(ReliableQuicStreamPeer::read_side_closed(stream_.get()));
+ }
+ EXPECT_TRUE(stream_->reading_stopped());
EXPECT_TRUE(stream_->write_side_closed());
}
@@ -238,7 +251,12 @@ TEST_F(QuicSpdyServerStreamTest, InvalidMultipleContentLength) {
stream_->OnStreamHeaders(headers_string_);
stream_->OnStreamHeadersComplete(false, headers_string_.size());
- EXPECT_TRUE(stream_->read_side_closed());
+ if (!FLAGS_quic_implement_stop_reading) {
+ EXPECT_TRUE(ReliableQuicStreamPeer::read_side_closed(stream_.get()));
+ } else {
+ EXPECT_FALSE(ReliableQuicStreamPeer::read_side_closed(stream_.get()));
+ }
+ EXPECT_TRUE(stream_->reading_stopped());
EXPECT_TRUE(stream_->write_side_closed());
}
@@ -256,7 +274,12 @@ TEST_F(QuicSpdyServerStreamTest, InvalidLeadingNullContentLength) {
stream_->OnStreamHeaders(headers_string_);
stream_->OnStreamHeadersComplete(false, headers_string_.size());
- EXPECT_TRUE(stream_->read_side_closed());
+ if (!FLAGS_quic_implement_stop_reading) {
+ EXPECT_TRUE(ReliableQuicStreamPeer::read_side_closed(stream_.get()));
+ } else {
+ EXPECT_FALSE(ReliableQuicStreamPeer::read_side_closed(stream_.get()));
+ }
+ EXPECT_TRUE(stream_->reading_stopped());
EXPECT_TRUE(stream_->write_side_closed());
}
@@ -271,7 +294,8 @@ TEST_F(QuicSpdyServerStreamTest, ValidMultipleContentLength) {
stream_->OnStreamHeadersComplete(false, headers_string_.size());
EXPECT_EQ(11, QuicSpdyServerStreamPeer::content_length(stream_.get()));
- EXPECT_FALSE(stream_->read_side_closed());
+ EXPECT_FALSE(ReliableQuicStreamPeer::read_side_closed(stream_.get()));
+ EXPECT_FALSE(stream_->reading_stopped());
EXPECT_FALSE(stream_->write_side_closed());
}
diff --git a/net/tools/quic/test_tools/quic_dispatcher_peer.cc b/net/tools/quic/test_tools/quic_dispatcher_peer.cc
index 02ca0bf..88a3078 100644
--- a/net/tools/quic/test_tools/quic_dispatcher_peer.cc
+++ b/net/tools/quic/test_tools/quic_dispatcher_peer.cc
@@ -57,6 +57,12 @@ QuicErrorCode QuicDispatcherPeer::GetAndClearLastError(
return ret;
}
+// static
+const QuicDispatcher::SessionMap& QuicDispatcherPeer::session_map(
+ QuicDispatcher* dispatcher) {
+ return dispatcher->session_map();
+}
+
} // namespace test
} // namespace tools
} // namespace net
diff --git a/net/tools/quic/test_tools/quic_dispatcher_peer.h b/net/tools/quic/test_tools/quic_dispatcher_peer.h
index b2b9451..91ae56b 100644
--- a/net/tools/quic/test_tools/quic_dispatcher_peer.h
+++ b/net/tools/quic/test_tools/quic_dispatcher_peer.h
@@ -41,6 +41,9 @@ class QuicDispatcherPeer {
// visitor's OnError() method. Then set that record to QUIC_NO_ERROR.
static QuicErrorCode GetAndClearLastError(QuicDispatcher* dispatcher);
+ static const QuicDispatcher::SessionMap& session_map(
+ QuicDispatcher* dispatcher);
+
private:
DISALLOW_COPY_AND_ASSIGN(QuicDispatcherPeer);
};
diff --git a/net/tools/quic/test_tools/quic_test_client.cc b/net/tools/quic/test_tools/quic_test_client.cc
index 37fcd0c..62984d9 100644
--- a/net/tools/quic/test_tools/quic_test_client.cc
+++ b/net/tools/quic/test_tools/quic_test_client.cc
@@ -181,11 +181,12 @@ QuicTestClient::QuicTestClient(IPEndPoint server_address,
PRIVACY_MODE_DISABLED),
config,
supported_versions,
- &epoll_server_)) {
+ &epoll_server_)),
+ allow_bidirectional_data_(false) {
Initialize(secure);
}
-QuicTestClient::QuicTestClient() {}
+QuicTestClient::QuicTestClient() : allow_bidirectional_data_(false) {}
QuicTestClient::~QuicTestClient() {
if (stream_) {
@@ -267,7 +268,7 @@ ssize_t QuicTestClient::GetOrCreateStreamAndSendRequest(
}
ret = stream->SendRequest(spdy_headers, body, fin);
} else {
- stream->SendBody(body.data(), fin, delegate);
+ stream->SendBody(body.as_string(), fin, delegate);
ret = body.length();
}
if (FLAGS_enable_quic_stateless_reject_support) {
@@ -385,7 +386,9 @@ QuicSpdyClientStream* QuicTestClient::GetOrCreateStream() {
return nullptr;
}
stream_->set_visitor(this);
- reinterpret_cast<QuicSpdyClientStream*>(stream_)->set_priority(priority_);
+ QuicSpdyClientStream* cs = reinterpret_cast<QuicSpdyClientStream*>(stream_);
+ cs->set_priority(priority_);
+ cs->set_allow_bidirectional_data(allow_bidirectional_data_);
// Set FEC policy on stream.
ReliableQuicStreamPeer::SetFecPolicy(stream_, fec_policy_);
}
diff --git a/net/tools/quic/test_tools/quic_test_client.h b/net/tools/quic/test_tools/quic_test_client.h
index c438197..79a8b44 100644
--- a/net/tools/quic/test_tools/quic_test_client.h
+++ b/net/tools/quic/test_tools/quic_test_client.h
@@ -184,6 +184,12 @@ class QuicTestClient : public SimpleClient,
EpollServer* epoll_server() { return &epoll_server_; }
+ void set_allow_bidirectional_data(bool value) {
+ allow_bidirectional_data_ = value;
+ }
+
+ bool allow_bidirectional_data() const { return allow_bidirectional_data_; }
+
protected:
QuicTestClient();
@@ -243,6 +249,9 @@ class QuicTestClient : public SimpleClient,
bool buffer_body_;
// FEC policy for data sent by this client.
FecPolicy fec_policy_;
+ // When true allows the sending of a request to continue while the response is
+ // arriving.
+ bool allow_bidirectional_data_;
// proof_verifier_ points to a RecordingProofVerifier that is owned by
// client_.
ProofVerifier* proof_verifier_;
diff --git a/net/tools/quic/test_tools/quic_test_server.cc b/net/tools/quic/test_tools/quic_test_server.cc
new file mode 100644
index 0000000..aaa1694
--- /dev/null
+++ b/net/tools/quic/test_tools/quic_test_server.cc
@@ -0,0 +1,145 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/quic/test_tools/quic_test_server.h"
+
+#include "base/logging.h"
+#include "base/run_loop.h"
+#include "base/thread_task_runner_handle.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/quic_crypto_server_config.h"
+#include "net/quic/crypto/quic_random.h"
+#include "net/quic/quic_config.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_connection_helper.h"
+#include "net/quic/quic_packet_writer.h"
+#include "net/quic/quic_protocol.h"
+#include "net/tools/quic/quic_dispatcher.h"
+#include "net/tools/quic/quic_epoll_connection_helper.h"
+#include "net/tools/quic/quic_server_session.h"
+#include "net/tools/quic/quic_spdy_server_stream.h"
+
+namespace net {
+namespace tools {
+namespace test {
+
+class CustomStreamSession : public QuicServerSession {
+ public:
+ CustomStreamSession(const QuicConfig& config,
+ QuicConnection* connection,
+ QuicServerSessionVisitor* visitor,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicTestServer::StreamCreationFunction creator)
+ : QuicServerSession(config, connection, visitor, crypto_config),
+ stream_creator_(creator) {}
+
+ QuicSpdyServerStream* CreateIncomingDynamicStream(QuicStreamId id) override {
+ if (!ShouldCreateIncomingDynamicStream(id)) {
+ return nullptr;
+ }
+ return stream_creator_(id, this);
+ }
+
+ private:
+ QuicTestServer::StreamCreationFunction stream_creator_;
+};
+
+class QuicTestDispatcher : public QuicDispatcher {
+ public:
+ QuicTestDispatcher(const QuicConfig& config,
+ const QuicCryptoServerConfig* crypto_config,
+ const QuicVersionVector& versions,
+ PacketWriterFactory* factory,
+ QuicConnectionHelperInterface* helper)
+ : QuicDispatcher(config, crypto_config, versions, factory, helper) {}
+ QuicServerSession* CreateQuicSession(QuicConnectionId id,
+ const IPEndPoint& client) override {
+ if (session_creator_ == nullptr && stream_creator_ == nullptr) {
+ return QuicDispatcher::CreateQuicSession(id, client);
+ }
+ QuicConnection* connection =
+ new QuicConnection(id, client, helper(), connection_writer_factory(),
+ /* owns_writer= */ true, Perspective::IS_SERVER,
+ /* is_secure */ true, supported_versions());
+
+ QuicServerSession* session = nullptr;
+ if (stream_creator_ != nullptr) {
+ session = new CustomStreamSession(config(), connection, this,
+ crypto_config(), stream_creator_);
+ } else {
+ session = session_creator_(config(), connection, this, crypto_config());
+ }
+ session->Initialize();
+ return session;
+ }
+
+ void set_session_creator(QuicTestServer::SessionCreationFunction function) {
+ DCHECK(session_creator_ == nullptr);
+ DCHECK(stream_creator_ == nullptr);
+ // TODO(rtenneti): use std::move when chromium supports it.
+ // session_creator_ = std::move(function);
+ session_creator_ = function;
+ }
+
+ void set_stream_creator(QuicTestServer::StreamCreationFunction function) {
+ DCHECK(session_creator_ == nullptr);
+ DCHECK(stream_creator_ == nullptr);
+ // TODO(rtenneti): use std::move when chromium supports it.
+ // stream_creator_ = std::move(function);
+ stream_creator_ = function;
+ }
+
+ QuicTestServer::SessionCreationFunction session_creator() {
+ return session_creator_;
+ }
+
+ QuicTestServer::StreamCreationFunction stream_creator() {
+ return stream_creator_;
+ }
+
+ private:
+ QuicTestServer::SessionCreationFunction session_creator_;
+ QuicTestServer::StreamCreationFunction stream_creator_;
+};
+
+QuicTestServer::QuicTestServer() : QuicServer() {}
+
+QuicTestServer::QuicTestServer(const QuicConfig& config,
+ const QuicVersionVector& supported_versions)
+ : QuicServer(config, supported_versions) {}
+
+QuicDispatcher* QuicTestServer::CreateQuicDispatcher() {
+ return new QuicTestDispatcher(
+ config(), &crypto_config(), supported_versions(),
+ new QuicDispatcher::DefaultPacketWriterFactory(),
+ new QuicEpollConnectionHelper(epoll_server()));
+}
+
+void QuicTestServer::SetSessionCreator(SessionCreationFunction function) {
+ static_cast<QuicTestDispatcher*>(dispatcher())
+ ->set_session_creator(std::move(function));
+}
+
+void QuicTestServer::SetSpdyStreamCreator(StreamCreationFunction function) {
+ DCHECK(dispatcher());
+ static_cast<QuicTestDispatcher*>(dispatcher())
+ ->set_stream_creator(std::move(function));
+}
+
+/////////////////////////// TEST SESSIONS ///////////////////////////////
+
+ImmediateGoAwaySession::ImmediateGoAwaySession(
+ const QuicConfig& config,
+ QuicConnection* connection,
+ QuicServerSessionVisitor* visitor,
+ const QuicCryptoServerConfig* crypto_config)
+ : QuicServerSession(config, connection, visitor, crypto_config) {
+ SendGoAway(QUIC_PEER_GOING_AWAY, "");
+}
+
+} // namespace test
+} // namespace tools
+} // namespace net
diff --git a/net/tools/quic/test_tools/quic_test_server.h b/net/tools/quic/test_tools/quic_test_server.h
new file mode 100644
index 0000000..ce593cf
--- /dev/null
+++ b/net/tools/quic/test_tools/quic_test_server.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_TOOLS_QUIC_TEST_TOOLS_QUIC_TEST_SERVER_H_
+#define NET_TOOLS_QUIC_TEST_TOOLS_QUIC_TEST_SERVER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/ip_endpoint.h"
+#include "net/quic/quic_session.h"
+#include "net/tools/quic/quic_dispatcher.h"
+#include "net/tools/quic/quic_server.h"
+#include "net/tools/quic/quic_spdy_server_stream.h"
+
+namespace net {
+
+namespace tools {
+
+namespace test {
+
+// A test server which enables easy creation of custom QuicServerSessions
+//
+// Eventually this may be extended to allow custom QuicConnections etc.
+class QuicTestServer : public QuicServer {
+ public:
+ typedef std::function<QuicServerSession*(
+ const QuicConfig& config,
+ QuicConnection* connection,
+ QuicServerSessionVisitor* visitor,
+ const QuicCryptoServerConfig* crypto_config)> SessionCreationFunction;
+
+ typedef std::function<QuicSpdyServerStream*(QuicStreamId id,
+ QuicSpdySession* session)>
+ StreamCreationFunction;
+
+ explicit QuicTestServer();
+ QuicTestServer(const QuicConfig& config,
+ const QuicVersionVector& supported_versions);
+
+ // Create a custom dispatcher which creates custom sessions.
+ QuicDispatcher* CreateQuicDispatcher() override;
+
+ // Sets a custom session creator, for easy custom session logic.
+ // This is incompatible with setting a stream creator.
+ void SetSessionCreator(SessionCreationFunction function);
+
+ // Sets a custom stream creator, for easy custom stream logic.
+ // This is incompatible with setting a session creator.
+ void SetSpdyStreamCreator(StreamCreationFunction function);
+};
+
+// Useful test sessions for the QuicTestServer.
+
+// Test session which sends a GOAWAY immedaitely on creation, before crypto
+// credentials have even been established.
+class ImmediateGoAwaySession : public QuicServerSession {
+ public:
+ ImmediateGoAwaySession(const QuicConfig& config,
+ QuicConnection* connection,
+ QuicServerSessionVisitor* visitor,
+ const QuicCryptoServerConfig* crypto_config);
+};
+
+} // namespace test
+
+} // namespace tools
+
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_TEST_TOOLS_QUIC_TEST_SERVER_H_