summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/quic/crypto/crypto_protocol.h1
-rw-r--r--net/quic/quic_config.cc14
-rw-r--r--net/quic/quic_config.h5
-rw-r--r--net/quic/quic_config_test.cc28
-rw-r--r--net/quic/quic_connection.cc66
-rw-r--r--net/quic/quic_connection.h5
-rw-r--r--net/quic/quic_connection_test.cc40
-rw-r--r--net/quic/quic_flags.cc4
-rw-r--r--net/quic/quic_flags.h1
-rw-r--r--net/quic/quic_sent_packet_manager.cc22
-rw-r--r--net/quic/quic_sent_packet_manager.h5
-rw-r--r--net/quic/reliable_quic_stream.cc9
-rw-r--r--net/quic/reliable_quic_stream.h3
-rw-r--r--net/quic/reliable_quic_stream_test.cc16
-rw-r--r--net/tools/quic/test_tools/quic_test_client.cc21
-rw-r--r--net/tools/quic/test_tools/quic_test_client.h5
16 files changed, 199 insertions, 46 deletions
diff --git a/net/quic/crypto/crypto_protocol.h b/net/quic/crypto/crypto_protocol.h
index 3b1a391..beada26 100644
--- a/net/quic/crypto/crypto_protocol.h
+++ b/net/quic/crypto/crypto_protocol.h
@@ -78,6 +78,7 @@ const QuicTag kTCID = TAG('T', 'C', 'I', 'D'); // Connection ID truncation.
// FEC options
const QuicTag kFHDR = TAG('F', 'H', 'D', 'R'); // FEC protect headers
+const QuicTag kFSTR = TAG('F', 'S', 'T', 'R'); // FEC protect all streams
// Set FecSendPolicy for sending FEC packet only when FEC alarm goes off.
const QuicTag kFSPA = TAG('F', 'S', 'P', 'A');
diff --git a/net/quic/quic_config.cc b/net/quic/quic_config.cc
index 30db003..9277985 100644
--- a/net/quic/quic_config.cc
+++ b/net/quic/quic_config.cc
@@ -388,6 +388,20 @@ QuicTagVector QuicConfig::SendConnectionOptions() const {
return connection_options_.GetSendValues();
}
+bool QuicConfig::HasClientSentConnectionOption(QuicTag tag,
+ Perspective perspective) const {
+ if (perspective == Perspective::IS_SERVER) {
+ if (HasReceivedConnectionOptions() &&
+ ContainsQuicTag(ReceivedConnectionOptions(), tag)) {
+ return true;
+ }
+ } else if (HasSendConnectionOptions() &&
+ ContainsQuicTag(SendConnectionOptions(), tag)) {
+ return true;
+ }
+ return false;
+}
+
void QuicConfig::SetIdleConnectionStateLifetime(
QuicTime::Delta max_idle_connection_state_lifetime,
QuicTime::Delta default_idle_conection_state_lifetime) {
diff --git a/net/quic/quic_config.h b/net/quic/quic_config.h
index 34ea593..20d71c60 100644
--- a/net/quic/quic_config.h
+++ b/net/quic/quic_config.h
@@ -229,6 +229,11 @@ class NET_EXPORT_PRIVATE QuicConfig {
QuicTagVector SendConnectionOptions() const;
+ // Returns true if the client is sending or the server has received a
+ // connection option.
+ bool HasClientSentConnectionOption(QuicTag tag,
+ Perspective perspective) const;
+
void SetIdleConnectionStateLifetime(
QuicTime::Delta max_idle_connection_state_lifetime,
QuicTime::Delta default_idle_conection_state_lifetime);
diff --git a/net/quic/quic_config_test.cc b/net/quic/quic_config_test.cc
index 4cd3820..dcf19a5 100644
--- a/net/quic/quic_config_test.cc
+++ b/net/quic/quic_config_test.cc
@@ -217,6 +217,34 @@ TEST_F(QuicConfigTest, InvalidFlowControlWindow) {
config.GetInitialStreamFlowControlWindowToSend());
}
+TEST_F(QuicConfigTest, HasClientSentConnectionOption) {
+ QuicConfig client_config;
+ QuicTagVector copt;
+ copt.push_back(kTBBR);
+ copt.push_back(kFHDR);
+ client_config.SetConnectionOptionsToSend(copt);
+ EXPECT_TRUE(client_config.HasClientSentConnectionOption(
+ kTBBR, Perspective::IS_CLIENT));
+ EXPECT_TRUE(client_config.HasClientSentConnectionOption(
+ kFHDR, Perspective::IS_CLIENT));
+
+ CryptoHandshakeMessage msg;
+ client_config.ToHandshakeMessage(&msg);
+
+ string error_details;
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, CLIENT, &error_details);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_TRUE(config_.negotiated());
+
+ EXPECT_TRUE(config_.HasReceivedConnectionOptions());
+ EXPECT_EQ(2u, config_.ReceivedConnectionOptions().size());
+ EXPECT_TRUE(
+ config_.HasClientSentConnectionOption(kTBBR, Perspective::IS_SERVER));
+ EXPECT_TRUE(
+ config_.HasClientSentConnectionOption(kFHDR, Perspective::IS_SERVER));
+}
+
} // namespace
} // namespace test
} // namespace net
diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc
index b1da304..3ba831f 100644
--- a/net/quic/quic_connection.cc
+++ b/net/quic/quic_connection.cc
@@ -17,6 +17,7 @@
#include "base/debug/stack_trace.h"
#include "base/format_macros.h"
#include "base/logging.h"
+#include "base/memory/ref_counted.h"
#include "base/profiler/scoped_tracker.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
@@ -178,6 +179,33 @@ class FecAlarm : public QuicAlarm::Delegate {
DISALLOW_COPY_AND_ASSIGN(FecAlarm);
};
+// Listens for acks of MTU discovery packets and raises the maximum packet size
+// of the connection if the probe succeeds.
+class MtuDiscoveryAckListener : public QuicAckNotifier::DelegateInterface {
+ public:
+ MtuDiscoveryAckListener(QuicConnection* connection, QuicByteCount probe_size)
+ : connection_(connection), probe_size_(probe_size) {}
+
+ void OnAckNotification(int /*num_retransmittable_packets*/,
+ int /*num_retransmittable_bytes*/,
+ QuicTime::Delta /*delta_largest_observed*/) override {
+ // Since the probe was successful, increase the maximum packet size to that.
+ if (probe_size_ > connection_->max_packet_length()) {
+ connection_->set_max_packet_length(probe_size_);
+ }
+ }
+
+ protected:
+ // MtuDiscoveryAckListener is ref counted.
+ ~MtuDiscoveryAckListener() override {}
+
+ private:
+ QuicConnection* connection_;
+ QuicByteCount probe_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(MtuDiscoveryAckListener);
+};
+
} // namespace
QuicConnection::QueuedPacket::QueuedPacket(SerializedPacket packet,
@@ -313,12 +341,7 @@ void QuicConnection::SetFromConfig(const QuicConfig& config) {
max_undecryptable_packets_ = config.max_undecryptable_packets();
if (FLAGS_quic_send_fec_packet_only_on_fec_alarm &&
- ((perspective_ == Perspective::IS_SERVER &&
- config.HasReceivedConnectionOptions() &&
- ContainsQuicTag(config.ReceivedConnectionOptions(), kFSPA)) ||
- (perspective_ == Perspective::IS_CLIENT &&
- config.HasSendConnectionOptions() &&
- ContainsQuicTag(config.SendConnectionOptions(), kFSPA)))) {
+ config.HasClientSentConnectionOption(kFSPA, perspective_)) {
packet_generator_.set_fec_send_policy(FecSendPolicy::FEC_ALARM_TRIGGER);
}
}
@@ -914,7 +937,7 @@ void QuicConnection::OnPacketComplete() {
if (!last_stream_frames_.empty()) {
visitor_->OnStreamFrames(last_stream_frames_);
- if (!connected_ && FLAGS_quic_stop_early_2) {
+ if (!connected_) {
return;
}
}
@@ -926,46 +949,44 @@ void QuicConnection::OnPacketComplete() {
// feedback.
if (!last_window_update_frames_.empty()) {
visitor_->OnWindowUpdateFrames(last_window_update_frames_);
- if (!connected_ && FLAGS_quic_stop_early_2) {
+ if (!connected_) {
return;
}
}
if (!last_blocked_frames_.empty()) {
visitor_->OnBlockedFrames(last_blocked_frames_);
- if (!connected_ && FLAGS_quic_stop_early_2) {
+ if (!connected_) {
return;
}
}
for (size_t i = 0; i < last_goaway_frames_.size(); ++i) {
visitor_->OnGoAway(last_goaway_frames_[i]);
- if (!connected_ && FLAGS_quic_stop_early_2) {
+ if (!connected_) {
return;
}
}
for (size_t i = 0; i < last_rst_frames_.size(); ++i) {
visitor_->OnRstStream(last_rst_frames_[i]);
- if (!connected_ && FLAGS_quic_stop_early_2) {
+ if (!connected_) {
return;
}
}
for (size_t i = 0; i < last_ack_frames_.size(); ++i) {
ProcessAckFrame(last_ack_frames_[i]);
- if (!connected_ && FLAGS_quic_stop_early_2) {
+ if (!connected_) {
return;
}
}
for (size_t i = 0; i < last_stop_waiting_frames_.size(); ++i) {
ProcessStopWaitingFrame(last_stop_waiting_frames_[i]);
- if (!connected_ && FLAGS_quic_stop_early_2) {
+ if (!connected_) {
return;
}
}
if (!last_close_frames_.empty()) {
CloseConnection(last_close_frames_[0].error_code, true);
DCHECK(!connected_);
- if (FLAGS_quic_stop_early_2) {
- return;
- }
+ return;
}
// If there are new missing packets to report, send an ack immediately.
@@ -2180,4 +2201,17 @@ bool QuicConnection::IsConnectionClose(const QueuedPacket& packet) {
return false;
}
+void QuicConnection::SendMtuDiscoveryPacket(QuicByteCount target_mtu) {
+ // Create a listener for the new probe. The ownership of the listener is
+ // transferred to the AckNotifierManager. The notifier will get destroyed
+ // before the connection (because it's stored in one of the connection's
+ // subfields), hence |this| pointer is guaranteed to stay valid at all times.
+ scoped_refptr<MtuDiscoveryAckListener> last_mtu_discovery_ack_listener(
+ new MtuDiscoveryAckListener(this, target_mtu));
+
+ // Send the probe.
+ packet_generator_.GenerateMtuDiscoveryPacket(
+ target_mtu, last_mtu_discovery_ack_listener.get());
+}
+
} // namespace net
diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h
index 234782c..f13c589 100644
--- a/net/quic/quic_connection.h
+++ b/net/quic/quic_connection.h
@@ -544,6 +544,11 @@ class NET_EXPORT_PRIVATE QuicConnection
bool is_secure() const { return is_secure_; }
+ // Sends an MTU discovery packet of size |target_mtu|. If the packet is
+ // acknowledged by the peer, the maximum packet size will be increased to
+ // |target_mtu|.
+ void SendMtuDiscoveryPacket(QuicByteCount target_mtu);
+
protected:
// Packets which have not been written to the wire.
// Owns the QuicPacket* packet.
diff --git a/net/quic/quic_connection_test.cc b/net/quic/quic_connection_test.cc
index e53e6f0..6747194 100644
--- a/net/quic/quic_connection_test.cc
+++ b/net/quic/quic_connection_test.cc
@@ -3220,6 +3220,46 @@ TEST_P(QuicConnectionTest, PingAfterSend) {
EXPECT_FALSE(connection_.GetPingAlarm()->IsSet());
}
+// Tests whether sending an MTU discovery packet to peer successfully causes the
+// maximum packet size to increase.
+TEST_P(QuicConnectionTest, SendMtuDiscoveryPacket) {
+ EXPECT_TRUE(connection_.connected());
+
+ // Send an MTU probe.
+ const size_t new_mtu = kDefaultMaxPacketSize + 100;
+ QuicByteCount mtu_probe_size;
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillOnce(DoAll(SaveArg<3>(&mtu_probe_size), Return(true)));
+ connection_.SendMtuDiscoveryPacket(new_mtu);
+ EXPECT_EQ(new_mtu, mtu_probe_size);
+ EXPECT_EQ(1u, creator_->sequence_number());
+
+ // Send more than MTU worth of data. No acknowledgement was received so far,
+ // so the MTU should be at its old value.
+ const string data(kDefaultMaxPacketSize + 1, '.');
+ QuicByteCount size_before_mtu_change;
+ // OnPacketSent will be called twice, but only the size of the first packet
+ // will be stored.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .Times(2)
+ .WillOnce(DoAll(SaveArg<3>(&size_before_mtu_change), Return(true)));
+ connection_.SendStreamDataWithString(3, data, 0, kFin, nullptr);
+ EXPECT_EQ(3u, creator_->sequence_number());
+ EXPECT_EQ(kDefaultMaxPacketSize, size_before_mtu_change);
+
+ // Acknowledge all packets so far.
+ QuicAckFrame probe_ack = InitAckFrame(3);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ ProcessAckPacket(&probe_ack);
+ EXPECT_EQ(new_mtu, connection_.max_packet_length());
+
+ // Send the same data again. Check that it fits into a single packet now.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ connection_.SendStreamDataWithString(3, data, 0, kFin, nullptr);
+ EXPECT_EQ(4u, creator_->sequence_number());
+}
+
TEST_P(QuicConnectionTest, TimeoutAfterSend) {
EXPECT_TRUE(connection_.connected());
EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
diff --git a/net/quic/quic_flags.cc b/net/quic/quic_flags.cc
index 2b57e2a..923a0f0 100644
--- a/net/quic/quic_flags.cc
+++ b/net/quic/quic_flags.cc
@@ -45,10 +45,6 @@ bool FLAGS_enable_quic_stateless_reject_support = false;
// If true, flow controller may grow the receive window size if necessary.
bool FLAGS_quic_auto_tune_receive_window = true;
-// If true, stop processing quic data as soon as the connection is
-// closed rather than processing a full packet.
-bool FLAGS_quic_stop_early_2 = true;
-
// Don't ack acks in QUIC, even when there is a recent missing packet.
bool FLAGS_quic_dont_ack_acks = true;
diff --git a/net/quic/quic_flags.h b/net/quic/quic_flags.h
index fbb185d..aff2c00 100644
--- a/net/quic/quic_flags.h
+++ b/net/quic/quic_flags.h
@@ -19,7 +19,6 @@ NET_EXPORT_PRIVATE extern int64 FLAGS_quic_time_wait_list_seconds;
NET_EXPORT_PRIVATE extern int64 FLAGS_quic_time_wait_list_max_connections;
NET_EXPORT_PRIVATE extern bool FLAGS_enable_quic_stateless_reject_support;
NET_EXPORT_PRIVATE extern bool FLAGS_quic_auto_tune_receive_window;
-NET_EXPORT_PRIVATE extern bool FLAGS_quic_stop_early_2;
NET_EXPORT_PRIVATE extern bool FLAGS_quic_dont_ack_acks;
NET_EXPORT_PRIVATE extern bool FLAGS_quic_send_fec_packet_only_on_fec_alarm;
NET_EXPORT_PRIVATE extern bool FLAGS_quic_use_is_useless_packet;
diff --git a/net/quic/quic_sent_packet_manager.cc b/net/quic/quic_sent_packet_manager.cc
index 02bc2af..039a43d 100644
--- a/net/quic/quic_sent_packet_manager.cc
+++ b/net/quic/quic_sent_packet_manager.cc
@@ -146,16 +146,16 @@ void QuicSentPacketManager::SetFromConfig(const QuicConfig& config) {
}
EnablePacing();
- if (HasClientSentConnectionOption(config, k1CON)) {
+ if (config.HasClientSentConnectionOption(k1CON, perspective_)) {
send_algorithm_->SetNumEmulatedConnections(1);
}
- if (HasClientSentConnectionOption(config, kNCON)) {
+ if (config.HasClientSentConnectionOption(kNCON, perspective_)) {
n_connection_simulation_ = true;
}
- if (HasClientSentConnectionOption(config, kNTLP)) {
+ if (config.HasClientSentConnectionOption(kNTLP, perspective_)) {
max_tail_loss_probes_ = 0;
}
- if (HasClientSentConnectionOption(config, kNRTO)) {
+ if (config.HasClientSentConnectionOption(kNRTO, perspective_)) {
use_new_rto_ = true;
}
if (config.HasReceivedConnectionOptions() &&
@@ -198,20 +198,6 @@ void QuicSentPacketManager::SetNumOpenStreams(size_t num_streams) {
}
}
-bool QuicSentPacketManager::HasClientSentConnectionOption(
- const QuicConfig& config, QuicTag tag) const {
- if (perspective_ == Perspective::IS_SERVER) {
- if (config.HasReceivedConnectionOptions() &&
- ContainsQuicTag(config.ReceivedConnectionOptions(), tag)) {
- return true;
- }
- } else if (config.HasSendConnectionOptions() &&
- ContainsQuicTag(config.SendConnectionOptions(), tag)) {
- return true;
- }
- return false;
-}
-
void QuicSentPacketManager::OnIncomingAck(const QuicAckFrame& ack_frame,
QuicTime ack_receive_time) {
QuicByteCount bytes_in_flight = unacked_packets_.bytes_in_flight();
diff --git a/net/quic/quic_sent_packet_manager.h b/net/quic/quic_sent_packet_manager.h
index bb65e5e..e0310c0 100644
--- a/net/quic/quic_sent_packet_manager.h
+++ b/net/quic/quic_sent_packet_manager.h
@@ -337,11 +337,6 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager {
const SequenceNumberList& all_transmissions,
QuicPacketSequenceNumber acked_sequence_number);
- // Returns true if the client is sending or the server has received a
- // connection option.
- bool HasClientSentConnectionOption(const QuicConfig& config,
- QuicTag tag) const;
-
// Newly serialized retransmittable and fec packets are added to this map,
// which contains owning pointers to any contained frames. If a packet is
// retransmitted, this map will contain entries for both the old and the new
diff --git a/net/quic/reliable_quic_stream.cc b/net/quic/reliable_quic_stream.cc
index 7b1f0e8..924211f 100644
--- a/net/quic/reliable_quic_stream.cc
+++ b/net/quic/reliable_quic_stream.cc
@@ -6,6 +6,7 @@
#include "base/logging.h"
#include "net/quic/iovector.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/quic_flow_controller.h"
#include "net/quic/quic_session.h"
#include "net/quic/quic_write_blocked_list.h"
@@ -136,11 +137,19 @@ ReliableQuicStream::ReliableQuicStream(QuicStreamId id, QuicSession* session)
session_->flow_controller()->auto_tune_receive_window()),
connection_flow_controller_(session_->flow_controller()),
stream_contributes_to_connection_flow_control_(true) {
+ SetFromConfig();
}
ReliableQuicStream::~ReliableQuicStream() {
}
+void ReliableQuicStream::SetFromConfig() {
+ if (FLAGS_quic_send_fec_packet_only_on_fec_alarm &&
+ session_->config()->HasClientSentConnectionOption(kFSTR, perspective_)) {
+ fec_policy_ = FEC_PROTECT_ALWAYS;
+ }
+}
+
void ReliableQuicStream::OnStreamFrame(const QuicStreamFrame& frame) {
if (read_side_closed_) {
DVLOG(1) << ENDPOINT << "Ignoring frame " << frame.stream_id;
diff --git a/net/quic/reliable_quic_stream.h b/net/quic/reliable_quic_stream.h
index a88a835..7496f03 100644
--- a/net/quic/reliable_quic_stream.h
+++ b/net/quic/reliable_quic_stream.h
@@ -48,6 +48,9 @@ class NET_EXPORT_PRIVATE ReliableQuicStream {
virtual ~ReliableQuicStream();
+ // Sets |fec_policy_| parameter from |session_|'s config.
+ void SetFromConfig();
+
// Called by the session when a (potentially duplicate) stream frame has been
// received for this stream.
virtual void OnStreamFrame(const QuicStreamFrame& frame);
diff --git a/net/quic/reliable_quic_stream_test.cc b/net/quic/reliable_quic_stream_test.cc
index 7899ee6..dbdb7b5 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"
@@ -696,6 +697,21 @@ TEST_F(ReliableQuicStreamTest, SetDrainingOutgoingIncoming) {
EXPECT_EQ(0u, session_->GetNumOpenStreams());
}
+TEST_F(ReliableQuicStreamTest, FecSendPolicyReceivedConnectionOption) {
+ ValueRestore<bool> old_flag(&FLAGS_quic_send_fec_packet_only_on_fec_alarm,
+ true);
+ Initialize(kShouldProcessData);
+
+ // Test ReceivedConnectionOptions.
+ QuicConfig* config = session_->config();
+ QuicTagVector copt;
+ copt.push_back(kFSTR);
+ QuicConfigPeer::SetReceivedConnectionOptions(config, copt);
+ EXPECT_EQ(FEC_PROTECT_OPTIONAL, stream_->fec_policy());
+ stream_->SetFromConfig();
+ EXPECT_EQ(FEC_PROTECT_ALWAYS, stream_->fec_policy());
+}
+
} // namespace
} // namespace test
} // namespace net
diff --git a/net/tools/quic/test_tools/quic_test_client.cc b/net/tools/quic/test_tools/quic_test_client.cc
index 75e8728..a1888d9 100644
--- a/net/tools/quic/test_tools/quic_test_client.cc
+++ b/net/tools/quic/test_tools/quic_test_client.cc
@@ -235,6 +235,15 @@ ssize_t QuicTestClient::SendRequest(const string& uri) {
return SendMessage(message);
}
+void QuicTestClient::SendRequestsAndWaitForLastResponse(
+ const vector<string>& url_list) {
+ for (const string& url : url_list) {
+ SendRequest(url);
+ }
+ WaitForResponse();
+ return;
+}
+
ssize_t QuicTestClient::SendMessage(const HTTPMessage& message) {
stream_ = nullptr; // Always force creation of a stream for SendMessage.
@@ -316,9 +325,12 @@ string QuicTestClient::SendCustomSynchronousRequest(
string QuicTestClient::SendSynchronousRequest(const string& uri) {
if (SendRequest(uri) == 0) {
DLOG(ERROR) << "Failed the request for uri:" << uri;
- return "";
+ // Set the response_ explicitly. Otherwise response_ will contain the
+ // response from the previously successful request.
+ response_ = "";
+ } else {
+ WaitForResponse();
}
- WaitForResponse();
return response_;
}
@@ -486,6 +498,11 @@ size_t QuicTestClient::bytes_written() const {
}
void QuicTestClient::OnClose(QuicDataStream* stream) {
+ if (stream != nullptr) {
+ // Always close the stream, regardless of whether it was the last stream
+ // written.
+ client()->OnClose(stream);
+ }
if (stream_ != stream) {
return;
}
diff --git a/net/tools/quic/test_tools/quic_test_client.h b/net/tools/quic/test_tools/quic_test_client.h
index 69cdb43..3c164ed 100644
--- a/net/tools/quic/test_tools/quic_test_client.h
+++ b/net/tools/quic/test_tools/quic_test_client.h
@@ -99,6 +99,11 @@ class QuicTestClient : public SimpleClient,
// Clears any outstanding state and sends a simple GET of 'uri' to the
// server. Returns 0 if the request failed and no bytes were written.
ssize_t SendRequest(const std::string& uri) override;
+ // Sends requests for all the urls and waits for the response for the last
+ // url. To process the individual responses as they are returned, the caller
+ // should use the set the response_listener on the client().
+ void SendRequestsAndWaitForLastResponse(
+ const std::vector<std::string>& url_list);
ssize_t SendMessage(const HTTPMessage& message) override;
std::string SendCustomSynchronousRequest(const HTTPMessage& message) override;
std::string SendSynchronousRequest(const std::string& uri) override;