diff options
author | willchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-26 22:20:54 +0000 |
---|---|---|
committer | willchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-26 22:20:54 +0000 |
commit | 2ff8b3133033d6130ea87df3ab2290a226c7bca7 (patch) | |
tree | 74ee45d17428c359a0bd8db8b97e51eeb4b7c44c /net | |
parent | 106113b3dde22f7d8b3a7f9a00a8884668cda782 (diff) | |
download | chromium_src-2ff8b3133033d6130ea87df3ab2290a226c7bca7.zip chromium_src-2ff8b3133033d6130ea87df3ab2290a226c7bca7.tar.gz chromium_src-2ff8b3133033d6130ea87df3ab2290a226c7bca7.tar.bz2 |
SPDY: Fix Alternate-Protocol.
(1) In DoInitConnection() we do the existing spdy session check. If it exists there, then we assuem it exists in DoSpdySendRequest(). Unfortunately, we didn't do the same check. Use a member variable to store the HostPortPair.
(2) In DoInitConnection(), we used the scheme://urlhost:urlport as the connection group. With Alternate-Protocol, we used the scheme://urlhost:urlport even though we were connecting to a different port, with a different protocol (TLS). This means we would mix conflicting sockets in the ClientSocketPool. I fix this by dropping scheme://, since it's unnecessary, and would cause us not to share SSL sockets in different connection groups (since the specified scheme might be http://, but due to Alternate-Protocol, we actually do an SSL connect). I also don't use the urlhost:urlport, but use the host:port that we actually connect to.
TODO(willchan):
Fix Alternate-Protocol so it works properly with proxies. I need to change CONNECT for http proxies and patch the SOCKs connects.
Review URL: http://codereview.chromium.org/1755005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@45627 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/base/host_port_pair.cc | 9 | ||||
-rw-r--r-- | net/base/host_port_pair.h | 10 | ||||
-rw-r--r-- | net/http/http_network_transaction.cc | 50 | ||||
-rw-r--r-- | net/http/http_network_transaction.h | 6 | ||||
-rw-r--r-- | net/http/http_network_transaction_unittest.cc | 231 | ||||
-rw-r--r-- | net/net.gyp | 1 | ||||
-rw-r--r-- | net/socket/socket_test_util.cc | 59 | ||||
-rw-r--r-- | net/socket/socket_test_util.h | 51 | ||||
-rw-r--r-- | net/spdy/spdy_framer.h | 3 | ||||
-rw-r--r-- | net/spdy/spdy_network_transaction_unittest.cc | 172 | ||||
-rw-r--r-- | net/spdy/spdy_test_util.h | 115 |
11 files changed, 495 insertions, 212 deletions
diff --git a/net/base/host_port_pair.cc b/net/base/host_port_pair.cc index 06a95fb..d4e7d4e 100644 --- a/net/base/host_port_pair.cc +++ b/net/base/host_port_pair.cc @@ -7,8 +7,15 @@ namespace net { +HostPortPair::HostPortPair() : port(0) {} +HostPortPair::HostPortPair(const std::string& in_host, uint16 in_port) + : host(in_host), port(in_port) {} + std::string HostPortPair::ToString() const { - return StringPrintf("[Host: %s, Port: %u]", host.c_str(), port); + // Check to see if the host is an IPv6 address. If so, added brackets. + if (host.find(':') != std::string::npos) + return StringPrintf("[%s]:%u", host.c_str(), port); + return StringPrintf("%s:%u", host.c_str(), port); } } // namespace net diff --git a/net/base/host_port_pair.h b/net/base/host_port_pair.h index ab7f312..547e898 100644 --- a/net/base/host_port_pair.h +++ b/net/base/host_port_pair.h @@ -11,9 +11,9 @@ namespace net { struct HostPortPair { - HostPortPair() {} - HostPortPair(const std::string& in_host, uint16 in_port) - : host(in_host), port(in_port) {} + HostPortPair(); + // If |in_host| represents an IPv6 address, it should not bracket the address. + HostPortPair(const std::string& in_host, uint16 in_port); // Comparator function so this can be placed in a std::map. bool operator<(const HostPortPair& other) const { @@ -22,8 +22,12 @@ struct HostPortPair { return port < other.port; } + // ToString() will convert the HostPortPair to "host:port". If |host| is an + // IPv6 literal, it will add brackets around |host|. std::string ToString() const; + // If |host| represents an IPv6 address, this string will not contain brackets + // around the address. std::string host; uint16 port; }; diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index f3cd4d5..544c033 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -710,25 +710,28 @@ int HttpNetworkTransaction::DoInitConnection() { // Build the string used to uniquely identify connections of this type. // Determine the host and port to connect to. std::string connection_group; - std::string host; - int port; + + // |endpoint| indicates the final destination endpoint. + HostPortPair endpoint; + endpoint.host = request_->url.HostNoBrackets(); + endpoint.port = request_->url.EffectiveIntPort(); + if (proxy_mode_ != kDirectConnection) { ProxyServer proxy_server = proxy_info_.proxy_server(); connection_group = "proxy/" + proxy_server.ToURI() + "/"; - host = proxy_server.HostNoBrackets(); - port = proxy_server.port(); + peer_.host = proxy_server.HostNoBrackets(); + peer_.port = proxy_server.port(); } else { - host = request_->url.HostNoBrackets(); - port = request_->url.EffectiveIntPort(); + peer_ = endpoint; if (alternate_protocol_mode_ == kUnspecified) { const HttpAlternateProtocols& alternate_protocols = session_->alternate_protocols(); - if (alternate_protocols.HasAlternateProtocolFor(host, port)) { + if (alternate_protocols.HasAlternateProtocolFor(peer_)) { HttpAlternateProtocols::PortProtocolPair alternate = - alternate_protocols.GetAlternateProtocolFor(host, port); + alternate_protocols.GetAlternateProtocolFor(peer_); if (alternate.protocol != HttpAlternateProtocols::BROKEN) { DCHECK_EQ(HttpAlternateProtocols::NPN_SPDY_1, alternate.protocol); - port = alternate.port; + peer_.port = alternate.port; using_ssl_ = true; alternate_protocol_ = HttpAlternateProtocols::NPN_SPDY_1; alternate_protocol_mode_ = kUsingAlternateProtocol; @@ -740,25 +743,27 @@ int HttpNetworkTransaction::DoInitConnection() { // Use the fixed testing ports if they've been provided. if (using_ssl_) { if (session_->fixed_https_port() != 0) - port = session_->fixed_https_port(); + peer_.port = session_->fixed_https_port(); } else if (session_->fixed_http_port() != 0) { - port = session_->fixed_http_port(); + peer_.port = session_->fixed_http_port(); } // Check first if we have a spdy session for this group. If so, then go // straight to using that. - HostPortPair host_port_pair(host, port); - if (session_->spdy_session_pool()->HasSession(host_port_pair)) { + if (session_->spdy_session_pool()->HasSession(peer_)) { using_spdy_ = true; return OK; } // For a connection via HTTP proxy not using CONNECT, the connection // is to the proxy server only. For all other cases - // (direct, HTTP proxy CONNECT, SOCKS), the connection is upto the + // (direct, HTTP proxy CONNECT, SOCKS), the connection is up to the // url endpoint. Hence we append the url data into the connection_group. - if (proxy_mode_ != kHTTPProxy) - connection_group.append(request_->url.GetOrigin().spec()); + // Note that the url endpoint may be different in the Alternate-Protocol case. + if (proxy_mode_ == kDirectConnection) + connection_group = peer_.ToString(); + else if (proxy_mode_ != kHTTPProxy) + connection_group.append(endpoint.ToString()); DCHECK(!connection_group.empty()); @@ -766,8 +771,8 @@ int HttpNetworkTransaction::DoInitConnection() { bool disable_resolver_cache = request_->load_flags & LOAD_BYPASS_CACHE || request_->load_flags & LOAD_DISABLE_CACHE; - TCPSocketParams tcp_params(host, port, request_->priority, request_->referrer, - disable_resolver_cache); + TCPSocketParams tcp_params(peer_.host, peer_.port, request_->priority, + request_->referrer, disable_resolver_cache); int rv; if (proxy_mode_ != kSOCKSProxy) { @@ -1250,20 +1255,19 @@ int HttpNetworkTransaction::DoSpdySendRequest() { // if one already exists, then screw it, use the existing one! Otherwise, // use the existing TCP socket. - HostPortPair host_port_pair(request_->url.HostNoBrackets(), - request_->url.EffectiveIntPort()); const scoped_refptr<SpdySessionPool> spdy_pool = session_->spdy_session_pool(); scoped_refptr<SpdySession> spdy_session; - if (spdy_pool->HasSession(host_port_pair)) { - spdy_session = spdy_pool->Get(host_port_pair, session_); + if (spdy_pool->HasSession(peer_)) { + spdy_session = spdy_pool->Get(peer_, session_); } else { // SPDY is negotiated using the TLS next protocol negotiation (NPN) // extension, so |connection_| must contain an SSLClientSocket. DCHECK(using_ssl_); + CHECK(connection_->socket()); spdy_session = spdy_pool->GetSpdySessionFromSSLSocket( - host_port_pair, session_, connection_.release()); + peer_, session_, connection_.release()); } CHECK(spdy_session.get()); diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h index 3705cf8..6a52f43 100644 --- a/net/http/http_network_transaction.h +++ b/net/http/http_network_transaction.h @@ -369,6 +369,12 @@ class HttpNetworkTransaction : public HttpTransaction { // The next state in the state machine. State next_state_; + + // The hostname and port of the peer. This is not necessarily the one + // specified by the URL, due to Alternate-Protocol or proxies. + HostPortPair peer_; + + DISALLOW_COPY_AND_ASSIGN(HttpNetworkTransaction); }; } // namespace net diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index 8c0d8bd..4742122 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc @@ -16,7 +16,6 @@ #include "net/base/ssl_info.h" #include "net/base/test_completion_callback.h" #include "net/base/upload_data.h" -#include "net/spdy/spdy_session_pool.h" #include "net/http/http_auth_handler_ntlm.h" #include "net/http/http_basic_stream.h" #include "net/http/http_network_session.h" @@ -27,6 +26,10 @@ #include "net/socket/client_socket_factory.h" #include "net/socket/socket_test_util.h" #include "net/socket/ssl_client_socket.h" +#include "net/spdy/spdy_framer.h" +#include "net/spdy/spdy_session.h" +#include "net/spdy/spdy_session_pool.h" +#include "net/spdy/spdy_test_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" @@ -80,7 +83,12 @@ HttpNetworkSession* CreateSession(SessionDependencies* session_deps) { class HttpNetworkTransactionTest : public PlatformTest { public: + virtual void SetUp() { + spdy::SpdyFramer::set_enable_compression_default(false); + } + virtual void TearDown() { + spdy::SpdyFramer::set_enable_compression_default(true); // Empty the current queue. MessageLoop::current()->RunAllPending(); PlatformTest::TearDown(); @@ -3674,7 +3682,12 @@ TEST_F(HttpNetworkTransactionTest, GroupNameForProxyConnections) { { "", // no proxy (direct) "http://www.google.com/direct", - "http://www.google.com/", + "www.google.com:80", + }, + { + "", // no proxy (direct) + "http://[2001:1418:13:1::25]/direct", + "[2001:1418:13:1::25]:80", }, { "http_proxy", @@ -3684,33 +3697,58 @@ TEST_F(HttpNetworkTransactionTest, GroupNameForProxyConnections) { { "socks4://socks_proxy:1080", "http://www.google.com/socks4_direct", - "proxy/socks4://socks_proxy:1080/http://www.google.com/", + "proxy/socks4://socks_proxy:1080/www.google.com:80", }, // SSL Tests { "", "https://www.google.com/direct_ssl", - "https://www.google.com/", + "www.google.com:443", }, { "http_proxy", "https://www.google.com/http_connect_ssl", - "proxy/http_proxy:80/https://www.google.com/", + "proxy/http_proxy:80/www.google.com:443", }, { "socks4://socks_proxy:1080", "https://www.google.com/socks4_ssl", - "proxy/socks4://socks_proxy:1080/https://www.google.com/", + "proxy/socks4://socks_proxy:1080/www.google.com:443", }, + { + "", // no proxy (direct) + "http://host.with.alternate/direct", + "host.with.alternate:443", + }, + + // TODO(willchan): Uncomment these tests when they work. +// { +// "http_proxy", +// "http://host.with.alternate/direct", +// "proxy/http_proxy:80/host.with.alternate:443", +// }, +// { +// "socks4://socks_proxy:1080", +// "http://host.with.alternate/direct", +// "proxy/socks4://socks_proxy:1080/host.with.alternate:443", +// }, }; + HttpNetworkTransaction::SetUseAlternateProtocols(true); + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { SessionDependencies session_deps( CreateFixedProxyService(tests[i].proxy_server)); scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + HttpAlternateProtocols* alternate_protocols = + session->mutable_alternate_protocols(); + alternate_protocols->SetAlternateProtocolFor( + HostPortPair("host.with.alternate", 80), 443, + HttpAlternateProtocols::NPN_SPDY_1); + scoped_refptr<CaptureGroupNameTCPSocketPool> tcp_conn_pool( new CaptureGroupNameTCPSocketPool(session.get(), session->socket_factory())); @@ -3735,6 +3773,8 @@ TEST_F(HttpNetworkTransactionTest, GroupNameForProxyConnections) { socks_conn_pool->last_group_name_received(); EXPECT_EQ(tests[i].expected_group_name, allgroups); } + + HttpNetworkTransaction::SetUseAlternateProtocols(false); } TEST_F(HttpNetworkTransactionTest, ReconsiderProxyAfterFailedConnection) { @@ -4641,6 +4681,8 @@ TEST_F(HttpNetworkTransactionTest, MarkBrokenAlternateProtocol) { TEST_F(HttpNetworkTransactionTest, FailNpnSpdyAndFallback) { HttpNetworkTransaction::SetUseAlternateProtocols(true); + HttpNetworkTransaction::SetNextProtos( + "\x08http/1.1\x07http1.1\x06spdy/1\x04spdy"); SessionDependencies session_deps; HttpRequestInfo request; @@ -4690,6 +4732,183 @@ TEST_F(HttpNetworkTransactionTest, FailNpnSpdyAndFallback) { std::string response_data; ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data)); EXPECT_EQ("hello world", response_data); + HttpNetworkTransaction::SetNextProtos(""); + HttpNetworkTransaction::SetUseAlternateProtocols(false); +} + +TEST_F(HttpNetworkTransactionTest, UseAlternateProtocolForNpnSpdy) { + HttpNetworkTransaction::SetUseAlternateProtocols(true); + HttpNetworkTransaction::SetNextProtos( + "\x08http/1.1\x07http1.1\x06spdy/1\x04spdy"); + SessionDependencies session_deps; + + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + request.load_flags = 0; + + MockRead data_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead("Alternate-Protocol: 443:npn-spdy/1\r\n\r\n"), + MockRead("hello world"), + MockRead(true, OK), + }; + + StaticSocketDataProvider first_transaction( + data_reads, arraysize(data_reads), NULL, 0); + session_deps.socket_factory.AddSocketDataProvider(&first_transaction); + + SSLSocketDataProvider ssl(true, OK); + ssl.next_proto_status = SSLClientSocket::kNextProtoNegotiated; + ssl.next_proto = "spdy/1"; + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl); + + MockWrite spdy_writes[] = { + MockWrite(true, reinterpret_cast<const char*>(kGetSyn), + arraysize(kGetSyn)), + }; + + MockRead spdy_reads[] = { + MockRead(true, reinterpret_cast<const char*>(kGetSynReply), + arraysize(kGetSynReply)), + MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), + arraysize(kGetBodyFrame)), + MockRead(true, 0, 0), + }; + + scoped_refptr<DelayedSocketData> spdy_data( + new DelayedSocketData( + 1, // wait for one write to finish before reading. + spdy_reads, arraysize(spdy_reads), + spdy_writes, arraysize(spdy_writes))); + session_deps.socket_factory.AddSocketDataProvider(spdy_data); + + TestCompletionCallback callback; + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + scoped_ptr<HttpNetworkTransaction> trans(new HttpNetworkTransaction(session)); + + int rv = trans->Start(&request, &callback, NULL); + EXPECT_EQ(ERR_IO_PENDING, rv); + EXPECT_EQ(OK, callback.WaitForResult()); + + const HttpResponseInfo* response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + ASSERT_TRUE(response->headers != NULL); + EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); + + std::string response_data; + ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data)); + EXPECT_EQ("hello world", response_data); + + trans.reset(new HttpNetworkTransaction(session)); + + rv = trans->Start(&request, &callback, NULL); + EXPECT_EQ(ERR_IO_PENDING, rv); + EXPECT_EQ(OK, callback.WaitForResult()); + + response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + ASSERT_TRUE(response->headers != NULL); + EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); + + ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data)); + EXPECT_EQ("hello!", response_data); + + HttpNetworkTransaction::SetNextProtos(""); + HttpNetworkTransaction::SetUseAlternateProtocols(false); +} + +TEST_F(HttpNetworkTransactionTest, + UseAlternateProtocolForNpnSpdyWithExistingSpdySession) { + HttpNetworkTransaction::SetUseAlternateProtocols(true); + HttpNetworkTransaction::SetNextProtos( + "\x08http/1.1\x07http1.1\x06spdy/1\x04spdy"); + SessionDependencies session_deps; + + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + request.load_flags = 0; + + MockRead data_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead("Alternate-Protocol: 443:npn-spdy/1\r\n\r\n"), + MockRead("hello world"), + MockRead(true, OK), + }; + + StaticSocketDataProvider first_transaction( + data_reads, arraysize(data_reads), NULL, 0); + session_deps.socket_factory.AddSocketDataProvider(&first_transaction); + + SSLSocketDataProvider ssl(true, OK); + ssl.next_proto_status = SSLClientSocket::kNextProtoNegotiated; + ssl.next_proto = "spdy/1"; + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl); + + MockWrite spdy_writes[] = { + MockWrite(true, reinterpret_cast<const char*>(kGetSyn), + arraysize(kGetSyn)), + }; + + MockRead spdy_reads[] = { + MockRead(true, reinterpret_cast<const char*>(kGetSynReply), + arraysize(kGetSynReply)), + MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), + arraysize(kGetBodyFrame)), + MockRead(true, 0, 0), + }; + + scoped_refptr<DelayedSocketData> spdy_data( + new DelayedSocketData( + 1, // wait for one write to finish before reading. + spdy_reads, arraysize(spdy_reads), + spdy_writes, arraysize(spdy_writes))); + session_deps.socket_factory.AddSocketDataProvider(spdy_data); + + TestCompletionCallback callback; + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + + scoped_ptr<HttpNetworkTransaction> trans(new HttpNetworkTransaction(session)); + + int rv = trans->Start(&request, &callback, NULL); + EXPECT_EQ(ERR_IO_PENDING, rv); + EXPECT_EQ(OK, callback.WaitForResult()); + + const HttpResponseInfo* response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + ASSERT_TRUE(response->headers != NULL); + EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); + + std::string response_data; + ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data)); + EXPECT_EQ("hello world", response_data); + + // Set up an initial SpdySession in the pool to reuse. + scoped_refptr<SpdySession> spdy_session = + session->spdy_session_pool()->Get(HostPortPair("www.google.com", 443), + session); + TCPSocketParams tcp_params("www.google.com", 443, MEDIUM, GURL(), false); + spdy_session->Connect( + "www.google.com:443", tcp_params, MEDIUM, BoundNetLog()); + + trans.reset(new HttpNetworkTransaction(session)); + + rv = trans->Start(&request, &callback, NULL); + EXPECT_EQ(ERR_IO_PENDING, rv); + EXPECT_EQ(OK, callback.WaitForResult()); + + response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + ASSERT_TRUE(response->headers != NULL); + EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); + + ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data)); + EXPECT_EQ("hello!", response_data); + + HttpNetworkTransaction::SetNextProtos(""); HttpNetworkTransaction::SetUseAlternateProtocols(false); } diff --git a/net/net.gyp b/net/net.gyp index 2a5dc4d..7ef7bf2 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -718,6 +718,7 @@ 'spdy/spdy_protocol_test.cc', 'spdy/spdy_session_unittest.cc', 'spdy/spdy_stream_unittest.cc', + 'spdy/spdy_test_util.h', 'url_request/url_request_unittest.cc', 'url_request/url_request_unittest.h', 'websockets/websocket_handshake_draft75_unittest.cc', diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc index 9e74d88..360f34f 100644 --- a/net/socket/socket_test_util.cc +++ b/net/socket/socket_test_util.cc @@ -256,10 +256,6 @@ MockSSLClientSocket::~MockSSLClientSocket() { Disconnect(); } -void MockSSLClientSocket::GetSSLInfo(net::SSLInfo* ssl_info) { - ssl_info->Reset(); -} - int MockSSLClientSocket::Connect(net::CompletionCallback* callback) { ConnectCallback* connect_callback = new ConnectCallback( this, callback, data_->connect.result); @@ -293,6 +289,16 @@ int MockSSLClientSocket::Write(net::IOBuffer* buf, int buf_len, return transport_->Write(buf, buf_len, callback); } +void MockSSLClientSocket::GetSSLInfo(net::SSLInfo* ssl_info) { + ssl_info->Reset(); +} + +SSLClientSocket::NextProtoStatus MockSSLClientSocket::GetNextProto( + std::string* proto) { + *proto = data_->next_proto; + return data_->next_proto_status; +} + MockRead StaticSocketDataProvider::GetNextRead() { DCHECK(!at_read_eof()); reads_[read_index_].time_stamp = base::Time::Now(); @@ -386,6 +392,51 @@ void DynamicSocketDataProvider::SimulateRead(const char* data) { reads_.push_back(MockRead(data)); } +DelayedSocketData::DelayedSocketData( + int write_delay, MockRead* reads, size_t reads_count, + MockWrite* writes, size_t writes_count) + : StaticSocketDataProvider(reads, reads_count, writes, writes_count), + write_delay_(write_delay), + ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) { + DCHECK_GE(write_delay_, 0); +} + +DelayedSocketData::DelayedSocketData( + const MockConnect& connect, int write_delay, MockRead* reads, + size_t reads_count, MockWrite* writes, size_t writes_count) + : StaticSocketDataProvider(reads, reads_count, writes, writes_count), + write_delay_(write_delay), + ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) { + DCHECK_GE(write_delay_, 0); + set_connect_data(connect); +} + +MockRead DelayedSocketData::GetNextRead() { + if (write_delay_) + return MockRead(true, ERR_IO_PENDING); + return StaticSocketDataProvider::GetNextRead(); +} + +MockWriteResult DelayedSocketData::OnWrite(const std::string& data) { + MockWriteResult rv = StaticSocketDataProvider::OnWrite(data); + // Now that our write has completed, we can allow reads to continue. + if (!--write_delay_) + MessageLoop::current()->PostDelayedTask(FROM_HERE, + factory_.NewRunnableMethod(&DelayedSocketData::CompleteRead), 100); + return rv; +} + +void DelayedSocketData::Reset() { + set_socket(NULL); + factory_.RevokeAll(); + StaticSocketDataProvider::Reset(); +} + +void DelayedSocketData::CompleteRead() { + if (socket()) + socket()->OnReadComplete(GetNextRead()); +} + void MockClientSocketFactory::AddSocketDataProvider( SocketDataProvider* data) { mock_data_.Add(data); diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h index 4358a72..e309786c 100644 --- a/net/socket/socket_test_util.h +++ b/net/socket/socket_test_util.h @@ -229,9 +229,51 @@ class DynamicSocketDataProvider : public SocketDataProvider { // SSLSocketDataProviders only need to keep track of the return code from calls // to Connect(). struct SSLSocketDataProvider { - SSLSocketDataProvider(bool async, int result) : connect(async, result) { } + SSLSocketDataProvider(bool async, int result) + : connect(async, result), + next_proto_status(SSLClientSocket::kNextProtoUnsupported) { } MockConnect connect; + SSLClientSocket::NextProtoStatus next_proto_status; + std::string next_proto; +}; + +// A DataProvider where the client must write a request before the reads (e.g. +// the response) will complete. +class DelayedSocketData : public StaticSocketDataProvider, + public base::RefCounted<DelayedSocketData> { + public: + // |write_delay| the number of MockWrites to complete before allowing + // a MockRead to complete. + // |reads| the list of MockRead completions. + // |writes| the list of MockWrite completions. + // Note: All MockReads and MockWrites must be async. + // Note: The MockRead and MockWrite lists musts end with a EOF + // e.g. a MockRead(true, 0, 0); + DelayedSocketData(int write_delay, + MockRead* reads, size_t reads_count, + MockWrite* writes, size_t writes_count); + + // |connect| the result for the connect phase. + // |reads| the list of MockRead completions. + // |write_delay| the number of MockWrites to complete before allowing + // a MockRead to complete. + // |writes| the list of MockWrite completions. + // Note: All MockReads and MockWrites must be async. + // Note: The MockRead and MockWrite lists musts end with a EOF + // e.g. a MockRead(true, 0, 0); + DelayedSocketData(const MockConnect& connect, int write_delay, + MockRead* reads, size_t reads_count, + MockWrite* writes, size_t writes_count); + + virtual MockRead GetNextRead(); + virtual MockWriteResult OnWrite(const std::string& data); + virtual void Reset(); + void CompleteRead(); + + private: + int write_delay_; + ScopedRunnableMethodFactory<DelayedSocketData> factory_; }; // Holds an array of SocketDataProvider elements. As Mock{TCP,SSL}ClientSocket @@ -401,8 +443,7 @@ class MockSSLClientSocket : public MockClientSocket { net::SSLSocketDataProvider* socket); ~MockSSLClientSocket(); - virtual void GetSSLInfo(net::SSLInfo* ssl_info); - + // ClientSocket methods: virtual int Connect(net::CompletionCallback* callback); virtual void Disconnect(); @@ -412,6 +453,10 @@ class MockSSLClientSocket : public MockClientSocket { virtual int Write(net::IOBuffer* buf, int buf_len, net::CompletionCallback* callback); + // SSLClientSocket methods: + virtual void GetSSLInfo(net::SSLInfo* ssl_info); + virtual NextProtoStatus GetNextProto(std::string* proto); + // This MockSocket does not implement the manual async IO feature. virtual void OnReadComplete(const MockRead& data) { NOTIMPLEMENTED(); } diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h index b07dd02..2471c36 100644 --- a/net/spdy/spdy_framer.h +++ b/net/spdy/spdy_framer.h @@ -25,6 +25,7 @@ typedef struct z_stream_s z_stream; // Forward declaration for zlib. namespace net { class HttpNetworkLayer; +class HttpNetworkTransactionTest; class SpdyNetworkTransactionTest; } @@ -237,6 +238,7 @@ class SpdyFramer { protected: FRIEND_TEST(SpdyFramerTest, HeaderBlockBarfsOnOutOfOrderHeaders); friend class net::SpdyNetworkTransactionTest; + friend class net::HttpNetworkTransactionTest; friend class net::HttpNetworkLayer; // This is temporary for the server. friend class test::TestSpdyVisitor; friend void test::FramerSetEnableCompressionHelper(SpdyFramer* framer, @@ -292,4 +294,3 @@ class SpdyFramer { } // namespace spdy #endif // NET_SPDY_SPDY_FRAMER_H_ - diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc index a115fd6..c257e66 100644 --- a/net/spdy/spdy_network_transaction_unittest.cc +++ b/net/spdy/spdy_network_transaction_unittest.cc @@ -20,6 +20,7 @@ #include "net/socket/socket_test_util.h" #include "net/spdy/spdy_framer.h" #include "net/spdy/spdy_protocol.h" +#include "net/spdy/spdy_test_util.h" #include "testing/platform_test.h" #define NET_TRACE(level, s) DLOG(level) << s << __FUNCTION__ << "() " @@ -193,107 +194,6 @@ void DumpMockRead(const MockRead& r) { // ---------------------------------------------------------------------------- -static const unsigned char kGetSyn[] = { - 0x80, 0x01, 0x00, 0x01, // header - 0x01, 0x00, 0x00, 0x49, // FIN, len - 0x00, 0x00, 0x00, 0x01, // stream id - 0x00, 0x00, 0x00, 0x00, // associated - 0xc0, 0x00, 0x00, 0x03, // 3 headers - 0x00, 0x06, 'm', 'e', 't', 'h', 'o', 'd', - 0x00, 0x03, 'G', 'E', 'T', - 0x00, 0x03, 'u', 'r', 'l', - 0x00, 0x16, 'h', 't', 't', 'p', ':', '/', '/', 'w', 'w', 'w', - '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'c', 'o', - 'm', '/', - 0x00, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n', - 0x00, 0x08, 'H', 'T', 'T', 'P', '/', '1', '.', '1', -}; - -static const unsigned char kGetSynCompressed[] = { - 0x80, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x47, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0xc0, 0x00, 0x38, 0xea, 0xdf, 0xa2, 0x51, 0xb2, - 0x62, 0x60, 0x66, 0x60, 0xcb, 0x05, 0xe6, 0xc3, - 0xfc, 0x14, 0x06, 0x66, 0x77, 0xd7, 0x10, 0x06, - 0x66, 0x90, 0xa0, 0x58, 0x46, 0x49, 0x49, 0x81, - 0x95, 0xbe, 0x3e, 0x30, 0xe2, 0xf5, 0xd2, 0xf3, - 0xf3, 0xd3, 0x73, 0x52, 0xf5, 0x92, 0xf3, 0x73, - 0xf5, 0x19, 0xd8, 0xa1, 0x1a, 0x19, 0x38, 0x60, - 0xe6, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff -}; - -static const unsigned char kGetSynReply[] = { - 0x80, 0x01, 0x00, 0x02, // header - 0x00, 0x00, 0x00, 0x45, - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x04, // 4 headers - 0x00, 0x05, 'h', 'e', 'l', 'l', 'o', // "hello" - 0x00, 0x03, 'b', 'y', 'e', // "bye" - 0x00, 0x06, 's', 't', 'a', 't', 'u', 's', // "status" - 0x00, 0x03, '2', '0', '0', // "200" - 0x00, 0x03, 'u', 'r', 'l', // "url" - 0x00, 0x0a, '/', 'i', 'n', 'd', 'e', 'x', '.', 'p', 'h', 'p', // "/index... - 0x00, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n', // "version" - 0x00, 0x08, 'H', 'T', 'T', 'P', '/', '1', '.', '1', // "HTTP/1.1" -}; - -static const unsigned char kGetBodyFrame[] = { - 0x00, 0x00, 0x00, 0x01, // header - 0x01, 0x00, 0x00, 0x06, // FIN, length - 'h', 'e', 'l', 'l', 'o', '!', // "hello" -}; - -static const unsigned char kPostSyn[] = { - 0x80, 0x01, 0x00, 0x01, // header - 0x00, 0x00, 0x00, 0x4a, // flags, len - 0x00, 0x00, 0x00, 0x01, // stream id - 0x00, 0x00, 0x00, 0x00, // associated - 0xc0, 0x00, 0x00, 0x03, // 4 headers - 0x00, 0x06, 'm', 'e', 't', 'h', 'o', 'd', - 0x00, 0x04, 'P', 'O', 'S', 'T', - 0x00, 0x03, 'u', 'r', 'l', - 0x00, 0x16, 'h', 't', 't', 'p', ':', '/', '/', 'w', 'w', 'w', - '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'c', 'o', - 'm', '/', - 0x00, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n', - 0x00, 0x08, 'H', 'T', 'T', 'P', '/', '1', '.', '1', -}; - -static const unsigned char kPostUploadFrame[] = { - 0x00, 0x00, 0x00, 0x01, // header - 0x01, 0x00, 0x00, 0x0c, // FIN flag - 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '\0' -}; - -// The response -static const unsigned char kPostSynReply[] = { - 0x80, 0x01, 0x00, 0x02, // header - 0x00, 0x00, 0x00, 0x45, - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x04, // 4 headers - 0x00, 0x05, 'h', 'e', 'l', 'l', 'o', // "hello" - 0x00, 0x03, 'b', 'y', 'e', // "bye" - 0x00, 0x06, 's', 't', 'a', 't', 'u', 's', // "status" - 0x00, 0x03, '2', '0', '0', // "200" - 0x00, 0x03, 'u', 'r', 'l', // "url" - // "/index.php" - 0x00, 0x0a, '/', 'i', 'n', 'd', 'e', 'x', '.', 'p', 'h', 'p', - 0x00, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n', // "version" - 0x00, 0x08, 'H', 'T', 'T', 'P', '/', '1', '.', '1', // "HTTP/1.1" -}; - -static const unsigned char kPostBodyFrame[] = { - 0x00, 0x00, 0x00, 0x01, // header - 0x01, 0x00, 0x00, 0x06, // FIN, length - 'h', 'e', 'l', 'l', 'o', '!', // "hello" -}; - -static const unsigned char kGoAway[] = { - 0x80, 0x01, 0x00, 0x07, // header - 0x00, 0x00, 0x00, 0x04, // flags, len - 0x00, 0x00, 0x00, 0x00, // last-accepted-stream-id -}; - // Adds headers and values to a map. // |extra_headers| is an array of { name, value } pairs, arranged as strings // where the even entries are the header names, and the odd entries are the @@ -594,76 +494,6 @@ spdy::SpdyFrame* ConstructSpdyGet(const char* const extra_headers[], } // namespace -// A DataProvider where the client must write a request before the reads (e.g. -// the response) will complete. -class DelayedSocketData : public StaticSocketDataProvider, - public base::RefCounted<DelayedSocketData> { - public: - // |write_delay| the number of MockWrites to complete before allowing - // a MockRead to complete. - // |reads| the list of MockRead completions. - // |writes| the list of MockWrite completions. - // Note: All MockReads and MockWrites must be async. - // Note: The MockRead and MockWrite lists musts end with a EOF - // e.g. a MockRead(true, 0, 0); - DelayedSocketData(int write_delay, - MockRead* reads, size_t reads_count, - MockWrite* writes, size_t writes_count) - : StaticSocketDataProvider(reads, reads_count, writes, writes_count), - write_delay_(write_delay), - ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) { - DCHECK_GE(write_delay_, 0); - } - - // |connect| the result for the connect phase. - // |reads| the list of MockRead completions. - // |write_delay| the number of MockWrites to complete before allowing - // a MockRead to complete. - // |writes| the list of MockWrite completions. - // Note: All MockReads and MockWrites must be async. - // Note: The MockRead and MockWrite lists musts end with a EOF - // e.g. a MockRead(true, 0, 0); - DelayedSocketData(const MockConnect& connect, int write_delay, - MockRead* reads, size_t reads_count, - MockWrite* writes, size_t writes_count) - : StaticSocketDataProvider(reads, reads_count, writes, writes_count), - write_delay_(write_delay), - ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) { - DCHECK_GE(write_delay_, 0); - set_connect_data(connect); - } - - virtual MockRead GetNextRead() { - if (write_delay_) - return MockRead(true, ERR_IO_PENDING); - return StaticSocketDataProvider::GetNextRead(); - } - - virtual MockWriteResult OnWrite(const std::string& data) { - MockWriteResult rv = StaticSocketDataProvider::OnWrite(data); - // Now that our write has completed, we can allow reads to continue. - if (!--write_delay_) - MessageLoop::current()->PostDelayedTask(FROM_HERE, - factory_.NewRunnableMethod(&DelayedSocketData::CompleteRead), 100); - return rv; - } - - virtual void Reset() { - set_socket(NULL); - factory_.RevokeAll(); - StaticSocketDataProvider::Reset(); - } - - void CompleteRead() { - if (socket()) - socket()->OnReadComplete(GetNextRead()); - } - - private: - int write_delay_; - ScopedRunnableMethodFactory<DelayedSocketData> factory_; -}; - // A DataProvider where the reads are ordered. // If a read is requested before its sequence number is reached, we return an // ERR_IO_PENDING (that way we don't have to explicitly add a MockRead just to diff --git a/net/spdy/spdy_test_util.h b/net/spdy/spdy_test_util.h new file mode 100644 index 0000000..830a290 --- /dev/null +++ b/net/spdy/spdy_test_util.h @@ -0,0 +1,115 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_SPDY_SPDY_TEST_UTIL_H_ +#define NET_SPDY_SPDY_TEST_UTIL_H_ + +#include "base/basictypes.h" + +namespace net { + +const uint8 kGetSyn[] = { + 0x80, 0x01, 0x00, 0x01, // header + 0x01, 0x00, 0x00, 0x49, // FIN, len + 0x00, 0x00, 0x00, 0x01, // stream id + 0x00, 0x00, 0x00, 0x00, // associated + 0xc0, 0x00, 0x00, 0x03, // 3 headers + 0x00, 0x06, 'm', 'e', 't', 'h', 'o', 'd', + 0x00, 0x03, 'G', 'E', 'T', + 0x00, 0x03, 'u', 'r', 'l', + 0x00, 0x16, 'h', 't', 't', 'p', ':', '/', '/', 'w', 'w', 'w', + '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'c', 'o', + 'm', '/', + 0x00, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n', + 0x00, 0x08, 'H', 'T', 'T', 'P', '/', '1', '.', '1', +}; + +const uint8 kGetSynReply[] = { + 0x80, 0x01, 0x00, 0x02, // header + 0x00, 0x00, 0x00, 0x45, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x04, // 4 headers + 0x00, 0x05, 'h', 'e', 'l', 'l', 'o', // "hello" + 0x00, 0x03, 'b', 'y', 'e', // "bye" + 0x00, 0x06, 's', 't', 'a', 't', 'u', 's', // "status" + 0x00, 0x03, '2', '0', '0', // "200" + 0x00, 0x03, 'u', 'r', 'l', // "url" + 0x00, 0x0a, '/', 'i', 'n', 'd', 'e', 'x', '.', 'p', 'h', 'p', // "/index... + 0x00, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n', // "version" + 0x00, 0x08, 'H', 'T', 'T', 'P', '/', '1', '.', '1', // "HTTP/1.1" +}; + +const uint8 kGetBodyFrame[] = { + 0x00, 0x00, 0x00, 0x01, // header + 0x01, 0x00, 0x00, 0x06, // FIN, length + 'h', 'e', 'l', 'l', 'o', '!', // "hello" +}; + +const uint8 kGetSynCompressed[] = { + 0x80, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x47, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x38, 0xea, 0xdf, 0xa2, 0x51, 0xb2, + 0x62, 0x60, 0x66, 0x60, 0xcb, 0x05, 0xe6, 0xc3, + 0xfc, 0x14, 0x06, 0x66, 0x77, 0xd7, 0x10, 0x06, + 0x66, 0x90, 0xa0, 0x58, 0x46, 0x49, 0x49, 0x81, + 0x95, 0xbe, 0x3e, 0x30, 0xe2, 0xf5, 0xd2, 0xf3, + 0xf3, 0xd3, 0x73, 0x52, 0xf5, 0x92, 0xf3, 0x73, + 0xf5, 0x19, 0xd8, 0xa1, 0x1a, 0x19, 0x38, 0x60, + 0xe6, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff +}; + +const uint8 kPostSyn[] = { + 0x80, 0x01, 0x00, 0x01, // header + 0x00, 0x00, 0x00, 0x4a, // flags, len + 0x00, 0x00, 0x00, 0x01, // stream id + 0x00, 0x00, 0x00, 0x00, // associated + 0xc0, 0x00, 0x00, 0x03, // 4 headers + 0x00, 0x06, 'm', 'e', 't', 'h', 'o', 'd', + 0x00, 0x04, 'P', 'O', 'S', 'T', + 0x00, 0x03, 'u', 'r', 'l', + 0x00, 0x16, 'h', 't', 't', 'p', ':', '/', '/', 'w', 'w', 'w', + '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'c', 'o', + 'm', '/', + 0x00, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n', + 0x00, 0x08, 'H', 'T', 'T', 'P', '/', '1', '.', '1', +}; + +const uint8 kPostUploadFrame[] = { + 0x00, 0x00, 0x00, 0x01, // header + 0x01, 0x00, 0x00, 0x0c, // FIN flag + 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '\0' +}; + +// The response +const uint8 kPostSynReply[] = { + 0x80, 0x01, 0x00, 0x02, // header + 0x00, 0x00, 0x00, 0x45, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x04, // 4 headers + 0x00, 0x05, 'h', 'e', 'l', 'l', 'o', // "hello" + 0x00, 0x03, 'b', 'y', 'e', // "bye" + 0x00, 0x06, 's', 't', 'a', 't', 'u', 's', // "status" + 0x00, 0x03, '2', '0', '0', // "200" + 0x00, 0x03, 'u', 'r', 'l', // "url" + // "/index.php" + 0x00, 0x0a, '/', 'i', 'n', 'd', 'e', 'x', '.', 'p', 'h', 'p', + 0x00, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n', // "version" + 0x00, 0x08, 'H', 'T', 'T', 'P', '/', '1', '.', '1', // "HTTP/1.1" +}; + +const uint8 kPostBodyFrame[] = { + 0x00, 0x00, 0x00, 0x01, // header + 0x01, 0x00, 0x00, 0x06, // FIN, length + 'h', 'e', 'l', 'l', 'o', '!', // "hello" +}; + +const uint8 kGoAway[] = { + 0x80, 0x01, 0x00, 0x07, // header + 0x00, 0x00, 0x00, 0x04, // flags, len + 0x00, 0x00, 0x00, 0x00, // last-accepted-stream-id +}; + +} // namespace net + +#endif // NET_SPDY_SPDY_TEST_UTIL_H_ |