summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/base/net_error_details.h9
-rw-r--r--net/quic/quic_chromium_client_session.cc11
-rw-r--r--net/quic/quic_chromium_client_session.h8
-rw-r--r--net/quic/quic_http_stream.cc9
-rw-r--r--net/quic/quic_http_stream.h5
-rw-r--r--net/quic/quic_network_transaction_unittest.cc80
-rw-r--r--net/quic/quic_stream_factory_test.cc40
-rw-r--r--net/quic/test_tools/quic_test_packet_maker.cc22
-rw-r--r--net/quic/test_tools/quic_test_packet_maker.h3
9 files changed, 180 insertions, 7 deletions
diff --git a/net/base/net_error_details.h b/net/base/net_error_details.h
index 47efebe..7bd5baa 100644
--- a/net/base/net_error_details.h
+++ b/net/base/net_error_details.h
@@ -16,12 +16,14 @@ struct NET_EXPORT NetErrorDetails {
NetErrorDetails()
: quic_broken(false),
quic_connection_error(QUIC_NO_ERROR),
- connection_info(HttpResponseInfo::CONNECTION_INFO_UNKNOWN) {}
+ connection_info(HttpResponseInfo::CONNECTION_INFO_UNKNOWN),
+ quic_port_migration_detected(false) {}
NetErrorDetails(bool quic_broken, QuicErrorCode quic_connection_error)
: quic_broken(quic_broken),
quic_connection_error(quic_connection_error),
- connection_info(HttpResponseInfo::CONNECTION_INFO_UNKNOWN) {}
+ connection_info(HttpResponseInfo::CONNECTION_INFO_UNKNOWN),
+ quic_port_migration_detected(false) {}
// True if all QUIC alternative services are marked broken for the origin.
bool quic_broken;
@@ -31,6 +33,9 @@ struct NET_EXPORT NetErrorDetails {
// Will be discarded by upper layers if the connection type can be fetched
// from response header from the server.
HttpResponseInfo::ConnectionInfo connection_info;
+ // True if receives a GoAway frame from the server due to connection
+ // migration with port change.
+ bool quic_port_migration_detected;
};
} // namespace net
diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc
index 15ec708..85d06ae 100644
--- a/net/quic/quic_chromium_client_session.cc
+++ b/net/quic/quic_chromium_client_session.cc
@@ -205,6 +205,7 @@ QuicChromiumClientSession::QuicChromiumClientSession(
std::move(socket_performance_watcher),
net_log_)),
going_away_(false),
+ port_migration_detected_(false),
disabled_reason_(QUIC_DISABLED_NOT),
token_binding_signatures_(kTokenBindingSignatureMapSize),
weak_factory_(this) {
@@ -370,7 +371,7 @@ void QuicChromiumClientSession::OnStreamFrame(const QuicStreamFrame& frame) {
void QuicChromiumClientSession::AddObserver(Observer* observer) {
if (going_away_) {
RecordUnexpectedObservers(ADD_OBSERVER);
- observer->OnSessionClosed(ERR_UNEXPECTED);
+ observer->OnSessionClosed(ERR_UNEXPECTED, port_migration_detected_);
return;
}
@@ -760,6 +761,7 @@ void QuicChromiumClientSession::OnCryptoHandshakeMessageReceived(
void QuicChromiumClientSession::OnGoAway(const QuicGoAwayFrame& frame) {
QuicSession::OnGoAway(frame);
NotifyFactoryOfSessionGoingAway();
+ port_migration_detected_ = frame.error_code == QUIC_ERROR_MIGRATING_PORT;
}
void QuicChromiumClientSession::OnRstStream(const QuicRstStreamFrame& frame) {
@@ -961,7 +963,7 @@ void QuicChromiumClientSession::CloseAllObservers(int net_error) {
while (!observers_.empty()) {
Observer* observer = *observers_.begin();
observers_.erase(observer);
- observer->OnSessionClosed(net_error);
+ observer->OnSessionClosed(net_error, port_migration_detected_);
}
}
@@ -1098,6 +1100,11 @@ bool QuicChromiumClientSession::MigrateToSocket(
return true;
}
+void QuicChromiumClientSession::PopulateNetErrorDetails(
+ NetErrorDetails* details) {
+ details->quic_port_migration_detected = port_migration_detected_;
+}
+
const DatagramClientSocket* QuicChromiumClientSession::GetDefaultSocket()
const {
DCHECK(sockets_.back().get() != nullptr);
diff --git a/net/quic/quic_chromium_client_session.h b/net/quic/quic_chromium_client_session.h
index c4ef293..5cc31d3 100644
--- a/net/quic/quic_chromium_client_session.h
+++ b/net/quic/quic_chromium_client_session.h
@@ -20,6 +20,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/time/time.h"
#include "net/base/completion_callback.h"
+#include "net/base/net_error_details.h"
#include "net/base/socket_performance_watcher.h"
#include "net/cert/ct_verify_result.h"
#include "net/proxy/proxy_server.h"
@@ -71,7 +72,7 @@ class NET_EXPORT_PRIVATE QuicChromiumClientSession
public:
virtual ~Observer() {}
virtual void OnCryptoHandshakeConfirmed() = 0;
- virtual void OnSessionClosed(int error) = 0;
+ virtual void OnSessionClosed(int error, bool port_migration_detected) = 0;
};
// A helper class used to manage a request to create a stream.
@@ -248,6 +249,9 @@ class NET_EXPORT_PRIVATE QuicChromiumClientSession
scoped_ptr<QuicChromiumPacketReader> reader,
scoped_ptr<QuicPacketWriter> writer);
+ // Populates network error details for this session.
+ void PopulateNetErrorDetails(NetErrorDetails* details);
+
// Returns current default socket. This is the socket over which all
// QUIC packets are sent. This default socket can change, so do not store the
// returned socket.
@@ -328,6 +332,8 @@ class NET_EXPORT_PRIVATE QuicChromiumClientSession
// True when the session is going away, and streams may no longer be created
// on this session. Existing stream will continue to be processed.
bool going_away_;
+ // True when the session receives a go away from server due to port migration.
+ bool port_migration_detected_;
QuicDisabledReason disabled_reason_;
TokenBindingSignatureMap token_binding_signatures_;
base::WeakPtrFactory<QuicChromiumClientSession> weak_factory_;
diff --git a/net/quic/quic_http_stream.cc b/net/quic/quic_http_stream.cc
index 5578bd6..55f3296 100644
--- a/net/quic/quic_http_stream.cc
+++ b/net/quic/quic_http_stream.cc
@@ -46,6 +46,7 @@ QuicHttpStream::QuicHttpStream(
closed_stream_sent_bytes_(0),
user_buffer_len_(0),
quic_connection_error_(QUIC_NO_ERROR),
+ port_migration_detected_(false),
found_promise_(false),
push_handle_(nullptr),
weak_factory_(this) {
@@ -437,6 +438,11 @@ void QuicHttpStream::PopulateNetErrorDetails(NetErrorDetails* details) {
details->connection_info = HttpResponseInfo::CONNECTION_INFO_QUIC1_SPDY3;
if (was_handshake_confirmed_)
details->quic_connection_error = quic_connection_error_;
+ if (session_) {
+ session_->PopulateNetErrorDetails(details);
+ } else {
+ details->quic_port_migration_detected = port_migration_detected_;
+ }
}
void QuicHttpStream::SetPriority(RequestPriority priority) {
@@ -514,9 +520,10 @@ void QuicHttpStream::OnCryptoHandshakeConfirmed() {
was_handshake_confirmed_ = true;
}
-void QuicHttpStream::OnSessionClosed(int error) {
+void QuicHttpStream::OnSessionClosed(int error, bool port_migration_detected) {
Close(false);
session_error_ = error;
+ port_migration_detected_ = port_migration_detected;
session_.reset();
}
diff --git a/net/quic/quic_http_stream.h b/net/quic/quic_http_stream.h
index 81aacb0..a88a6e1 100644
--- a/net/quic/quic_http_stream.h
+++ b/net/quic/quic_http_stream.h
@@ -79,7 +79,7 @@ class NET_EXPORT_PRIVATE QuicHttpStream
// QuicChromiumClientSession::Observer implementation
void OnCryptoHandshakeConfirmed() override;
- void OnSessionClosed(int error) override;
+ void OnSessionClosed(int error, bool port_migration_detected) override;
// QuicClientPushPromiseIndex::Delegate implementation
bool CheckVary(const SpdyHeaderBlock& client_request,
@@ -193,6 +193,9 @@ class NET_EXPORT_PRIVATE QuicHttpStream
// SSLInfo from the underlying QuicSession.
SSLInfo ssl_info_;
+ // True when this stream receives a go away from server due to port migration.
+ bool port_migration_detected_;
+
bool found_promise_;
// |QuicClientPromisedInfo| owns this. It will be set when |Try()|
// is asynchronous, i.e. it returned QUIC_PENDING, and remains valid
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
index b5a8d6d..16847aa 100644
--- a/net/quic/quic_network_transaction_unittest.cc
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -233,6 +233,13 @@ class QuicNetworkTransactionTest
return maker_.MakeConnectionClosePacket(num);
}
+ scoped_ptr<QuicEncryptedPacket> ConstructGoAwayPacket(
+ QuicPacketNumber num,
+ QuicErrorCode error_code,
+ std::string reason_phrase) {
+ return maker_.MakeGoAwayPacket(num, error_code, reason_phrase);
+ }
+
scoped_ptr<QuicEncryptedPacket> ConstructAckPacket(
QuicPacketNumber largest_received,
QuicPacketNumber least_unacked) {
@@ -240,6 +247,18 @@ class QuicNetworkTransactionTest
least_unacked, true);
}
+ scoped_ptr<QuicEncryptedPacket> ConstructAckAndRstPacket(
+ QuicPacketNumber num,
+ QuicStreamId stream_id,
+ QuicRstStreamErrorCode error_code,
+ QuicPacketNumber largest_received,
+ QuicPacketNumber ack_least_unacked,
+ QuicPacketNumber stop_least_unacked) {
+ return maker_.MakeAckAndRstPacket(num, false, stream_id, error_code,
+ largest_received, ack_least_unacked,
+ stop_least_unacked, true);
+ }
+
scoped_ptr<QuicEncryptedPacket> ConstructAckPacket(
QuicPacketNumber largest_received,
QuicPacketNumber least_unacked,
@@ -915,6 +934,67 @@ TEST_P(QuicNetworkTransactionTest, UseAlternativeServiceQuicSupportedVersion) {
SendRequestAndExpectQuicResponse("hello!");
}
+TEST_P(QuicNetworkTransactionTest, GoAwayWithConnectionMigrationOnPortsOnly) {
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "https", "/")));
+ mock_quic_data.AddRead(ConstructResponseHeadersPacket(
+ 1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK")));
+ // Read a GoAway packet with
+ // QuicErrorCode: QUIC_ERROR_MIGRATING_PORT from the peer.
+ mock_quic_data.AddRead(
+ ConstructGoAwayPacket(2, QUIC_ERROR_MIGRATING_PORT,
+ "connection migration with port change only"));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddRead(
+ ConstructDataPacket(3, kClientDataStreamId1, false, true, 0, "hello!"));
+ mock_quic_data.AddWrite(ConstructAckAndRstPacket(
+ 3, kClientDataStreamId1, QUIC_STREAM_CANCELLED, 3, 3, 1));
+ mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING); // No more data to read
+ mock_quic_data.AddRead(ASYNC, 0); // EOF
+
+ mock_quic_data.AddSocketDataToFactory(&socket_factory_);
+
+ // The non-alternate protocol job needs to hang in order to guarantee that
+ // the alternate-protocol job will "win".
+ AddHangingNonAlternateProtocolSocketData();
+
+ // In order for a new QUIC session to be established via alternate-protocol
+ // without racing an HTTP connection, we need the host resolution to happen
+ // synchronously. Of course, even though QUIC *could* perform a 0-RTT
+ // connection to the the server, in this test we require confirmation
+ // before encrypting so the HTTP job will still start.
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
+ "");
+ HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
+ AddressList address;
+ host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address, CompletionCallback(),
+ nullptr, net_log_.bound());
+
+ CreateSession();
+ session_->quic_stream_factory()->set_require_confirmation(true);
+ AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT);
+
+ scoped_ptr<HttpNetworkTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
+ TestCompletionCallback callback;
+ int rv = trans->Start(&request_, callback.callback(), net_log_.bound());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ crypto_client_stream_factory_.last_stream()->SendOnCryptoHandshakeEvent(
+ QuicSession::HANDSHAKE_CONFIRMED);
+ EXPECT_EQ(OK, callback.WaitForResult());
+
+ // Check whether this transaction is correctly marked as received a go-away
+ // because of migrating port.
+ NetErrorDetails details;
+ EXPECT_FALSE(details.quic_port_migration_detected);
+ trans->PopulateNetErrorDetails(&details);
+ EXPECT_TRUE(details.quic_port_migration_detected);
+}
+
TEST_P(QuicNetworkTransactionTest,
DoNotUseAlternativeServiceQuicUnsupportedVersion) {
std::string altsvc_header = base::StringPrintf(
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc
index 762a2d7..c01c3dc 100644
--- a/net/quic/quic_stream_factory_test.cc
+++ b/net/quic/quic_stream_factory_test.cc
@@ -603,6 +603,46 @@ TEST_P(QuicStreamFactoryTest, GoAway) {
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
}
+TEST_P(QuicStreamFactoryTest, GoAwayForConnectionMigrationWithPortOnly) {
+ Initialize();
+ ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
+ crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+
+ MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
+ SequencedSocketData socket_data(reads, arraysize(reads), nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data);
+
+ QuicStreamRequest request(factory_.get());
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_, privacy_mode_,
+ /*cert_verify_flags=*/0, url_, "GET", net_log_,
+ callback_.callback()));
+
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+
+ QuicChromiumClientSession* session =
+ QuicStreamFactoryPeer::GetActiveSession(factory_.get(), host_port_pair_);
+
+ session->OnGoAway(
+ QuicGoAwayFrame(QUIC_ERROR_MIGRATING_PORT, 0,
+ "peer connection migration due to port change only"));
+ NetErrorDetails details;
+ EXPECT_FALSE(details.quic_port_migration_detected);
+ session->PopulateNetErrorDetails(&details);
+ EXPECT_TRUE(details.quic_port_migration_detected);
+ details.quic_port_migration_detected = false;
+ stream->PopulateNetErrorDetails(&details);
+ EXPECT_TRUE(details.quic_port_migration_detected);
+
+ EXPECT_FALSE(
+ QuicStreamFactoryPeer::HasActiveSession(factory_.get(), host_port_pair_));
+
+ EXPECT_TRUE(socket_data.AllReadDataConsumed());
+ EXPECT_TRUE(socket_data.AllWriteDataConsumed());
+}
+
TEST_P(QuicStreamFactoryTest, Pooling) {
Initialize();
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
diff --git a/net/quic/test_tools/quic_test_packet_maker.cc b/net/quic/test_tools/quic_test_packet_maker.cc
index ebd2021..0bb671c 100644
--- a/net/quic/test_tools/quic_test_packet_maker.cc
+++ b/net/quic/test_tools/quic_test_packet_maker.cc
@@ -190,6 +190,28 @@ scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeConnectionClosePacket(
return scoped_ptr<QuicEncryptedPacket>(MakePacket(header, QuicFrame(&close)));
}
+scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeGoAwayPacket(
+ QuicPacketNumber num,
+ QuicErrorCode error_code,
+ std::string reason_phrase) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = connection_id_;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.public_header.packet_number_length = PACKET_1BYTE_PACKET_NUMBER;
+ header.packet_number = num;
+ header.entropy_flag = false;
+ header.fec_flag = false;
+ header.fec_group = 0;
+
+ QuicGoAwayFrame goaway;
+ goaway.error_code = error_code;
+ goaway.last_good_stream_id = 0;
+ goaway.reason_phrase = reason_phrase;
+ return scoped_ptr<QuicEncryptedPacket>(
+ MakePacket(header, QuicFrame(&goaway)));
+}
+
// Sets both least_unacked fields in stop waiting frame and ack frame
// to be |least_unacked|.
scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeAckPacket(
diff --git a/net/quic/test_tools/quic_test_packet_maker.h b/net/quic/test_tools/quic_test_packet_maker.h
index 2b2499fa..3527cdf 100644
--- a/net/quic/test_tools/quic_test_packet_maker.h
+++ b/net/quic/test_tools/quic_test_packet_maker.h
@@ -56,6 +56,9 @@ class QuicTestPacketMaker {
std::string& quic_error_details);
scoped_ptr<QuicEncryptedPacket> MakeConnectionClosePacket(
QuicPacketNumber num);
+ scoped_ptr<QuicEncryptedPacket> MakeGoAwayPacket(QuicPacketNumber num,
+ QuicErrorCode error_code,
+ std::string reason_phrase);
scoped_ptr<QuicEncryptedPacket> MakeAckPacket(
QuicPacketNumber packet_number,
QuicPacketNumber largest_received,