blob: f95ea511fa3a17985f9b717ea06375915ddd33c2 [file] [log] [blame]
// 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/http/http_stream_factory_impl.h"
#include <stdint.h>
#include <string>
#include <utility>
#include <vector>
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "net/base/port_util.h"
#include "net/base/test_completion_callback.h"
#include "net/base/test_data_directory.h"
#include "net/cert/mock_cert_verifier.h"
#include "net/dns/mock_host_resolver.h"
#include "net/http/bidirectional_stream_impl.h"
#include "net/http/bidirectional_stream_request_info.h"
#include "net/http/http_auth_handler_factory.h"
#include "net/http/http_network_session.h"
#include "net/http/http_network_session_peer.h"
#include "net/http/http_network_transaction.h"
#include "net/http/http_request_info.h"
#include "net/http/http_server_properties.h"
#include "net/http/http_server_properties_impl.h"
#include "net/http/http_stream.h"
#include "net/http/transport_security_state.h"
#include "net/log/net_log.h"
#include "net/proxy/proxy_info.h"
#include "net/proxy/proxy_service.h"
#include "net/quic/quic_http_utils.h"
#include "net/quic/quic_server_id.h"
#include "net/quic/test_tools/crypto_test_utils.h"
#include "net/quic/test_tools/mock_crypto_client_stream_factory.h"
#include "net/quic/test_tools/mock_random.h"
#include "net/quic/test_tools/quic_stream_factory_peer.h"
#include "net/quic/test_tools/quic_test_packet_maker.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/socket/client_socket_handle.h"
#include "net/socket/mock_client_socket_pool_manager.h"
#include "net/socket/next_proto.h"
#include "net/socket/socket_test_util.h"
#include "net/spdy/spdy_session.h"
#include "net/spdy/spdy_session_pool.h"
#include "net/spdy/spdy_test_util_common.h"
#include "net/ssl/ssl_config_service.h"
#include "net/ssl/ssl_config_service_defaults.h"
#include "net/test/cert_test_util.h"
// This file can be included from net/http even though
// it is in net/websockets because it doesn't
// introduce any link dependency to net/websockets.
#include "net/websockets/websocket_handshake_stream_base.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
class BidirectionalStreamImpl;
namespace {
class MockWebSocketHandshakeStream : public WebSocketHandshakeStreamBase {
public:
enum StreamType {
kStreamTypeBasic,
kStreamTypeSpdy,
};
explicit MockWebSocketHandshakeStream(StreamType type) : type_(type) {}
~MockWebSocketHandshakeStream() override {}
StreamType type() const {
return type_;
}
// HttpStream methods
int InitializeStream(const HttpRequestInfo* request_info,
RequestPriority priority,
const BoundNetLog& net_log,
const CompletionCallback& callback) override {
return ERR_IO_PENDING;
}
int SendRequest(const HttpRequestHeaders& request_headers,
HttpResponseInfo* response,
const CompletionCallback& callback) override {
return ERR_IO_PENDING;
}
int ReadResponseHeaders(const CompletionCallback& callback) override {
return ERR_IO_PENDING;
}
int ReadResponseBody(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) override {
return ERR_IO_PENDING;
}
void Close(bool not_reusable) override {}
bool IsResponseBodyComplete() const override { return false; }
bool IsConnectionReused() const override { return false; }
void SetConnectionReused() override {}
bool CanReuseConnection() const override { return false; }
int64_t GetTotalReceivedBytes() const override { return 0; }
int64_t GetTotalSentBytes() const override { return 0; }
bool GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const override {
return false;
}
void GetSSLInfo(SSLInfo* ssl_info) override {}
void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info) override {}
bool GetRemoteEndpoint(IPEndPoint* endpoint) override { return false; }
Error GetSignedEKMForTokenBinding(crypto::ECPrivateKey* key,
std::vector<uint8_t>* out) override {
ADD_FAILURE();
return ERR_NOT_IMPLEMENTED;
}
void Drain(HttpNetworkSession* session) override {}
void PopulateNetErrorDetails(NetErrorDetails* details) override { return; }
void SetPriority(RequestPriority priority) override {}
UploadProgress GetUploadProgress() const override { return UploadProgress(); }
HttpStream* RenewStreamForAuth() override { return nullptr; }
scoped_ptr<WebSocketStream> Upgrade() override {
return scoped_ptr<WebSocketStream>();
}
private:
const StreamType type_;
};
// HttpStreamFactoryImpl subclass that can wait until a preconnect is complete.
class MockHttpStreamFactoryImplForPreconnect : public HttpStreamFactoryImpl {
public:
MockHttpStreamFactoryImplForPreconnect(HttpNetworkSession* session)
: HttpStreamFactoryImpl(session, false),
preconnect_done_(false),
waiting_for_preconnect_(false) {}
void WaitForPreconnects() {
while (!preconnect_done_) {
waiting_for_preconnect_ = true;
base::MessageLoop::current()->Run();
waiting_for_preconnect_ = false;
}
}
private:
// HttpStreamFactoryImpl methods.
void OnPreconnectsCompleteInternal() override {
preconnect_done_ = true;
if (waiting_for_preconnect_)
base::MessageLoop::current()->QuitWhenIdle();
}
bool preconnect_done_;
bool waiting_for_preconnect_;
};
class StreamRequestWaiter : public HttpStreamRequest::Delegate {
public:
StreamRequestWaiter()
: waiting_for_stream_(false), stream_done_(false), error_status_(OK) {}
// HttpStreamRequest::Delegate
void OnStreamReady(const SSLConfig& used_ssl_config,
const ProxyInfo& used_proxy_info,
HttpStream* stream) override {
stream_done_ = true;
if (waiting_for_stream_)
base::MessageLoop::current()->QuitWhenIdle();
stream_.reset(stream);
used_ssl_config_ = used_ssl_config;
used_proxy_info_ = used_proxy_info;
}
void OnWebSocketHandshakeStreamReady(
const SSLConfig& used_ssl_config,
const ProxyInfo& used_proxy_info,
WebSocketHandshakeStreamBase* stream) override {
stream_done_ = true;
if (waiting_for_stream_)
base::MessageLoop::current()->QuitWhenIdle();
websocket_stream_.reset(stream);
used_ssl_config_ = used_ssl_config;
used_proxy_info_ = used_proxy_info;
}
void OnBidirectionalStreamImplReady(
const SSLConfig& used_ssl_config,
const ProxyInfo& used_proxy_info,
BidirectionalStreamImpl* stream) override {
stream_done_ = true;
if (waiting_for_stream_)
base::MessageLoop::current()->QuitWhenIdle();
bidirectional_stream_impl_.reset(stream);
used_ssl_config_ = used_ssl_config;
used_proxy_info_ = used_proxy_info;
}
void OnStreamFailed(int status,
const SSLConfig& used_ssl_config,
SSLFailureState ssl_failure_state) override {
stream_done_ = true;
if (waiting_for_stream_)
base::MessageLoop::current()->QuitWhenIdle();
used_ssl_config_ = used_ssl_config;
error_status_ = status;
}
void OnCertificateError(int status,
const SSLConfig& used_ssl_config,
const SSLInfo& ssl_info) override {}
void OnNeedsProxyAuth(const HttpResponseInfo& proxy_response,
const SSLConfig& used_ssl_config,
const ProxyInfo& used_proxy_info,
HttpAuthController* auth_controller) override {}
void OnNeedsClientAuth(const SSLConfig& used_ssl_config,
SSLCertRequestInfo* cert_info) override {}
void OnHttpsProxyTunnelResponse(const HttpResponseInfo& response_info,
const SSLConfig& used_ssl_config,
const ProxyInfo& used_proxy_info,
HttpStream* stream) override {}
void OnQuicBroken() override {}
void WaitForStream() {
while (!stream_done_) {
waiting_for_stream_ = true;
base::MessageLoop::current()->Run();
waiting_for_stream_ = false;
}
}
const SSLConfig& used_ssl_config() const {
return used_ssl_config_;
}
const ProxyInfo& used_proxy_info() const {
return used_proxy_info_;
}
HttpStream* stream() {
return stream_.get();
}
MockWebSocketHandshakeStream* websocket_stream() {
return static_cast<MockWebSocketHandshakeStream*>(websocket_stream_.get());
}
BidirectionalStreamImpl* bidirectional_stream_impl() {
return bidirectional_stream_impl_.get();
}
bool stream_done() const { return stream_done_; }
int error_status() const { return error_status_; }
private:
bool waiting_for_stream_;
bool stream_done_;
scoped_ptr<HttpStream> stream_;
scoped_ptr<WebSocketHandshakeStreamBase> websocket_stream_;
scoped_ptr<BidirectionalStreamImpl> bidirectional_stream_impl_;
SSLConfig used_ssl_config_;
ProxyInfo used_proxy_info_;
int error_status_;
DISALLOW_COPY_AND_ASSIGN(StreamRequestWaiter);
};
class WebSocketSpdyHandshakeStream : public MockWebSocketHandshakeStream {
public:
explicit WebSocketSpdyHandshakeStream(
const base::WeakPtr<SpdySession>& spdy_session)
: MockWebSocketHandshakeStream(kStreamTypeSpdy),
spdy_session_(spdy_session) {}
~WebSocketSpdyHandshakeStream() override {}
SpdySession* spdy_session() { return spdy_session_.get(); }
private:
base::WeakPtr<SpdySession> spdy_session_;
};
class WebSocketBasicHandshakeStream : public MockWebSocketHandshakeStream {
public:
explicit WebSocketBasicHandshakeStream(
scoped_ptr<ClientSocketHandle> connection)
: MockWebSocketHandshakeStream(kStreamTypeBasic),
connection_(std::move(connection)) {}
~WebSocketBasicHandshakeStream() override {
connection_->socket()->Disconnect();
}
ClientSocketHandle* connection() { return connection_.get(); }
private:
scoped_ptr<ClientSocketHandle> connection_;
};
class WebSocketStreamCreateHelper
: public WebSocketHandshakeStreamBase::CreateHelper {
public:
~WebSocketStreamCreateHelper() override {}
WebSocketHandshakeStreamBase* CreateBasicStream(
scoped_ptr<ClientSocketHandle> connection,
bool using_proxy) override {
return new WebSocketBasicHandshakeStream(std::move(connection));
}
WebSocketHandshakeStreamBase* CreateSpdyStream(
const base::WeakPtr<SpdySession>& spdy_session,
bool use_relative_url) override {
return new WebSocketSpdyHandshakeStream(spdy_session);
}
};
struct TestCase {
int num_streams;
bool ssl;
};
TestCase kTests[] = {
{ 1, false },
{ 2, false },
{ 1, true},
{ 2, true},
};
void PreconnectHelperForURL(int num_streams,
const GURL& url,
HttpNetworkSession* session) {
HttpNetworkSessionPeer peer(session);
MockHttpStreamFactoryImplForPreconnect* mock_factory =
new MockHttpStreamFactoryImplForPreconnect(session);
peer.SetHttpStreamFactory(scoped_ptr<HttpStreamFactory>(mock_factory));
SSLConfig ssl_config;
session->ssl_config_service()->GetSSLConfig(&ssl_config);
HttpRequestInfo request;
request.method = "GET";
request.url = url;
request.load_flags = 0;
session->http_stream_factory()->PreconnectStreams(num_streams, request,
ssl_config, ssl_config);
mock_factory->WaitForPreconnects();
}
void PreconnectHelper(const TestCase& test,
HttpNetworkSession* session) {
GURL url = test.ssl ? GURL("https://www.google.com") :
GURL("http://www.google.com");
PreconnectHelperForURL(test.num_streams, url, session);
}
template<typename ParentPool>
class CapturePreconnectsSocketPool : public ParentPool {
public:
CapturePreconnectsSocketPool(HostResolver* host_resolver,
CertVerifier* cert_verifier);
int last_num_streams() const {
return last_num_streams_;
}
int RequestSocket(const std::string& group_name,
const void* socket_params,
RequestPriority priority,
ClientSocketPool::RespectLimits respect_limits,
ClientSocketHandle* handle,
const CompletionCallback& callback,
const BoundNetLog& net_log) override {
ADD_FAILURE();
return ERR_UNEXPECTED;
}
void RequestSockets(const std::string& group_name,
const void* socket_params,
int num_sockets,
const BoundNetLog& net_log) override {
last_num_streams_ = num_sockets;
}
void CancelRequest(const std::string& group_name,
ClientSocketHandle* handle) override {
ADD_FAILURE();
}
void ReleaseSocket(const std::string& group_name,
scoped_ptr<StreamSocket> socket,
int id) override {
ADD_FAILURE();
}
void CloseIdleSockets() override { ADD_FAILURE(); }
int IdleSocketCount() const override {
ADD_FAILURE();
return 0;
}
int IdleSocketCountInGroup(const std::string& group_name) const override {
ADD_FAILURE();
return 0;
}
LoadState GetLoadState(const std::string& group_name,
const ClientSocketHandle* handle) const override {
ADD_FAILURE();
return LOAD_STATE_IDLE;
}
base::TimeDelta ConnectionTimeout() const override {
return base::TimeDelta();
}
private:
int last_num_streams_;
};
typedef CapturePreconnectsSocketPool<TransportClientSocketPool>
CapturePreconnectsTransportSocketPool;
typedef CapturePreconnectsSocketPool<HttpProxyClientSocketPool>
CapturePreconnectsHttpProxySocketPool;
typedef CapturePreconnectsSocketPool<SOCKSClientSocketPool>
CapturePreconnectsSOCKSSocketPool;
typedef CapturePreconnectsSocketPool<SSLClientSocketPool>
CapturePreconnectsSSLSocketPool;
template <typename ParentPool>
CapturePreconnectsSocketPool<ParentPool>::CapturePreconnectsSocketPool(
HostResolver* host_resolver,
CertVerifier* /* cert_verifier */)
: ParentPool(0, 0, host_resolver, nullptr, nullptr), last_num_streams_(-1) {
}
template <>
CapturePreconnectsHttpProxySocketPool::CapturePreconnectsSocketPool(
HostResolver* /* host_resolver */,
CertVerifier* /* cert_verifier */)
: HttpProxyClientSocketPool(0, 0, nullptr, nullptr, nullptr),
last_num_streams_(-1) {
}
template <>
CapturePreconnectsSSLSocketPool::CapturePreconnectsSocketPool(
HostResolver* /* host_resolver */,
CertVerifier* cert_verifier)
: SSLClientSocketPool(0,
0,
cert_verifier,
nullptr, // channel_id_store
nullptr, // transport_security_state
nullptr, // cert_transparency_verifier
nullptr, // ct_policy_enforcer
std::string(), // ssl_session_cache_shard
nullptr, // deterministic_socket_factory
nullptr, // transport_socket_pool
nullptr,
nullptr,
nullptr, // ssl_config_service
nullptr), // net_log
last_num_streams_(-1) {
}
class HttpStreamFactoryTest : public ::testing::Test,
public ::testing::WithParamInterface<NextProto> {
};
INSTANTIATE_TEST_CASE_P(NextProto,
HttpStreamFactoryTest,
testing::Values(kProtoSPDY31,
kProtoHTTP2));
TEST_P(HttpStreamFactoryTest, PreconnectDirect) {
for (size_t i = 0; i < arraysize(kTests); ++i) {
SpdySessionDependencies session_deps(
GetParam(), ProxyService::CreateDirect());
scoped_ptr<HttpNetworkSession> session(
SpdySessionDependencies::SpdyCreateSession(&session_deps));
HttpNetworkSessionPeer peer(session.get());
CapturePreconnectsTransportSocketPool* transport_conn_pool =
new CapturePreconnectsTransportSocketPool(
session_deps.host_resolver.get(),
session_deps.cert_verifier.get());
CapturePreconnectsSSLSocketPool* ssl_conn_pool =
new CapturePreconnectsSSLSocketPool(
session_deps.host_resolver.get(),
session_deps.cert_verifier.get());
scoped_ptr<MockClientSocketPoolManager> mock_pool_manager(
new MockClientSocketPoolManager);
mock_pool_manager->SetTransportSocketPool(transport_conn_pool);
mock_pool_manager->SetSSLSocketPool(ssl_conn_pool);
peer.SetClientSocketPoolManager(std::move(mock_pool_manager));
PreconnectHelper(kTests[i], session.get());
if (kTests[i].ssl)
EXPECT_EQ(kTests[i].num_streams, ssl_conn_pool->last_num_streams());
else
EXPECT_EQ(kTests[i].num_streams, transport_conn_pool->last_num_streams());
}
}
TEST_P(HttpStreamFactoryTest, PreconnectHttpProxy) {
for (size_t i = 0; i < arraysize(kTests); ++i) {
SpdySessionDependencies session_deps(
GetParam(), ProxyService::CreateFixed("http_proxy"));
scoped_ptr<HttpNetworkSession> session(
SpdySessionDependencies::SpdyCreateSession(&session_deps));
HttpNetworkSessionPeer peer(session.get());
HostPortPair proxy_host("http_proxy", 80);
CapturePreconnectsHttpProxySocketPool* http_proxy_pool =
new CapturePreconnectsHttpProxySocketPool(
session_deps.host_resolver.get(),
session_deps.cert_verifier.get());
CapturePreconnectsSSLSocketPool* ssl_conn_pool =
new CapturePreconnectsSSLSocketPool(
session_deps.host_resolver.get(),
session_deps.cert_verifier.get());
scoped_ptr<MockClientSocketPoolManager> mock_pool_manager(
new MockClientSocketPoolManager);
mock_pool_manager->SetSocketPoolForHTTPProxy(proxy_host, http_proxy_pool);
mock_pool_manager->SetSocketPoolForSSLWithProxy(proxy_host, ssl_conn_pool);
peer.SetClientSocketPoolManager(std::move(mock_pool_manager));
PreconnectHelper(kTests[i], session.get());
if (kTests[i].ssl)
EXPECT_EQ(kTests[i].num_streams, ssl_conn_pool->last_num_streams());
else
EXPECT_EQ(kTests[i].num_streams, http_proxy_pool->last_num_streams());
}
}
TEST_P(HttpStreamFactoryTest, PreconnectSocksProxy) {
for (size_t i = 0; i < arraysize(kTests); ++i) {
SpdySessionDependencies session_deps(
GetParam(), ProxyService::CreateFixed("socks4://socks_proxy:1080"));
scoped_ptr<HttpNetworkSession> session(
SpdySessionDependencies::SpdyCreateSession(&session_deps));
HttpNetworkSessionPeer peer(session.get());
HostPortPair proxy_host("socks_proxy", 1080);
CapturePreconnectsSOCKSSocketPool* socks_proxy_pool =
new CapturePreconnectsSOCKSSocketPool(
session_deps.host_resolver.get(),
session_deps.cert_verifier.get());
CapturePreconnectsSSLSocketPool* ssl_conn_pool =
new CapturePreconnectsSSLSocketPool(
session_deps.host_resolver.get(),
session_deps.cert_verifier.get());
scoped_ptr<MockClientSocketPoolManager> mock_pool_manager(
new MockClientSocketPoolManager);
mock_pool_manager->SetSocketPoolForSOCKSProxy(proxy_host, socks_proxy_pool);
mock_pool_manager->SetSocketPoolForSSLWithProxy(proxy_host, ssl_conn_pool);
peer.SetClientSocketPoolManager(std::move(mock_pool_manager));
PreconnectHelper(kTests[i], session.get());
if (kTests[i].ssl)
EXPECT_EQ(kTests[i].num_streams, ssl_conn_pool->last_num_streams());
else
EXPECT_EQ(kTests[i].num_streams, socks_proxy_pool->last_num_streams());
}
}
TEST_P(HttpStreamFactoryTest, PreconnectDirectWithExistingSpdySession) {
for (size_t i = 0; i < arraysize(kTests); ++i) {
SpdySessionDependencies session_deps(
GetParam(), ProxyService::CreateDirect());
scoped_ptr<HttpNetworkSession> session(
SpdySessionDependencies::SpdyCreateSession(&session_deps));
HttpNetworkSessionPeer peer(session.get());
// Put a SpdySession in the pool.
HostPortPair host_port_pair("www.google.com", 443);
SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
PRIVACY_MODE_DISABLED);
ignore_result(CreateFakeSpdySession(session->spdy_session_pool(), key));
CapturePreconnectsTransportSocketPool* transport_conn_pool =
new CapturePreconnectsTransportSocketPool(
session_deps.host_resolver.get(),
session_deps.cert_verifier.get());
CapturePreconnectsSSLSocketPool* ssl_conn_pool =
new CapturePreconnectsSSLSocketPool(
session_deps.host_resolver.get(),
session_deps.cert_verifier.get());
scoped_ptr<MockClientSocketPoolManager> mock_pool_manager(
new MockClientSocketPoolManager);
mock_pool_manager->SetTransportSocketPool(transport_conn_pool);
mock_pool_manager->SetSSLSocketPool(ssl_conn_pool);
peer.SetClientSocketPoolManager(std::move(mock_pool_manager));
PreconnectHelper(kTests[i], session.get());
// We shouldn't be preconnecting if we have an existing session, which is
// the case for https://www.google.com.
if (kTests[i].ssl)
EXPECT_EQ(-1, ssl_conn_pool->last_num_streams());
else
EXPECT_EQ(kTests[i].num_streams,
transport_conn_pool->last_num_streams());
}
}
// Verify that preconnects to unsafe ports are cancelled before they reach
// the SocketPool.
TEST_P(HttpStreamFactoryTest, PreconnectUnsafePort) {
ASSERT_FALSE(IsPortAllowedForScheme(7, "http"));
SpdySessionDependencies session_deps(
GetParam(), ProxyService::CreateDirect());
scoped_ptr<HttpNetworkSession> session(
SpdySessionDependencies::SpdyCreateSession(&session_deps));
HttpNetworkSessionPeer peer(session.get());
CapturePreconnectsTransportSocketPool* transport_conn_pool =
new CapturePreconnectsTransportSocketPool(
session_deps.host_resolver.get(),
session_deps.cert_verifier.get());
scoped_ptr<MockClientSocketPoolManager> mock_pool_manager(
new MockClientSocketPoolManager);
mock_pool_manager->SetTransportSocketPool(transport_conn_pool);
peer.SetClientSocketPoolManager(std::move(mock_pool_manager));
PreconnectHelperForURL(1, GURL("http://www.google.com:7"), session.get());
EXPECT_EQ(-1, transport_conn_pool->last_num_streams());
}
TEST_P(HttpStreamFactoryTest, JobNotifiesProxy) {
const char* kProxyString = "PROXY bad:99; PROXY maybe:80; DIRECT";
SpdySessionDependencies session_deps(
GetParam(), ProxyService::CreateFixedFromPacResult(kProxyString));
// First connection attempt fails
StaticSocketDataProvider socket_data1;
socket_data1.set_connect_data(MockConnect(ASYNC, ERR_ADDRESS_UNREACHABLE));
session_deps.socket_factory->AddSocketDataProvider(&socket_data1);
// Second connection attempt succeeds
StaticSocketDataProvider socket_data2;
socket_data2.set_connect_data(MockConnect(ASYNC, OK));
session_deps.socket_factory->AddSocketDataProvider(&socket_data2);
scoped_ptr<HttpNetworkSession> session(
SpdySessionDependencies::SpdyCreateSession(&session_deps));
// Now request a stream. It should succeed using the second proxy in the
// list.
HttpRequestInfo request_info;
request_info.method = "GET";
request_info.url = GURL("http://www.google.com");
SSLConfig ssl_config;
StreamRequestWaiter waiter;
scoped_ptr<HttpStreamRequest> request(
session->http_stream_factory()->RequestStream(
request_info, DEFAULT_PRIORITY, ssl_config, ssl_config,
&waiter, BoundNetLog()));
waiter.WaitForStream();
// The proxy that failed should now be known to the proxy_service as bad.
const ProxyRetryInfoMap& retry_info =
session->proxy_service()->proxy_retry_info();
EXPECT_EQ(1u, retry_info.size());
ProxyRetryInfoMap::const_iterator iter = retry_info.find("bad:99");
EXPECT_TRUE(iter != retry_info.end());
}
TEST_P(HttpStreamFactoryTest, UnreachableQuicProxyMarkedAsBad) {
const int mock_error[] = {ERR_PROXY_CONNECTION_FAILED,
ERR_NAME_NOT_RESOLVED,
ERR_INTERNET_DISCONNECTED,
ERR_ADDRESS_UNREACHABLE,
ERR_CONNECTION_CLOSED,
ERR_CONNECTION_TIMED_OUT,
ERR_CONNECTION_RESET,
ERR_CONNECTION_REFUSED,
ERR_CONNECTION_ABORTED,
ERR_TIMED_OUT,
ERR_TUNNEL_CONNECTION_FAILED,
ERR_SOCKS_CONNECTION_FAILED,
ERR_PROXY_CERTIFICATE_INVALID,
ERR_QUIC_PROTOCOL_ERROR,
ERR_QUIC_HANDSHAKE_FAILED,
ERR_SSL_PROTOCOL_ERROR,
ERR_MSG_TOO_BIG};
for (size_t i = 0; i < arraysize(mock_error); ++i) {
scoped_ptr<ProxyService> proxy_service;
proxy_service =
ProxyService::CreateFixedFromPacResult("QUIC bad:99; DIRECT");
HttpNetworkSession::Params params;
params.enable_quic = true;
params.quic_disable_preconnect_if_0rtt = false;
params.enable_quic_for_proxies = true;
scoped_refptr<SSLConfigServiceDefaults> ssl_config_service(
new SSLConfigServiceDefaults);
HttpServerPropertiesImpl http_server_properties;
MockClientSocketFactory socket_factory;
params.client_socket_factory = &socket_factory;
MockHostResolver host_resolver;
params.host_resolver = &host_resolver;
TransportSecurityState transport_security_state;
params.transport_security_state = &transport_security_state;
params.proxy_service = proxy_service.get();
params.ssl_config_service = ssl_config_service.get();
params.http_server_properties = http_server_properties.GetWeakPtr();
scoped_ptr<HttpNetworkSession> session(new HttpNetworkSession(params));
session->quic_stream_factory()->set_require_confirmation(false);
StaticSocketDataProvider socket_data1;
socket_data1.set_connect_data(MockConnect(ASYNC, mock_error[i]));
socket_factory.AddSocketDataProvider(&socket_data1);
// Second connection attempt succeeds.
StaticSocketDataProvider socket_data2;
socket_data2.set_connect_data(MockConnect(ASYNC, OK));
socket_factory.AddSocketDataProvider(&socket_data2);
// Now request a stream. It should succeed using the second proxy in the
// list.
HttpRequestInfo request_info;
request_info.method = "GET";
request_info.url = GURL("http://www.google.com");
SSLConfig ssl_config;
StreamRequestWaiter waiter;
scoped_ptr<HttpStreamRequest> request(
session->http_stream_factory()->RequestStream(
request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter,
BoundNetLog()));
waiter.WaitForStream();
// The proxy that failed should now be known to the proxy_service as bad.
const ProxyRetryInfoMap& retry_info =
session->proxy_service()->proxy_retry_info();
EXPECT_EQ(1u, retry_info.size()) << mock_error[i];
EXPECT_TRUE(waiter.used_proxy_info().is_direct());
ProxyRetryInfoMap::const_iterator iter = retry_info.find("quic://bad:99");
EXPECT_TRUE(iter != retry_info.end()) << mock_error[i];
}
}
// BidirectionalStreamImpl::Delegate to wait until response headers are
// received.
class TestBidirectionalDelegate : public BidirectionalStreamImpl::Delegate {
public:
void WaitUntilDone() { loop_.Run(); }
const SpdyHeaderBlock& response_headers() const { return response_headers_; }
private:
void OnHeadersSent() override {}
void OnHeadersReceived(const SpdyHeaderBlock& response_headers) override {
response_headers_ = response_headers;
loop_.Quit();
}
void OnDataRead(int bytes_read) override { NOTREACHED(); }
void OnDataSent() override { NOTREACHED(); }
void OnTrailersReceived(const SpdyHeaderBlock& trailers) override {
NOTREACHED();
}
void OnFailed(int error) override { NOTREACHED(); }
base::RunLoop loop_;
SpdyHeaderBlock response_headers_;
};
// Helper class to encapsulate MockReads and MockWrites for QUIC.
// Simplify ownership issues and the interaction with the MockSocketFactory.
class MockQuicData {
public:
MockQuicData() : packet_number_(0) {}
~MockQuicData() { STLDeleteElements(&packets_); }
void AddRead(scoped_ptr<QuicEncryptedPacket> packet) {
reads_.push_back(
MockRead(ASYNC, packet->data(), packet->length(), packet_number_++));
packets_.push_back(packet.release());
}
void AddRead(IoMode mode, int rv) {
reads_.push_back(MockRead(mode, rv, packet_number_++));
}
void AddWrite(scoped_ptr<QuicEncryptedPacket> packet) {
writes_.push_back(MockWrite(SYNCHRONOUS, packet->data(), packet->length(),
packet_number_++));
packets_.push_back(packet.release());
}
void AddSocketDataToFactory(MockClientSocketFactory* factory) {
MockRead* reads = reads_.empty() ? nullptr : &reads_[0];
MockWrite* writes = writes_.empty() ? nullptr : &writes_[0];
socket_data_.reset(
new SequencedSocketData(reads, reads_.size(), writes, writes_.size()));
factory->AddSocketDataProvider(socket_data_.get());
}
private:
std::vector<QuicEncryptedPacket*> packets_;
std::vector<MockWrite> writes_;
std::vector<MockRead> reads_;
size_t packet_number_;
scoped_ptr<SequencedSocketData> socket_data_;
};
} // namespace
TEST_P(HttpStreamFactoryTest, QuicLossyProxyMarkedAsBad) {
// Checks if a
scoped_ptr<ProxyService> proxy_service;
proxy_service = ProxyService::CreateFixedFromPacResult("QUIC bad:99; DIRECT");
HttpNetworkSession::Params params;
params.enable_quic = true;
params.enable_quic_for_proxies = true;
params.quic_disable_preconnect_if_0rtt = false;
scoped_refptr<SSLConfigServiceDefaults> ssl_config_service(
new SSLConfigServiceDefaults);
HttpServerPropertiesImpl http_server_properties;
MockClientSocketFactory socket_factory;
params.client_socket_factory = &socket_factory;
MockHostResolver host_resolver;
params.host_resolver = &host_resolver;
TransportSecurityState transport_security_state;
params.transport_security_state = &transport_security_state;
params.proxy_service = proxy_service.get();
params.ssl_config_service = ssl_config_service.get();
params.http_server_properties = http_server_properties.GetWeakPtr();
params.quic_max_number_of_lossy_connections = 2;
scoped_ptr<HttpNetworkSession> session(new HttpNetworkSession(params));
session->quic_stream_factory()->set_require_confirmation(false);
session->quic_stream_factory()->number_of_lossy_connections_[99] =
params.quic_max_number_of_lossy_connections;
session->quic_stream_factory()->MaybeDisableQuic(99);
ASSERT_TRUE(session->quic_stream_factory()->IsQuicDisabled(99));
StaticSocketDataProvider socket_data2;
socket_data2.set_connect_data(MockConnect(ASYNC, OK));
socket_factory.AddSocketDataProvider(&socket_data2);
// Now request a stream. It should succeed using the second proxy in the
// list.
HttpRequestInfo request_info;
request_info.method = "GET";
request_info.url = GURL("http://www.google.com");
SSLConfig ssl_config;
StreamRequestWaiter waiter;
scoped_ptr<HttpStreamRequest> request(
session->http_stream_factory()->RequestStream(
request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter,
BoundNetLog()));
waiter.WaitForStream();
// The proxy that failed should now be known to the proxy_service as bad.
const ProxyRetryInfoMap& retry_info =
session->proxy_service()->proxy_retry_info();
EXPECT_EQ(1u, retry_info.size());
EXPECT_TRUE(waiter.used_proxy_info().is_direct());
ProxyRetryInfoMap::const_iterator iter = retry_info.find("quic://bad:99");
EXPECT_TRUE(iter != retry_info.end());
}
TEST_P(HttpStreamFactoryTest, UsePreConnectIfNoZeroRTT) {
for (int num_streams = 1; num_streams < 3; ++num_streams) {
GURL url = GURL("https://www.google.com");
// Set up QUIC as alternative_service.
HttpServerPropertiesImpl http_server_properties;
const AlternativeService alternative_service(QUIC, url.host().c_str(),
url.IntPort());
AlternativeServiceInfoVector alternative_service_info_vector;
base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
alternative_service_info_vector.push_back(
AlternativeServiceInfo(alternative_service, expiration));
HostPortPair host_port_pair(alternative_service.host_port_pair());
http_server_properties.SetAlternativeServices(
host_port_pair, alternative_service_info_vector);
SpdySessionDependencies session_deps(
GetParam(), ProxyService::CreateFixed("http_proxy"));
// Setup params to disable preconnect, but QUIC doesn't 0RTT.
HttpNetworkSession::Params params =
SpdySessionDependencies::CreateSessionParams(&session_deps);
params.enable_quic = true;
params.quic_disable_preconnect_if_0rtt = true;
params.http_server_properties = http_server_properties.GetWeakPtr();
scoped_ptr<HttpNetworkSession> session(new HttpNetworkSession(params));
HttpNetworkSessionPeer peer(session.get());
HostPortPair proxy_host("http_proxy", 80);
CapturePreconnectsHttpProxySocketPool* http_proxy_pool =
new CapturePreconnectsHttpProxySocketPool(
session_deps.host_resolver.get(), session_deps.cert_verifier.get());
CapturePreconnectsSSLSocketPool* ssl_conn_pool =
new CapturePreconnectsSSLSocketPool(session_deps.host_resolver.get(),
session_deps.cert_verifier.get());
scoped_ptr<MockClientSocketPoolManager> mock_pool_manager(
new MockClientSocketPoolManager);
mock_pool_manager->SetSocketPoolForHTTPProxy(proxy_host, http_proxy_pool);
mock_pool_manager->SetSocketPoolForSSLWithProxy(proxy_host, ssl_conn_pool);
peer.SetClientSocketPoolManager(std::move(mock_pool_manager));
PreconnectHelperForURL(num_streams, url, session.get());
EXPECT_EQ(num_streams, ssl_conn_pool->last_num_streams());
}
}
TEST_P(HttpStreamFactoryTest, QuicDisablePreConnectIfZeroRtt) {
for (int num_streams = 1; num_streams < 3; ++num_streams) {
GURL url = GURL("https://www.google.com");
// Set up QUIC as alternative_service.
HttpServerPropertiesImpl http_server_properties;
const AlternativeService alternative_service(QUIC, "www.google.com", 443);
AlternativeServiceInfoVector alternative_service_info_vector;
base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
alternative_service_info_vector.push_back(
AlternativeServiceInfo(alternative_service, expiration));
HostPortPair host_port_pair(alternative_service.host_port_pair());
http_server_properties.SetAlternativeServices(
host_port_pair, alternative_service_info_vector);
SpdySessionDependencies session_deps(GetParam());
// Setup params to disable preconnect, but QUIC does 0RTT.
HttpNetworkSession::Params params =
SpdySessionDependencies::CreateSessionParams(&session_deps);
params.enable_quic = true;
params.quic_disable_preconnect_if_0rtt = true;
params.http_server_properties = http_server_properties.GetWeakPtr();
scoped_ptr<HttpNetworkSession> session(new HttpNetworkSession(params));
// Setup 0RTT for QUIC.
QuicStreamFactory* factory = session->quic_stream_factory();
factory->set_require_confirmation(false);
test::QuicStreamFactoryPeer::CacheDummyServerConfig(
factory, QuicServerId(host_port_pair, PRIVACY_MODE_DISABLED));
HttpNetworkSessionPeer peer(session.get());
CapturePreconnectsTransportSocketPool* transport_conn_pool =
new CapturePreconnectsTransportSocketPool(
session_deps.host_resolver.get(), session_deps.cert_verifier.get());
scoped_ptr<MockClientSocketPoolManager> mock_pool_manager(
new MockClientSocketPoolManager);
mock_pool_manager->SetTransportSocketPool(transport_conn_pool);
peer.SetClientSocketPoolManager(std::move(mock_pool_manager));
HttpRequestInfo request;
request.method = "GET";
request.url = url;
request.load_flags = 0;
SSLConfig ssl_config;
session->ssl_config_service()->GetSSLConfig(&ssl_config);
session->http_stream_factory()->PreconnectStreams(num_streams, request,
ssl_config, ssl_config);
EXPECT_EQ(-1, transport_conn_pool->last_num_streams());
}
}
namespace {
TEST_P(HttpStreamFactoryTest, PrivacyModeDisablesChannelId) {
SpdySessionDependencies session_deps(
GetParam(), ProxyService::CreateDirect());
StaticSocketDataProvider socket_data;
socket_data.set_connect_data(MockConnect(ASYNC, OK));
session_deps.socket_factory->AddSocketDataProvider(&socket_data);
SSLSocketDataProvider ssl(ASYNC, OK);
session_deps.socket_factory->AddSSLSocketDataProvider(&ssl);
scoped_ptr<HttpNetworkSession> session(
SpdySessionDependencies::SpdyCreateSession(&session_deps));
// Set an existing SpdySession in the pool.
HostPortPair host_port_pair("www.google.com", 443);
SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
PRIVACY_MODE_ENABLED);
HttpRequestInfo request_info;
request_info.method = "GET";
request_info.url = GURL("https://www.google.com");
request_info.load_flags = 0;
request_info.privacy_mode = PRIVACY_MODE_DISABLED;
SSLConfig ssl_config;
StreamRequestWaiter waiter;
scoped_ptr<HttpStreamRequest> request(
session->http_stream_factory()->RequestStream(
request_info, DEFAULT_PRIORITY, ssl_config, ssl_config,
&waiter, BoundNetLog()));
waiter.WaitForStream();
// The stream shouldn't come from spdy as we are using different privacy mode
EXPECT_FALSE(request->using_spdy());
SSLConfig used_ssl_config = waiter.used_ssl_config();
EXPECT_EQ(used_ssl_config.channel_id_enabled, ssl_config.channel_id_enabled);
}
namespace {
// Return count of distinct groups in given socket pool.
int GetSocketPoolGroupCount(ClientSocketPool* pool) {
int count = 0;
scoped_ptr<base::DictionaryValue> dict(pool->GetInfoAsValue("", "", false));
EXPECT_TRUE(dict != nullptr);
base::DictionaryValue* groups = nullptr;
if (dict->GetDictionary("groups", &groups) && (groups != nullptr)) {
count = static_cast<int>(groups->size());
}
return count;
}
// Return count of distinct spdy sessions.
int GetSpdySessionCount(HttpNetworkSession* session) {
scoped_ptr<base::Value> value(
session->spdy_session_pool()->SpdySessionPoolInfoToValue());
base::ListValue* session_list;
if (!value || !value->GetAsList(&session_list))
return -1;
return session_list->GetSize();
}
} // namespace
TEST_P(HttpStreamFactoryTest, PrivacyModeUsesDifferentSocketPoolGroup) {
SpdySessionDependencies session_deps(
GetParam(), ProxyService::CreateDirect());
StaticSocketDataProvider socket_data;
socket_data.set_connect_data(MockConnect(ASYNC, OK));
session_deps.socket_factory->AddSocketDataProvider(&socket_data);
SSLSocketDataProvider ssl(ASYNC, OK);
session_deps.socket_factory->AddSSLSocketDataProvider(&ssl);
scoped_ptr<HttpNetworkSession> session(
SpdySessionDependencies::SpdyCreateSession(&session_deps));
SSLClientSocketPool* ssl_pool = session->GetSSLSocketPool(
HttpNetworkSession::NORMAL_SOCKET_POOL);
EXPECT_EQ(GetSocketPoolGroupCount(ssl_pool), 0);
HttpRequestInfo request_info;
request_info.method = "GET";
request_info.url = GURL("https://www.google.com");
request_info.load_flags = 0;
request_info.privacy_mode = PRIVACY_MODE_DISABLED;
SSLConfig ssl_config;
StreamRequestWaiter waiter;
scoped_ptr<HttpStreamRequest> request1(
session->http_stream_factory()->RequestStream(
request_info, DEFAULT_PRIORITY, ssl_config, ssl_config,
&waiter, BoundNetLog()));
waiter.WaitForStream();
EXPECT_EQ(GetSocketPoolGroupCount(ssl_pool), 1);
scoped_ptr<HttpStreamRequest> request2(
session->http_stream_factory()->RequestStream(
request_info, DEFAULT_PRIORITY, ssl_config, ssl_config,
&waiter, BoundNetLog()));
waiter.WaitForStream();
EXPECT_EQ(GetSocketPoolGroupCount(ssl_pool), 1);
request_info.privacy_mode = PRIVACY_MODE_ENABLED;
scoped_ptr<HttpStreamRequest> request3(
session->http_stream_factory()->RequestStream(
request_info, DEFAULT_PRIORITY, ssl_config, ssl_config,
&waiter, BoundNetLog()));
waiter.WaitForStream();
EXPECT_EQ(GetSocketPoolGroupCount(ssl_pool), 2);
}
TEST_P(HttpStreamFactoryTest, GetLoadState) {
SpdySessionDependencies session_deps(
GetParam(), ProxyService::CreateDirect());
StaticSocketDataProvider socket_data;
socket_data.set_connect_data(MockConnect(ASYNC, OK));
session_deps.socket_factory->AddSocketDataProvider(&socket_data);
scoped_ptr<HttpNetworkSession> session(
SpdySessionDependencies::SpdyCreateSession(&session_deps));
HttpRequestInfo request_info;
request_info.method = "GET";
request_info.url = GURL("http://www.google.com");
SSLConfig ssl_config;
StreamRequestWaiter waiter;
scoped_ptr<HttpStreamRequest> request(
session->http_stream_factory()->RequestStream(
request_info, DEFAULT_PRIORITY, ssl_config, ssl_config,
&waiter, BoundNetLog()));
EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, request->GetLoadState());
waiter.WaitForStream();
}
TEST_P(HttpStreamFactoryTest, RequestHttpStream) {
SpdySessionDependencies session_deps(
GetParam(), ProxyService::CreateDirect());
StaticSocketDataProvider socket_data;
socket_data.set_connect_data(MockConnect(ASYNC, OK));
session_deps.socket_factory->AddSocketDataProvider(&socket_data);
scoped_ptr<HttpNetworkSession> session(
SpdySessionDependencies::SpdyCreateSession(&session_deps));
// Now request a stream. It should succeed using the second proxy in the
// list.
HttpRequestInfo request_info;
request_info.method = "GET";
request_info.url = GURL("http://www.google.com");
request_info.load_flags = 0;
SSLConfig ssl_config;
StreamRequestWaiter waiter;
scoped_ptr<HttpStreamRequest> request(
session->http_stream_factory()->RequestStream(
request_info,
DEFAULT_PRIORITY,
ssl_config,
ssl_config,
&waiter,
BoundNetLog()));
waiter.WaitForStream();
EXPECT_TRUE(waiter.stream_done());
ASSERT_TRUE(nullptr != waiter.stream());
EXPECT_TRUE(nullptr == waiter.websocket_stream());
EXPECT_EQ(0, GetSpdySessionCount(session.get()));
EXPECT_EQ(1, GetSocketPoolGroupCount(
session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(0, GetSocketPoolGroupCount(
session->GetTransportSocketPool(
HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
EXPECT_TRUE(waiter.used_proxy_info().is_direct());
}
TEST_P(HttpStreamFactoryTest, RequestHttpStreamOverSSL) {
SpdySessionDependencies session_deps(
GetParam(), ProxyService::CreateDirect());
MockRead mock_read(ASYNC, OK);
StaticSocketDataProvider socket_data(&mock_read, 1, nullptr, 0);
socket_data.set_connect_data(MockConnect(ASYNC, OK));
session_deps.socket_factory->AddSocketDataProvider(&socket_data);
SSLSocketDataProvider ssl_socket_data(ASYNC, OK);
session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data);
scoped_ptr<HttpNetworkSession> session(
SpdySessionDependencies::SpdyCreateSession(&session_deps));
// Now request a stream.
HttpRequestInfo request_info;
request_info.method = "GET";
request_info.url = GURL("https://www.google.com");
request_info.load_flags = 0;
SSLConfig ssl_config;
StreamRequestWaiter waiter;
scoped_ptr<HttpStreamRequest> request(
session->http_stream_factory()->RequestStream(
request_info,
DEFAULT_PRIORITY,
ssl_config,
ssl_config,
&waiter,
BoundNetLog()));
waiter.WaitForStream();
EXPECT_TRUE(waiter.stream_done());
ASSERT_TRUE(nullptr != waiter.stream());
EXPECT_TRUE(nullptr == waiter.websocket_stream());
EXPECT_EQ(0, GetSpdySessionCount(session.get()));
EXPECT_EQ(1, GetSocketPoolGroupCount(
session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(1, GetSocketPoolGroupCount(
session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(0, GetSocketPoolGroupCount(
session->GetTransportSocketPool(
HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
EXPECT_TRUE(waiter.used_proxy_info().is_direct());
}
TEST_P(HttpStreamFactoryTest, RequestHttpStreamOverProxy) {
SpdySessionDependencies session_deps(
GetParam(), ProxyService::CreateFixed("myproxy:8888"));
StaticSocketDataProvider socket_data;
socket_data.set_connect_data(MockConnect(ASYNC, OK));
session_deps.socket_factory->AddSocketDataProvider(&socket_data);
scoped_ptr<HttpNetworkSession> session(
SpdySessionDependencies::SpdyCreateSession(&session_deps));
// Now request a stream. It should succeed using the second proxy in the
// list.
HttpRequestInfo request_info;
request_info.method = "GET";
request_info.url = GURL("http://www.google.com");
request_info.load_flags = 0;
SSLConfig ssl_config;
StreamRequestWaiter waiter;
scoped_ptr<HttpStreamRequest> request(
session->http_stream_factory()->RequestStream(
request_info,
DEFAULT_PRIORITY,
ssl_config,
ssl_config,
&waiter,
BoundNetLog()));
waiter.WaitForStream();
EXPECT_TRUE(waiter.stream_done());
ASSERT_TRUE(nullptr != waiter.stream());
EXPECT_TRUE(nullptr == waiter.websocket_stream());
EXPECT_EQ(0, GetSpdySessionCount(session.get()));
EXPECT_EQ(0, GetSocketPoolGroupCount(
session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(0, GetSocketPoolGroupCount(
session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSocketPoolForHTTPProxy(
HttpNetworkSession::NORMAL_SOCKET_POOL,
HostPortPair("myproxy", 8888))));
EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPoolForSSLWithProxy(
HttpNetworkSession::NORMAL_SOCKET_POOL,
HostPortPair("myproxy", 8888))));
EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPoolForHTTPProxy(
HttpNetworkSession::WEBSOCKET_SOCKET_POOL,
HostPortPair("myproxy", 8888))));
EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPoolForSSLWithProxy(
HttpNetworkSession::WEBSOCKET_SOCKET_POOL,
HostPortPair("myproxy", 8888))));
EXPECT_FALSE(waiter.used_proxy_info().is_direct());
}
TEST_P(HttpStreamFactoryTest, RequestWebSocketBasicHandshakeStream) {
SpdySessionDependencies session_deps(
GetParam(), ProxyService::CreateDirect());
StaticSocketDataProvider socket_data;
socket_data.set_connect_data(MockConnect(ASYNC, OK));
session_deps.socket_factory->AddSocketDataProvider(&socket_data);
scoped_ptr<HttpNetworkSession> session(
SpdySessionDependencies::SpdyCreateSession(&session_deps));
// Now request a stream.
HttpRequestInfo request_info;
request_info.method = "GET";
request_info.url = GURL("ws://www.google.com");
request_info.load_flags = 0;
SSLConfig ssl_config;
StreamRequestWaiter waiter;
WebSocketStreamCreateHelper create_helper;
scoped_ptr<HttpStreamRequest> request(
session->http_stream_factory_for_websocket()
->RequestWebSocketHandshakeStream(request_info,
DEFAULT_PRIORITY,
ssl_config,
ssl_config,
&waiter,
&create_helper,
BoundNetLog()));
waiter.WaitForStream();
EXPECT_TRUE(waiter.stream_done());
EXPECT_TRUE(nullptr == waiter.stream());
ASSERT_TRUE(nullptr != waiter.websocket_stream());
EXPECT_EQ(MockWebSocketHandshakeStream::kStreamTypeBasic,
waiter.websocket_stream()->type());
EXPECT_EQ(0, GetSocketPoolGroupCount(
session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(0, GetSocketPoolGroupCount(
session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(0, GetSocketPoolGroupCount(
session->GetSSLSocketPool(HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
EXPECT_TRUE(waiter.used_proxy_info().is_direct());
}
TEST_P(HttpStreamFactoryTest, RequestWebSocketBasicHandshakeStreamOverSSL) {
SpdySessionDependencies session_deps(
GetParam(), ProxyService::CreateDirect());
MockRead mock_read(ASYNC, OK);
StaticSocketDataProvider socket_data(&mock_read, 1, nullptr, 0);
socket_data.set_connect_data(MockConnect(ASYNC, OK));
session_deps.socket_factory->AddSocketDataProvider(&socket_data);
SSLSocketDataProvider ssl_socket_data(ASYNC, OK);
session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data);
scoped_ptr<HttpNetworkSession> session(
SpdySessionDependencies::SpdyCreateSession(&session_deps));
// Now request a stream.
HttpRequestInfo request_info;
request_info.method = "GET";
request_info.url = GURL("wss://www.google.com");
request_info.load_flags = 0;
SSLConfig ssl_config;
StreamRequestWaiter waiter;
WebSocketStreamCreateHelper create_helper;
scoped_ptr<HttpStreamRequest> request(
session->http_stream_factory_for_websocket()
->RequestWebSocketHandshakeStream(request_info,
DEFAULT_PRIORITY,
ssl_config,
ssl_config,
&waiter,
&create_helper,
BoundNetLog()));
waiter.WaitForStream();
EXPECT_TRUE(waiter.stream_done());
EXPECT_TRUE(nullptr == waiter.stream());
ASSERT_TRUE(nullptr != waiter.websocket_stream());
EXPECT_EQ(MockWebSocketHandshakeStream::kStreamTypeBasic,
waiter.websocket_stream()->type());
EXPECT_EQ(0, GetSocketPoolGroupCount(
session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(0, GetSocketPoolGroupCount(
session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(1, GetSocketPoolGroupCount(
session->GetSSLSocketPool(HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
EXPECT_TRUE(waiter.used_proxy_info().is_direct());
}
TEST_P(HttpStreamFactoryTest, RequestWebSocketBasicHandshakeStreamOverProxy) {
SpdySessionDependencies session_deps(
GetParam(), ProxyService::CreateFixed("myproxy:8888"));
MockRead read(SYNCHRONOUS, "HTTP/1.0 200 Connection established\r\n\r\n");
StaticSocketDataProvider socket_data(&read, 1, 0, 0);
socket_data.set_connect_data(MockConnect(ASYNC, OK));
session_deps.socket_factory->AddSocketDataProvider(&socket_data);
scoped_ptr<HttpNetworkSession> session(
SpdySessionDependencies::SpdyCreateSession(&session_deps));
// Now request a stream.
HttpRequestInfo request_info;
request_info.method = "GET";
request_info.url = GURL("ws://www.google.com");
request_info.load_flags = 0;
SSLConfig ssl_config;
StreamRequestWaiter waiter;
WebSocketStreamCreateHelper create_helper;
scoped_ptr<HttpStreamRequest> request(
session->http_stream_factory_for_websocket()
->RequestWebSocketHandshakeStream(request_info,
DEFAULT_PRIORITY,
ssl_config,
ssl_config,
&waiter,
&create_helper,
BoundNetLog()));
waiter.WaitForStream();
EXPECT_TRUE(waiter.stream_done());
EXPECT_TRUE(nullptr == waiter.stream());
ASSERT_TRUE(nullptr != waiter.websocket_stream());
EXPECT_EQ(MockWebSocketHandshakeStream::kStreamTypeBasic,
waiter.websocket_stream()->type());
EXPECT_EQ(0, GetSocketPoolGroupCount(
session->GetTransportSocketPool(
HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
EXPECT_EQ(0, GetSocketPoolGroupCount(
session->GetSSLSocketPool(HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPoolForHTTPProxy(
HttpNetworkSession::NORMAL_SOCKET_POOL,
HostPortPair("myproxy", 8888))));
EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPoolForSSLWithProxy(
HttpNetworkSession::NORMAL_SOCKET_POOL,
HostPortPair("myproxy", 8888))));
EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSocketPoolForHTTPProxy(
HttpNetworkSession::WEBSOCKET_SOCKET_POOL,
HostPortPair("myproxy", 8888))));
EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPoolForSSLWithProxy(
HttpNetworkSession::WEBSOCKET_SOCKET_POOL,
HostPortPair("myproxy", 8888))));
EXPECT_FALSE(waiter.used_proxy_info().is_direct());
}
TEST_P(HttpStreamFactoryTest, RequestSpdyHttpStream) {
SpdySessionDependencies session_deps(GetParam(),
ProxyService::CreateDirect());
MockRead mock_read(SYNCHRONOUS, ERR_IO_PENDING);
SequencedSocketData socket_data(&mock_read, 1, nullptr, 0);
socket_data.set_connect_data(MockConnect(ASYNC, OK));
session_deps.socket_factory->AddSocketDataProvider(&socket_data);
SSLSocketDataProvider ssl_socket_data(ASYNC, OK);
ssl_socket_data.SetNextProto(GetParam());
session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data);
HostPortPair host_port_pair("www.google.com", 443);
scoped_ptr<HttpNetworkSession> session(
SpdySessionDependencies::SpdyCreateSession(&session_deps));
// Now request a stream.
HttpRequestInfo request_info;
request_info.method = "GET";
request_info.url = GURL("https://www.google.com");
request_info.load_flags = 0;
SSLConfig ssl_config;
StreamRequestWaiter waiter;
scoped_ptr<HttpStreamRequest> request(
session->http_stream_factory()->RequestStream(
request_info,
DEFAULT_PRIORITY,
ssl_config,
ssl_config,
&waiter,
BoundNetLog()));
waiter.WaitForStream();
EXPECT_TRUE(waiter.stream_done());
EXPECT_TRUE(nullptr == waiter.websocket_stream());
ASSERT_TRUE(nullptr != waiter.stream());
EXPECT_EQ(1, GetSpdySessionCount(session.get()));
EXPECT_EQ(1, GetSocketPoolGroupCount(
session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(1, GetSocketPoolGroupCount(
session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(0, GetSocketPoolGroupCount(
session->GetTransportSocketPool(
HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
EXPECT_EQ(0, GetSocketPoolGroupCount(
session->GetSSLSocketPool(HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
EXPECT_TRUE(waiter.used_proxy_info().is_direct());
}
TEST_P(HttpStreamFactoryTest, RequestBidirectionalStreamImpl) {
SpdySessionDependencies session_deps(GetParam(),
ProxyService::CreateDirect());
MockRead mock_read(ASYNC, OK);
SequencedSocketData socket_data(&mock_read, 1, nullptr, 0);
socket_data.set_connect_data(MockConnect(ASYNC, OK));
session_deps.socket_factory->AddSocketDataProvider(&socket_data);
SSLSocketDataProvider ssl_socket_data(ASYNC, OK);
ssl_socket_data.SetNextProto(GetParam());
session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data);
HostPortPair host_port_pair("www.google.com", 443);
scoped_ptr<HttpNetworkSession> session(
SpdySessionDependencies::SpdyCreateSession(&session_deps));
// Now request a stream.
HttpRequestInfo request_info;
request_info.method = "GET";
request_info.url = GURL("https://www.google.com");
request_info.load_flags = 0;
SSLConfig ssl_config;
StreamRequestWaiter waiter;
scoped_ptr<HttpStreamRequest> request(
session->http_stream_factory()->RequestBidirectionalStreamImpl(
request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter,
BoundNetLog()));
waiter.WaitForStream();
EXPECT_TRUE(waiter.stream_done());
EXPECT_FALSE(waiter.websocket_stream());
ASSERT_FALSE(waiter.stream());
ASSERT_TRUE(waiter.bidirectional_stream_impl());
EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSSLSocketPool(
HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
EXPECT_TRUE(waiter.used_proxy_info().is_direct());
ASSERT_EQ(0u,
static_cast<HttpStreamFactoryImpl*>(session->http_stream_factory())
->num_orphaned_jobs());
}
class HttpStreamFactoryBidirectionalQuicTest
: public ::testing::Test,
public ::testing::WithParamInterface<QuicVersion> {
protected:
HttpStreamFactoryBidirectionalQuicTest()
: clock_(new MockClock),
packet_maker_(GetParam(), 0, clock_, "www.example.org"),
random_generator_(0),
proxy_service_(ProxyService::CreateDirect()),
ssl_config_service_(new SSLConfigServiceDefaults) {
clock_->AdvanceTime(QuicTime::Delta::FromMilliseconds(20));
}
// Disable bidirectional stream over QUIC. This should be invoked before
// Initialize().
void DisableQuicBidirectionalStream() {
params_.quic_disable_bidirectional_streams = true;
}
void Initialize() {
params_.enable_quic = true;
params_.http_server_properties = http_server_properties_.GetWeakPtr();
params_.quic_host_whitelist.insert("www.example.org");
params_.quic_random = &random_generator_;
params_.quic_clock = clock_;
// Load a certificate that is valid for *.example.org
scoped_refptr<X509Certificate> test_cert(
ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
EXPECT_TRUE(test_cert.get());
verify_details_.cert_verify_result.verified_cert = test_cert;
verify_details_.cert_verify_result.is_issued_by_known_root = true;
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details_);
crypto_client_stream_factory_.set_handshake_mode(
MockCryptoClientStream::CONFIRM_HANDSHAKE);
params_.quic_crypto_client_stream_factory = &crypto_client_stream_factory_;
params_.quic_supported_versions = test::SupportedVersions(GetParam());
params_.transport_security_state = &transport_security_state_;
params_.host_resolver = &host_resolver_;
params_.proxy_service = proxy_service_.get();
params_.ssl_config_service = ssl_config_service_.get();
params_.client_socket_factory = &socket_factory_;
session_.reset(new HttpNetworkSession(params_));
session_->quic_stream_factory()->set_require_confirmation(false);
}
void AddQuicAlternativeService() {
const AlternativeService alternative_service(QUIC, "www.example.org", 443);
AlternativeServiceInfoVector alternative_service_info_vector;
base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
alternative_service_info_vector.push_back(
AlternativeServiceInfo(alternative_service, expiration));
HostPortPair host_port_pair(alternative_service.host_port_pair());
http_server_properties_.SetAlternativeServices(
host_port_pair, alternative_service_info_vector);
};
test::QuicTestPacketMaker& packet_maker() { return packet_maker_; }
MockClientSocketFactory& socket_factory() { return socket_factory_; }
HttpNetworkSession* session() { return session_.get(); }
private:
MockClock* clock_; // Owned by QuicStreamFactory
test::QuicTestPacketMaker packet_maker_;
MockClientSocketFactory socket_factory_;
scoped_ptr<HttpNetworkSession> session_;
test::MockRandom random_generator_;
ProofVerifyDetailsChromium verify_details_;
MockCryptoClientStreamFactory crypto_client_stream_factory_;
HttpServerPropertiesImpl http_server_properties_;
TransportSecurityState transport_security_state_;
MockHostResolver host_resolver_;
scoped_ptr<ProxyService> proxy_service_;
scoped_refptr<SSLConfigServiceDefaults> ssl_config_service_;
HttpNetworkSession::Params params_;
};
INSTANTIATE_TEST_CASE_P(Version,
HttpStreamFactoryBidirectionalQuicTest,
::testing::ValuesIn(QuicSupportedVersions()));
TEST_P(HttpStreamFactoryBidirectionalQuicTest,
RequestBidirectionalStreamImplQuicAlternative) {
GURL url = GURL("https://www.example.org");
MockQuicData mock_quic_data;
SpdyPriority priority =
ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY);
size_t spdy_headers_frame_length;
mock_quic_data.AddWrite(packet_maker().MakeRequestHeadersPacket(
1, test::kClientDataStreamId1, /*should_include_version=*/true,
/*fin=*/true, priority,
packet_maker().GetRequestHeaders("GET", "https", "/"),
&spdy_headers_frame_length));
size_t spdy_response_headers_frame_length;
mock_quic_data.AddRead(packet_maker().MakeResponseHeadersPacket(
1, test::kClientDataStreamId1, /*should_include_version=*/false,
/*fin=*/true, packet_maker().GetResponseHeaders("200"),
&spdy_response_headers_frame_length));
mock_quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // No more read data.
mock_quic_data.AddSocketDataToFactory(&socket_factory());
// Add hanging data for http job.
scoped_ptr<StaticSocketDataProvider> hanging_data;
hanging_data.reset(new StaticSocketDataProvider());
MockConnect hanging_connect(SYNCHRONOUS, ERR_IO_PENDING);
hanging_data->set_connect_data(hanging_connect);
socket_factory().AddSocketDataProvider(hanging_data.get());
SSLSocketDataProvider ssl_data(ASYNC, OK);
socket_factory().AddSSLSocketDataProvider(&ssl_data);
// Set up QUIC as alternative_service.
AddQuicAlternativeService();
Initialize();
// Now request a stream.
SSLConfig ssl_config;
HttpRequestInfo request_info;
request_info.method = "GET";
request_info.url = GURL("https://www.example.org");
request_info.load_flags = 0;
StreamRequestWaiter waiter;
scoped_ptr<HttpStreamRequest> request(
session()->http_stream_factory()->RequestBidirectionalStreamImpl(
request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter,
BoundNetLog()));
waiter.WaitForStream();
EXPECT_TRUE(waiter.stream_done());
EXPECT_FALSE(waiter.websocket_stream());
ASSERT_FALSE(waiter.stream());
ASSERT_TRUE(waiter.bidirectional_stream_impl());
BidirectionalStreamImpl* stream_impl = waiter.bidirectional_stream_impl();
BidirectionalStreamRequestInfo bidi_request_info;
bidi_request_info.method = "GET";
bidi_request_info.url = GURL("https://www.example.org/");
bidi_request_info.end_stream_on_headers = true;
bidi_request_info.priority = LOWEST;
TestBidirectionalDelegate delegate;
stream_impl->Start(&bidi_request_info, BoundNetLog(), &delegate, nullptr);
delegate.WaitUntilDone();
scoped_refptr<IOBuffer> buffer = new net::IOBuffer(1);
EXPECT_EQ(OK, stream_impl->ReadData(buffer.get(), 1));
EXPECT_EQ(kProtoQUIC1SPDY3, stream_impl->GetProtocol());
EXPECT_EQ("200", delegate.response_headers().find(":status")->second);
EXPECT_EQ(1, GetSocketPoolGroupCount(session()->GetTransportSocketPool(
HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(1, GetSocketPoolGroupCount(session()->GetSSLSocketPool(
HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(0, GetSocketPoolGroupCount(session()->GetTransportSocketPool(
HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
EXPECT_EQ(0, GetSocketPoolGroupCount(session()->GetSSLSocketPool(
HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
EXPECT_TRUE(waiter.used_proxy_info().is_direct());
}
// Tests that when QUIC is not enabled for bidirectional streaming, HTTP/2 is
// used instead.
TEST_P(HttpStreamFactoryBidirectionalQuicTest,
RequestBidirectionalStreamImplQuicNotEnabled) {
GURL url = GURL("https://www.example.org");
// Make the http job fail.
scoped_ptr<StaticSocketDataProvider> http_job_data;
http_job_data.reset(new StaticSocketDataProvider());
MockConnect failed_connect(ASYNC, ERR_CONNECTION_REFUSED);
http_job_data->set_connect_data(failed_connect);
socket_factory().AddSocketDataProvider(http_job_data.get());
SSLSocketDataProvider ssl_data(ASYNC, OK);
socket_factory().AddSSLSocketDataProvider(&ssl_data);
// Set up QUIC as alternative_service.
AddQuicAlternativeService();
DisableQuicBidirectionalStream();
Initialize();
// Now request a stream.
SSLConfig ssl_config;
HttpRequestInfo request_info;
request_info.method = "GET";
request_info.url = GURL("https://www.example.org");
request_info.load_flags = 0;
StreamRequestWaiter waiter;
scoped_ptr<HttpStreamRequest> request(
session()->http_stream_factory()->RequestBidirectionalStreamImpl(
request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter,
BoundNetLog()));
waiter.WaitForStream();
EXPECT_TRUE(waiter.stream_done());
EXPECT_FALSE(waiter.websocket_stream());
ASSERT_FALSE(waiter.stream());
ASSERT_FALSE(waiter.bidirectional_stream_impl());
// Since the alternative service job is not started, we will get the error
// from the http job.
ASSERT_EQ(ERR_CONNECTION_REFUSED, waiter.error_status());
}
// Tests that if Http job fails, but Quic job succeeds, we return
// BidirectionalStreamQuicImpl.
TEST_P(HttpStreamFactoryBidirectionalQuicTest,
RequestBidirectionalStreamImplHttpJobFailsQuicJobSucceeds) {
GURL url = GURL("https://www.example.org");
// Set up Quic data.
MockQuicData mock_quic_data;
SpdyPriority priority =
ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY);
size_t spdy_headers_frame_length;
mock_quic_data.AddWrite(packet_maker().MakeRequestHeadersPacket(
1, test::kClientDataStreamId1, /*should_include_version=*/true,
/*fin=*/true, priority,
packet_maker().GetRequestHeaders("GET", "https", "/"),
&spdy_headers_frame_length));
size_t spdy_response_headers_frame_length;
mock_quic_data.AddRead(packet_maker().MakeResponseHeadersPacket(
1, test::kClientDataStreamId1, /*should_include_version=*/false,
/*fin=*/true, packet_maker().GetResponseHeaders("200"),
&spdy_response_headers_frame_length));
mock_quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // No more read data.
mock_quic_data.AddSocketDataToFactory(&socket_factory());
// Make the http job fail.
scoped_ptr<StaticSocketDataProvider> http_job_data;
http_job_data.reset(new StaticSocketDataProvider());
MockConnect failed_connect(ASYNC, ERR_CONNECTION_REFUSED);
http_job_data->set_connect_data(failed_connect);
socket_factory().AddSocketDataProvider(http_job_data.get());
SSLSocketDataProvider ssl_data(ASYNC, OK);
socket_factory().AddSSLSocketDataProvider(&ssl_data);
// Set up QUIC as alternative_service.
AddQuicAlternativeService();
Initialize();
// Now request a stream.
SSLConfig ssl_config;
HttpRequestInfo request_info;
request_info.method = "GET";
request_info.url = GURL("https://www.example.org");
request_info.load_flags = 0;
StreamRequestWaiter waiter;
scoped_ptr<HttpStreamRequest> request(
session()->http_stream_factory()->RequestBidirectionalStreamImpl(
request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter,
BoundNetLog()));
waiter.WaitForStream();
EXPECT_TRUE(waiter.stream_done());
EXPECT_FALSE(waiter.websocket_stream());
ASSERT_FALSE(waiter.stream());
ASSERT_TRUE(waiter.bidirectional_stream_impl());
BidirectionalStreamImpl* stream_impl = waiter.bidirectional_stream_impl();
BidirectionalStreamRequestInfo bidi_request_info;
bidi_request_info.method = "GET";
bidi_request_info.url = GURL("https://www.example.org/");
bidi_request_info.end_stream_on_headers = true;
bidi_request_info.priority = LOWEST;
TestBidirectionalDelegate delegate;
stream_impl->Start(&bidi_request_info, BoundNetLog(), &delegate, nullptr);
delegate.WaitUntilDone();
// Make sure the BidirectionalStream negotiated goes through QUIC.
scoped_refptr<IOBuffer> buffer = new net::IOBuffer(1);
EXPECT_EQ(OK, stream_impl->ReadData(buffer.get(), 1));
EXPECT_EQ(kProtoQUIC1SPDY3, stream_impl->GetProtocol());
EXPECT_EQ("200", delegate.response_headers().find(":status")->second);
// There is no Http2 socket pool.
EXPECT_EQ(0, GetSocketPoolGroupCount(session()->GetTransportSocketPool(
HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(0, GetSocketPoolGroupCount(session()->GetSSLSocketPool(
HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(0, GetSocketPoolGroupCount(session()->GetTransportSocketPool(
HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
EXPECT_EQ(0, GetSocketPoolGroupCount(session()->GetSSLSocketPool(
HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
EXPECT_TRUE(waiter.used_proxy_info().is_direct());
}
TEST_P(HttpStreamFactoryTest, RequestBidirectionalStreamImplFailure) {
SpdySessionDependencies session_deps(GetParam(),
ProxyService::CreateDirect());
MockRead mock_read(ASYNC, OK);
SequencedSocketData socket_data(&mock_read, 1, nullptr, 0);
socket_data.set_connect_data(MockConnect(ASYNC, OK));
session_deps.socket_factory->AddSocketDataProvider(&socket_data);
SSLSocketDataProvider ssl_socket_data(ASYNC, OK);
// If HTTP/1 is used, BidirectionalStreamImpl should not be obtained.
ssl_socket_data.SetNextProto(kProtoHTTP11);
session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data);
HostPortPair host_port_pair("www.google.com", 443);
scoped_ptr<HttpNetworkSession> session(
SpdySessionDependencies::SpdyCreateSession(&session_deps));
// Now request a stream.
HttpRequestInfo request_info;
request_info.method = "GET";
request_info.url = GURL("https://www.google.com");
request_info.load_flags = 0;
SSLConfig ssl_config;
StreamRequestWaiter waiter;
scoped_ptr<HttpStreamRequest> request(
session->http_stream_factory()->RequestBidirectionalStreamImpl(
request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter,
BoundNetLog()));
waiter.WaitForStream();
EXPECT_TRUE(waiter.stream_done());
ASSERT_EQ(ERR_FAILED, waiter.error_status());
EXPECT_FALSE(waiter.websocket_stream());
ASSERT_FALSE(waiter.stream());
ASSERT_FALSE(waiter.bidirectional_stream_impl());
EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSSLSocketPool(
HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
ASSERT_EQ(0u,
static_cast<HttpStreamFactoryImpl*>(session->http_stream_factory())
->num_orphaned_jobs());
}
// TODO(ricea): This test can be removed once the new WebSocket stack supports
// SPDY. Currently, even if we connect to a SPDY-supporting server, we need to
// use plain SSL.
TEST_P(HttpStreamFactoryTest, RequestWebSocketSpdyHandshakeStreamButGetSSL) {
SpdySessionDependencies session_deps(GetParam(),
ProxyService::CreateDirect());
MockRead mock_read(SYNCHRONOUS, ERR_IO_PENDING);
StaticSocketDataProvider socket_data(&mock_read, 1, nullptr, 0);
socket_data.set_connect_data(MockConnect(ASYNC, OK));
session_deps.socket_factory->AddSocketDataProvider(&socket_data);
SSLSocketDataProvider ssl_socket_data(ASYNC, OK);
session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data);
HostPortPair host_port_pair("www.google.com", 80);
scoped_ptr<HttpNetworkSession> session(
SpdySessionDependencies::SpdyCreateSession(&session_deps));
// Now request a stream.
HttpRequestInfo request_info;
request_info.method = "GET";
request_info.url = GURL("wss://www.google.com");
request_info.load_flags = 0;
SSLConfig ssl_config;
StreamRequestWaiter waiter1;
WebSocketStreamCreateHelper create_helper;
scoped_ptr<HttpStreamRequest> request1(
session->http_stream_factory_for_websocket()
->RequestWebSocketHandshakeStream(request_info,
DEFAULT_PRIORITY,
ssl_config,
ssl_config,
&waiter1,
&create_helper,
BoundNetLog()));
waiter1.WaitForStream();
EXPECT_TRUE(waiter1.stream_done());
ASSERT_TRUE(nullptr != waiter1.websocket_stream());
EXPECT_EQ(MockWebSocketHandshakeStream::kStreamTypeBasic,
waiter1.websocket_stream()->type());
EXPECT_TRUE(nullptr == waiter1.stream());
EXPECT_EQ(0, GetSocketPoolGroupCount(
session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(0, GetSocketPoolGroupCount(
session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(1, GetSocketPoolGroupCount(
session->GetSSLSocketPool(HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
EXPECT_TRUE(waiter1.used_proxy_info().is_direct());
}
// TODO(ricea): Re-enable once WebSocket-over-SPDY is implemented.
TEST_P(HttpStreamFactoryTest, DISABLED_RequestWebSocketSpdyHandshakeStream) {
SpdySessionDependencies session_deps(GetParam(),
ProxyService::CreateDirect());
MockRead mock_read(SYNCHRONOUS, ERR_IO_PENDING);
StaticSocketDataProvider socket_data(&mock_read, 1, nullptr, 0);
socket_data.set_connect_data(MockConnect(ASYNC, OK));
session_deps.socket_factory->AddSocketDataProvider(&socket_data);
SSLSocketDataProvider ssl_socket_data(ASYNC, OK);
ssl_socket_data.SetNextProto(GetParam());
session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data);
HostPortPair host_port_pair("www.google.com", 80);
scoped_ptr<HttpNetworkSession> session(
SpdySessionDependencies::SpdyCreateSession(&session_deps));
// Now request a stream.
HttpRequestInfo request_info;
request_info.method = "GET";
request_info.url = GURL("wss://www.google.com");
request_info.load_flags = 0;
SSLConfig ssl_config;
StreamRequestWaiter waiter1;
WebSocketStreamCreateHelper create_helper;
scoped_ptr<HttpStreamRequest> request1(
session->http_stream_factory_for_websocket()
->RequestWebSocketHandshakeStream(request_info,
DEFAULT_PRIORITY,
ssl_config,
ssl_config,
&waiter1,
&create_helper,
BoundNetLog()));
waiter1.WaitForStream();
EXPECT_TRUE(waiter1.stream_done());
ASSERT_TRUE(nullptr != waiter1.websocket_stream());
EXPECT_EQ(MockWebSocketHandshakeStream::kStreamTypeSpdy,
waiter1.websocket_stream()->type());
EXPECT_TRUE(nullptr == waiter1.stream());
StreamRequestWaiter waiter2;
scoped_ptr<HttpStreamRequest> request2(
session->http_stream_factory_for_websocket()
->RequestWebSocketHandshakeStream(request_info,
DEFAULT_PRIORITY,
ssl_config,
ssl_config,
&waiter2,
&create_helper,
BoundNetLog()));
waiter2.WaitForStream();
EXPECT_TRUE(waiter2.stream_done());
ASSERT_TRUE(nullptr != waiter2.websocket_stream());
EXPECT_EQ(MockWebSocketHandshakeStream::kStreamTypeSpdy,
waiter2.websocket_stream()->type());
EXPECT_TRUE(nullptr == waiter2.stream());
EXPECT_NE(waiter2.websocket_stream(), waiter1.websocket_stream());
EXPECT_EQ(static_cast<WebSocketSpdyHandshakeStream*>(
waiter2.websocket_stream())->spdy_session(),
static_cast<WebSocketSpdyHandshakeStream*>(
waiter1.websocket_stream())->spdy_session());
EXPECT_EQ(0, GetSocketPoolGroupCount(
session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(0, GetSocketPoolGroupCount(
session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(1, GetSocketPoolGroupCount(
session->GetTransportSocketPool(
HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
EXPECT_EQ(1, GetSocketPoolGroupCount(
session->GetSSLSocketPool(HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
EXPECT_TRUE(waiter1.used_proxy_info().is_direct());
}
// TODO(ricea): Re-enable once WebSocket over SPDY is implemented.
TEST_P(HttpStreamFactoryTest, DISABLED_OrphanedWebSocketStream) {
SpdySessionDependencies session_deps(GetParam(),
ProxyService::CreateDirect());
session_deps.parse_alternative_services = true;
session_deps.enable_alternative_service_with_different_host = false;
MockRead mock_read(ASYNC, OK);
SequencedSocketData socket_data(&mock_read, 1, nullptr, 0);
socket_data.set_connect_data(MockConnect(ASYNC, OK));
session_deps.socket_factory->AddSocketDataProvider(&socket_data);
MockRead mock_read2(ASYNC, OK);
SequencedSocketData socket_data2(&mock_read2, 1, nullptr, 0);
socket_data2.set_connect_data(MockConnect(ASYNC, ERR_IO_PENDING));
session_deps.socket_factory->AddSocketDataProvider(&socket_data2);
SSLSocketDataProvider ssl_socket_data(ASYNC, OK);
ssl_socket_data.SetNextProto(GetParam());
session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data);
scoped_ptr<HttpNetworkSession> session(
SpdySessionDependencies::SpdyCreateSession(&session_deps));
// Now request a stream.
HttpRequestInfo request_info;
request_info.method = "GET";
request_info.url = GURL("ws://www.google.com:8888");
request_info.load_flags = 0;
base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
session->http_server_properties()->SetAlternativeService(
HostPortPair("www.google.com", 8888),
AlternativeService(NPN_HTTP_2, "www.google.com", 9999), expiration);
SSLConfig ssl_config;
StreamRequestWaiter waiter;
WebSocketStreamCreateHelper create_helper;
scoped_ptr<HttpStreamRequest> request(
session->http_stream_factory_for_websocket()
->RequestWebSocketHandshakeStream(request_info,
DEFAULT_PRIORITY,
ssl_config,
ssl_config,
&waiter,
&create_helper,
BoundNetLog()));
waiter.WaitForStream();
EXPECT_TRUE(waiter.stream_done());
EXPECT_TRUE(nullptr == waiter.stream());
ASSERT_TRUE(nullptr != waiter.websocket_stream());
EXPECT_EQ(MockWebSocketHandshakeStream::kStreamTypeSpdy,
waiter.websocket_stream()->type());
// Make sure that there was an alternative connection
// which consumes extra connections.
EXPECT_EQ(0, GetSocketPoolGroupCount(
session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(0, GetSocketPoolGroupCount(
session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)));
EXPECT_EQ(2, GetSocketPoolGroupCount(
session->GetTransportSocketPool(
HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
EXPECT_EQ(1, GetSocketPoolGroupCount(
session->GetSSLSocketPool(HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
EXPECT_TRUE(waiter.used_proxy_info().is_direct());
// Make sure there is no orphaned job. it is already canceled.
ASSERT_EQ(0u, static_cast<HttpStreamFactoryImpl*>(
session->http_stream_factory_for_websocket())->num_orphaned_jobs());
}
} // namespace
} // namespace net