summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwillchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-26 22:20:54 +0000
committerwillchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-26 22:20:54 +0000
commit2ff8b3133033d6130ea87df3ab2290a226c7bca7 (patch)
tree74ee45d17428c359a0bd8db8b97e51eeb4b7c44c
parent106113b3dde22f7d8b3a7f9a00a8884668cda782 (diff)
downloadchromium_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
-rw-r--r--net/base/host_port_pair.cc9
-rw-r--r--net/base/host_port_pair.h10
-rw-r--r--net/http/http_network_transaction.cc50
-rw-r--r--net/http/http_network_transaction.h6
-rw-r--r--net/http/http_network_transaction_unittest.cc231
-rw-r--r--net/net.gyp1
-rw-r--r--net/socket/socket_test_util.cc59
-rw-r--r--net/socket/socket_test_util.h51
-rw-r--r--net/spdy/spdy_framer.h3
-rw-r--r--net/spdy/spdy_network_transaction_unittest.cc172
-rw-r--r--net/spdy/spdy_test_util.h115
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_