When a request fails because the QUIC handshake failed, retry the request
(which will happen over TCP).
Review URL: https://codereview.chromium.org/27181004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@229112 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h
index 5ec421ed..ec25dc4c 100644
--- a/net/base/net_error_list.h
+++ b/net/base/net_error_list.h
@@ -552,6 +552,10 @@
// The HTTP headers were truncated by an EOF.
NET_ERROR(RESPONSE_HEADERS_TRUNCATED, -357)
+// The QUIC crytpo handshake failed. This means that the server was unable
+// to read any requests sent, so they may be resent.
+NET_ERROR(QUIC_HANDSHAKE_FAILED, -358)
+
// The cache does not have the requested entry.
NET_ERROR(CACHE_MISS, -400)
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index 7c5f0de..018ba3b 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -897,6 +897,11 @@
return result;
}
+ if (result == ERR_QUIC_HANDSHAKE_FAILED) {
+ ResetConnectionAndRequestForResend();
+ return OK;
+ }
+
if (result < 0 && result != ERR_CONNECTION_CLOSED)
return HandleIOError(result);
@@ -1339,6 +1344,7 @@
break;
case ERR_SPDY_PING_FAILED:
case ERR_SPDY_SERVER_REFUSED_STREAM:
+ case ERR_QUIC_HANDSHAKE_FAILED:
net_log_.AddEventWithNetErrorCode(
NetLog::TYPE_HTTP_TRANSACTION_RESTART_AFTER_ERROR, error);
ResetConnectionAndRequestForResend();
diff --git a/net/quic/quic_client_session.cc b/net/quic/quic_client_session.cc
index 860429408..13362b64 100644
--- a/net/quic/quic_client_session.cc
+++ b/net/quic/quic_client_session.cc
@@ -47,7 +47,7 @@
}
int QuicClientSession::StreamRequest::StartRequest(
- const base::WeakPtr<QuicClientSession> session,
+ const base::WeakPtr<QuicClientSession>& session,
QuicReliableClientStream** stream,
const CompletionCallback& callback) {
session_ = session;
@@ -112,6 +112,12 @@
}
QuicClientSession::~QuicClientSession() {
+ // The session must be closed before it is destroyed.
+ DCHECK(streams()->empty());
+ CloseAllStreams(ERR_UNEXPECTED);
+ DCHECK(observers_.empty());
+ CloseAllObservers(ERR_UNEXPECTED);
+
connection()->set_debug_visitor(NULL);
net_log_.EndEvent(NetLog::TYPE_QUIC_SESSION);
@@ -156,6 +162,16 @@
return QuicSession::OnStreamFrames(frames);
}
+void QuicClientSession::AddObserver(Observer* observer) {
+ DCHECK(!ContainsKey(observers_, observer));
+ observers_.insert(observer);
+}
+
+void QuicClientSession::RemoveObserver(Observer* observer) {
+ DCHECK(ContainsKey(observers_, observer));
+ observers_.erase(observer);
+}
+
int QuicClientSession::TryCreateStream(StreamRequest* request,
QuicReliableClientStream** stream) {
if (!crypto_stream_->encryption_established()) {
@@ -296,6 +312,14 @@
// following code needs to changed.
base::ResetAndReturn(&callback_).Run(OK);
}
+ if (event == HANDSHAKE_CONFIRMED) {
+ ObserverSet::iterator it = observers_.begin();
+ while (it != observers_.end()) {
+ Observer* observer = *it;
+ ++it;
+ observer->OnCryptoHandshakeConfirmed();
+ }
+ }
QuicSession::OnCryptoHandshakeEvent(event);
}
@@ -328,12 +352,13 @@
UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.QuicVersion",
connection()->version());
+ NotifyFactoryOfSessionGoingAway();
if (!callback_.is_null()) {
base::ResetAndReturn(&callback_).Run(ERR_QUIC_PROTOCOL_ERROR);
}
socket_->Close();
QuicSession::ConnectionClose(error, from_peer);
- NotifyFactoryOfSessionCloseLater();
+ NotifyFactoryOfSessionClosedLater();
}
void QuicClientSession::OnSuccessfulVersionNegotiation(
@@ -367,7 +392,7 @@
void QuicClientSession::CloseSessionOnError(int error) {
UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.CloseSessionOnError", -error);
CloseSessionOnErrorInner(error, QUIC_INTERNAL_ERROR);
- NotifyFactoryOfSessionClose();
+ NotifyFactoryOfSessionClosed();
}
void QuicClientSession::CloseSessionOnErrorInner(int net_error,
@@ -375,12 +400,8 @@
if (!callback_.is_null()) {
base::ResetAndReturn(&callback_).Run(net_error);
}
- while (!streams()->empty()) {
- ReliableQuicStream* stream = streams()->begin()->second;
- QuicStreamId id = stream->id();
- static_cast<QuicReliableClientStream*>(stream)->OnError(net_error);
- CloseStream(id);
- }
+ CloseAllStreams(net_error);
+ CloseAllObservers(net_error);
net_log_.AddEvent(
NetLog::TYPE_QUIC_SESSION_CLOSE_ON_ERROR,
NetLog::IntegerCallback("net_error", net_error));
@@ -389,6 +410,23 @@
DCHECK(!connection()->connected());
}
+void QuicClientSession::CloseAllStreams(int net_error) {
+ while (!streams()->empty()) {
+ ReliableQuicStream* stream = streams()->begin()->second;
+ QuicStreamId id = stream->id();
+ static_cast<QuicReliableClientStream*>(stream)->OnError(net_error);
+ CloseStream(id);
+ }
+}
+
+void QuicClientSession::CloseAllObservers(int net_error) {
+ while (!observers_.empty()) {
+ Observer* observer = *observers_.begin();
+ observers_.erase(observer);
+ observer->OnSessionClosed(net_error);
+ }
+}
+
base::Value* QuicClientSession::GetInfoAsValue(const HostPortPair& pair) const {
base::DictionaryValue* dict = new base::DictionaryValue();
dict->SetString("host_port_pair", pair.ToString());
@@ -412,8 +450,9 @@
if (result < 0) {
DLOG(INFO) << "Closing session on read error: " << result;
UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.ReadError", -result);
+ NotifyFactoryOfSessionGoingAway();
CloseSessionOnErrorInner(result, QUIC_PACKET_READ_ERROR);
- NotifyFactoryOfSessionCloseLater();
+ NotifyFactoryOfSessionClosedLater();
return;
}
@@ -428,26 +467,31 @@
// use a weak pointer to be safe.
connection()->ProcessUdpPacket(local_address, peer_address, packet);
if (!connection()->connected()) {
- stream_factory_->OnSessionClose(this);
+ stream_factory_->OnSessionClosed(this);
return;
}
StartReading();
}
-void QuicClientSession::NotifyFactoryOfSessionCloseLater() {
+void QuicClientSession::NotifyFactoryOfSessionGoingAway() {
+ if (stream_factory_)
+ stream_factory_->OnSessionGoingAway(this);
+}
+
+void QuicClientSession::NotifyFactoryOfSessionClosedLater() {
DCHECK_EQ(0u, GetNumOpenStreams());
DCHECK(!connection()->connected());
base::MessageLoop::current()->PostTask(
FROM_HERE,
- base::Bind(&QuicClientSession::NotifyFactoryOfSessionClose,
+ base::Bind(&QuicClientSession::NotifyFactoryOfSessionClosed,
weak_factory_.GetWeakPtr()));
}
-void QuicClientSession::NotifyFactoryOfSessionClose() {
+void QuicClientSession::NotifyFactoryOfSessionClosed() {
DCHECK_EQ(0u, GetNumOpenStreams());
- DCHECK(stream_factory_);
// Will delete |this|.
- stream_factory_->OnSessionClose(this);
+ if (stream_factory_)
+ stream_factory_->OnSessionClosed(this);
}
} // namespace net
diff --git a/net/quic/quic_client_session.h b/net/quic/quic_client_session.h
index 2a7a7d0..f1a328e 100644
--- a/net/quic/quic_client_session.h
+++ b/net/quic/quic_client_session.h
@@ -34,6 +34,14 @@
class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession {
public:
+ // An interface for observing events on a session.
+ class NET_EXPORT_PRIVATE Observer {
+ public:
+ virtual ~Observer() {}
+ virtual void OnCryptoHandshakeConfirmed() = 0;
+ virtual void OnSessionClosed(int error) = 0;
+ };
+
// A helper class used to manage a request to create a stream.
class NET_EXPORT_PRIVATE StreamRequest {
public:
@@ -44,7 +52,7 @@
// |stream| will be updated with the newly created stream. If
// ERR_IO_PENDING is returned, then when the request is eventuallly
// complete |callback| will be called.
- int StartRequest(const base::WeakPtr<QuicClientSession> session,
+ int StartRequest(const base::WeakPtr<QuicClientSession>& session,
QuicReliableClientStream** stream,
const CompletionCallback& callback);
@@ -85,6 +93,9 @@
virtual ~QuicClientSession();
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
// Attempts to create a new stream. If the stream can be
// created immediately, returns OK. If the open stream limit
// has been reached, returns ERR_IO_PENDING, and |request|
@@ -149,6 +160,7 @@
private:
friend class test::QuicClientSessionPeer;
+ typedef std::set<Observer*> ObserverSet;
typedef std::list<StreamRequest*> StreamRequestQueue;
QuicReliableClientStream* CreateOutgoingReliableStreamImpl();
@@ -157,20 +169,36 @@
void OnClosedStream();
+ // A Session may be closed via any of three methods:
+ // OnConnectionClosed - called by the connection when the connection has been
+ // closed, perhaps due to a timeout or a protocol error.
+ // CloseSessionOnError - called from the owner of the session,
+ // the QuicStreamFactory, when there is an error.
+ // OnReadComplete - when there is a read error.
+ // This method closes all stream and performs any necessary cleanup.
void CloseSessionOnErrorInner(int net_error, QuicErrorCode quic_error);
+ void CloseAllStreams(int net_error);
+ void CloseAllObservers(int net_error);
+
+ // Notifies the factory that this session is going away and no more streams
+ // should be created from it. This needs to be called before closing any
+ // streams, because closing a stream may cause a new stream to be created.
+ void NotifyFactoryOfSessionGoingAway();
+
// Posts a task to notify the factory that this session has been closed.
- void NotifyFactoryOfSessionCloseLater();
+ void NotifyFactoryOfSessionClosedLater();
// Notifies the factory that this session has been closed which will
// delete |this|.
- void NotifyFactoryOfSessionClose();
+ void NotifyFactoryOfSessionClosed();
bool require_confirmation_;
scoped_ptr<QuicCryptoClientStream> crypto_stream_;
QuicStreamFactory* stream_factory_;
scoped_ptr<DatagramClientSocket> socket_;
scoped_refptr<IOBufferWithSize> read_buffer_;
+ ObserverSet observers_;
StreamRequestQueue stream_requests_;
bool read_pending_;
CompletionCallback callback_;
diff --git a/net/quic/quic_client_session_test.cc b/net/quic/quic_client_session_test.cc
index 385be52..65937de 100644
--- a/net/quic/quic_client_session_test.cc
+++ b/net/quic/quic_client_session_test.cc
@@ -6,6 +6,7 @@
#include <vector>
+#include "base/rand_util.h"
#include "net/base/capturing_net_log.h"
#include "net/base/test_completion_callback.h"
#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
@@ -15,6 +16,7 @@
#include "net/quic/test_tools/crypto_test_utils.h"
#include "net/quic/test_tools/quic_client_session_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/socket/socket_test_util.h"
#include "net/udp/datagram_client_socket.h"
using testing::_;
@@ -30,13 +32,24 @@
QuicClientSessionTest()
: guid_(1),
connection_(new PacketSavingConnection(guid_, IPEndPoint(), false)),
- session_(connection_, scoped_ptr<DatagramClientSocket>(), NULL,
+ session_(connection_, GetSocket().Pass(), NULL,
NULL, kServerHostname, DefaultQuicConfig(), &crypto_config_,
&net_log_) {
session_.config()->SetDefaults();
crypto_config_.SetDefaults();
}
+ virtual void TearDown() OVERRIDE {
+ session_.CloseSessionOnError(ERR_ABORTED);
+ }
+
+ scoped_ptr<DatagramClientSocket> GetSocket() {
+ socket_factory_.AddSocketDataProvider(&socket_data_);
+ return socket_factory_.CreateDatagramClientSocket(
+ DatagramSocket::DEFAULT_BIND, base::Bind(&base::RandInt),
+ &net_log_, NetLog::Source());
+ }
+
void CompleteCryptoHandshake() {
ASSERT_EQ(ERR_IO_PENDING,
session_.CryptoConnect(false, callback_.callback()));
@@ -48,6 +61,8 @@
QuicGuid guid_;
PacketSavingConnection* connection_;
CapturingNetLog net_log_;
+ MockClientSocketFactory socket_factory_;
+ StaticSocketDataProvider socket_data_;
QuicClientSession session_;
MockClock clock_;
MockRandom random_;
diff --git a/net/quic/quic_http_stream.cc b/net/quic/quic_http_stream.cc
index 79ab001..56936ed 100644
--- a/net/quic/quic_http_stream.cc
+++ b/net/quic/quic_http_stream.cc
@@ -24,9 +24,11 @@
static const size_t kHeaderBufInitialSize = 4096;
-QuicHttpStream::QuicHttpStream(const base::WeakPtr<QuicClientSession> session)
+QuicHttpStream::QuicHttpStream(const base::WeakPtr<QuicClientSession>& session)
: next_state_(STATE_NONE),
session_(session),
+ session_error_(OK),
+ was_handshake_confirmed_(session->IsCryptoHandshakeConfirmed()),
stream_(NULL),
request_info_(NULL),
request_body_stream_(NULL),
@@ -38,10 +40,13 @@
user_buffer_len_(0),
weak_factory_(this) {
DCHECK(session_);
+ session_->AddObserver(this);
}
QuicHttpStream::~QuicHttpStream() {
Close(false);
+ if (session_)
+ session_->RemoveObserver(this);
}
int QuicHttpStream::InitializeStream(const HttpRequestInfo* request_info,
@@ -50,7 +55,8 @@
const CompletionCallback& callback) {
DCHECK(!stream_);
if (!session_)
- return ERR_CONNECTION_CLOSED;
+ return was_handshake_confirmed_ ? ERR_CONNECTION_CLOSED :
+ ERR_QUIC_HANDSHAKE_FAILED;
stream_net_log_ = stream_net_log;
request_info_ = request_info;
@@ -59,19 +65,24 @@
int rv = stream_request_.StartRequest(
session_, &stream_, base::Bind(&QuicHttpStream::OnStreamReady,
weak_factory_.GetWeakPtr()));
- if (rv == ERR_IO_PENDING)
+ if (rv == ERR_IO_PENDING) {
callback_ = callback;
-
- if (rv == OK)
+ } else if (rv == OK) {
stream_->SetDelegate(this);
+ } else if (!was_handshake_confirmed_) {
+ rv = ERR_QUIC_HANDSHAKE_FAILED;
+ }
return rv;
}
void QuicHttpStream::OnStreamReady(int rv) {
DCHECK(rv == OK || !stream_);
- if (rv == OK)
+ if (rv == OK) {
stream_->SetDelegate(this);
+ } else if (!was_handshake_confirmed_) {
+ rv = ERR_QUIC_HANDSHAKE_FAILED;
+ }
ResetAndReturn(&callback_).Run(rv);
}
@@ -323,7 +334,8 @@
void QuicHttpStream::OnClose(QuicErrorCode error) {
if (error != QUIC_NO_ERROR) {
- response_status_ = ERR_QUIC_PROTOCOL_ERROR;
+ response_status_ = was_handshake_confirmed_ ?
+ ERR_QUIC_PROTOCOL_ERROR : ERR_QUIC_HANDSHAKE_FAILED;
} else if (!response_headers_received_) {
response_status_ = ERR_ABORTED;
}
@@ -335,7 +347,8 @@
void QuicHttpStream::OnError(int error) {
stream_ = NULL;
- response_status_ = error;
+ response_status_ = was_handshake_confirmed_ ?
+ error : ERR_QUIC_HANDSHAKE_FAILED;
if (!callback_.is_null())
DoCallback(response_status_);
}
@@ -344,6 +357,15 @@
return next_state_ > STATE_SEND_HEADERS_COMPLETE;
}
+void QuicHttpStream::OnCryptoHandshakeConfirmed() {
+ was_handshake_confirmed_ = true;
+}
+
+void QuicHttpStream::OnSessionClosed(int error) {
+ session_error_ = error;
+ session_.reset();
+}
+
void QuicHttpStream::OnIOComplete(int rv) {
rv = DoLoop(rv);
diff --git a/net/quic/quic_http_stream.h b/net/quic/quic_http_stream.h
index 71fb515..31e6e994 100644
--- a/net/quic/quic_http_stream.h
+++ b/net/quic/quic_http_stream.h
@@ -23,10 +23,11 @@
// non-owning pointer to a QuicReliableClientStream which it uses to
// send and receive data.
class NET_EXPORT_PRIVATE QuicHttpStream :
+ public QuicClientSession::Observer,
public QuicReliableClientStream::Delegate,
public HttpStream {
public:
- explicit QuicHttpStream(const base::WeakPtr<QuicClientSession> session);
+ explicit QuicHttpStream(const base::WeakPtr<QuicClientSession>& session);
virtual ~QuicHttpStream();
@@ -68,6 +69,10 @@
virtual void OnError(int error) OVERRIDE;
virtual bool HasSendHeadersComplete() OVERRIDE;
+ // QuicClientSession::Observer implementation
+ virtual void OnCryptoHandshakeConfirmed() OVERRIDE;
+ virtual void OnSessionClosed(int error) OVERRIDE;
+
private:
friend class test::QuicHttpStreamPeer;
@@ -102,7 +107,9 @@
State next_state_;
- const base::WeakPtr<QuicClientSession> session_;
+ base::WeakPtr<QuicClientSession> session_;
+ int session_error_; // Error code from the connection shutdown.
+ bool was_handshake_confirmed_; // True if the crypto handshake succeeded.
QuicClientSession::StreamRequest stream_request_;
QuicReliableClientStream* stream_; // Non-owning.
diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc
index b26087f9..ffbfad1 100644
--- a/net/quic/quic_http_stream_test.cc
+++ b/net/quic/quic_http_stream_test.cc
@@ -143,6 +143,7 @@
}
~QuicHttpStreamTest() {
+ session_->CloseSessionOnError(ERR_ABORTED);
for (size_t i = 0; i < writes_.size(); i++) {
delete writes_[i].packet;
}
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
index a9438c4d..526cb68 100644
--- a/net/quic/quic_network_transaction_unittest.cc
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -834,18 +834,28 @@
AddHangingNonAlternateProtocolSocketData();
+ // Final job that will proceed when the QUIC job fails.
+ MockRead http_reads[] = {
+ MockRead("HTTP/1.1 200 OK\r\n\r\n"),
+ MockRead("hello from http"),
+ MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
+ MockRead(ASYNC, OK)
+ };
+
+ StaticSocketDataProvider http_data(http_reads, arraysize(http_reads),
+ NULL, 0);
+ socket_factory_.AddSocketDataProvider(&http_data);
+
CreateSession();
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);
- EXPECT_EQ(ERR_CONNECTION_CLOSED, callback.WaitForResult());
+ SendRequestAndExpectHttpResponse("hello from http");
ExpectBrokenAlternateProtocolMapping();
+
+ EXPECT_TRUE(quic_data.at_read_eof());
+ EXPECT_TRUE(quic_data.at_write_eof());
}
} // namespace test
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc
index ecb94f9..7df1f37f 100644
--- a/net/quic/quic_stream_factory.cc
+++ b/net/quic/quic_stream_factory.cc
@@ -257,6 +257,7 @@
}
QuicStreamFactory::~QuicStreamFactory() {
+ CloseAllSessions(ERR_ABORTED);
STLDeleteElements(&all_sessions_);
STLDeleteValues(&active_jobs_);
STLDeleteValues(&all_crypto_configs_);
@@ -336,14 +337,14 @@
QuicClientSession* session = active_sessions_[host_port_proxy_pair];
DCHECK(session);
- return scoped_ptr<QuicHttpStream>(new QuicHttpStream(session->GetWeakPtr()));
+ return scoped_ptr<QuicHttpStream>(
+ new QuicHttpStream(session->GetWeakPtr()));
}
void QuicStreamFactory::OnIdleSession(QuicClientSession* session) {
}
-void QuicStreamFactory::OnSessionClose(QuicClientSession* session) {
- DCHECK_EQ(0u, session->GetNumOpenStreams());
+void QuicStreamFactory::OnSessionGoingAway(QuicClientSession* session) {
const AliasSet& aliases = session_aliases_[session];
for (AliasSet::const_iterator it = aliases.begin(); it != aliases.end();
++it) {
@@ -358,8 +359,13 @@
http_server_properties_->SetBrokenAlternateProtocol(it->first);
}
}
- all_sessions_.erase(session);
session_aliases_.erase(session);
+}
+
+void QuicStreamFactory::OnSessionClosed(QuicClientSession* session) {
+ DCHECK_EQ(0u, session->GetNumOpenStreams());
+ OnSessionGoingAway(session);
+ all_sessions_.erase(session);
delete session;
}
diff --git a/net/quic/quic_stream_factory.h b/net/quic/quic_stream_factory.h
index 48a9917..6316d62 100644
--- a/net/quic/quic_stream_factory.h
+++ b/net/quic/quic_stream_factory.h
@@ -109,8 +109,12 @@
// Called by a session when it becomes idle.
void OnIdleSession(QuicClientSession* session);
+ // Called by a session when it is going away and no more streams should be
+ // created on it.
+ void OnSessionGoingAway(QuicClientSession* session);
+
// Called by a session after it shuts down.
- void OnSessionClose(QuicClientSession* session);
+ void OnSessionClosed(QuicClientSession* session);
// Cancels a pending request.
void CancelRequest(QuicStreamRequest* request);
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc
index c09fcc2..8f1291a 100644
--- a/net/quic/quic_stream_factory_test.cc
+++ b/net/quic/quic_stream_factory_test.cc
@@ -32,6 +32,29 @@
const HostPortProxyPair& host_port_proxy_pair) {
return factory->GetOrCreateCryptoConfig(host_port_proxy_pair);
}
+
+ static bool HasActiveSession(QuicStreamFactory* factory,
+ const HostPortProxyPair& host_port_proxy_pair) {
+ return factory->HasActiveSession(host_port_proxy_pair);
+ }
+
+ static QuicClientSession* GetActiveSession(
+ QuicStreamFactory* factory,
+ const HostPortProxyPair& host_port_proxy_pair) {
+ DCHECK(factory->HasActiveSession(host_port_proxy_pair));
+ return factory->active_sessions_[host_port_proxy_pair];
+ }
+
+ static bool IsLiveSession(QuicStreamFactory* factory,
+ QuicClientSession* session) {
+ for (QuicStreamFactory::SessionSet::iterator it =
+ factory->all_sessions_.begin();
+ it != factory->all_sessions_.end(); ++it) {
+ if (*it == session)
+ return true;
+ }
+ return false;
+ }
};
class QuicStreamFactoryTest : public ::testing::Test {
@@ -180,6 +203,61 @@
EXPECT_TRUE(socket_data.at_write_eof());
}
+TEST_F(QuicStreamFactoryTest, Goaway) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
+ socket_data.StopAfter(1);
+ socket_factory_.AddSocketDataProvider(&socket_data);
+ DeterministicSocketData socket_data2(reads, arraysize(reads), NULL, 0);
+ socket_data2.StopAfter(1);
+ socket_factory_.AddSocketDataProvider(&socket_data2);
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
+ cert_verifier_.get(), net_log_,
+ callback_.callback()));
+
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+
+ // Mark the session as going away. Ensure that while it is still alive
+ // that it is no longer active.
+ QuicClientSession* session = QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, host_port_proxy_pair_);
+ factory_.OnSessionGoingAway(session);
+ EXPECT_EQ(true, QuicStreamFactoryPeer::IsLiveSession(&factory_, session));
+ EXPECT_FALSE(QuicStreamFactoryPeer::HasActiveSession(&factory_,
+ host_port_proxy_pair_));
+ EXPECT_EQ(NULL, factory_.CreateIfSessionExists(host_port_proxy_pair_,
+ net_log_).get());
+
+ // Create a new request for the same destination and verify that a
+ // new session is created.
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_proxy_pair_, is_https_,
+ cert_verifier_.get(), net_log_,
+ callback_.callback()));
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
+ EXPECT_TRUE(stream2.get());
+
+ EXPECT_TRUE(QuicStreamFactoryPeer::HasActiveSession(&factory_,
+ host_port_proxy_pair_));
+ EXPECT_NE(session,
+ QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, host_port_proxy_pair_));
+ EXPECT_EQ(true, QuicStreamFactoryPeer::IsLiveSession(&factory_, session));
+
+ stream2.reset();
+ stream.reset();
+
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+}
+
TEST_F(QuicStreamFactoryTest, MaxOpenStream) {
MockRead reads[] = {
MockRead(ASYNC, OK, 0) // EOF
diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h
index 5449e87..99bbd4d 100644
--- a/net/quic/test_tools/quic_test_utils.h
+++ b/net/quic/test_tools/quic_test_utils.h
@@ -292,6 +292,7 @@
QuicStreamOffset offset,
bool fin));
MOCK_METHOD0(IsHandshakeComplete, bool());
+ MOCK_METHOD0(IsCryptoHandshakeConfirmed, bool());
private:
DISALLOW_COPY_AND_ASSIGN(MockSession);