diff options
author | willchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-09 16:30:42 +0000 |
---|---|---|
committer | willchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-09 16:30:42 +0000 |
commit | 564b491ea6d163724307331648bd7b45cdada99e (patch) | |
tree | 135aa4b4e6e36e5ace999fb363c20b7913f4061d /net | |
parent | ad54c1966904ed5b3d7e71c8f6caf4606c7e6a93 (diff) | |
download | chromium_src-564b491ea6d163724307331648bd7b45cdada99e.zip chromium_src-564b491ea6d163724307331648bd7b45cdada99e.tar.gz chromium_src-564b491ea6d163724307331648bd7b45cdada99e.tar.bz2 |
SPDY: Add basic support for Alternate-Protocol header.
Review URL: http://codereview.chromium.org/668197
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@41032 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/base/host_port_pair.cc | 14 | ||||
-rw-r--r-- | net/base/host_port_pair.h | 33 | ||||
-rw-r--r-- | net/http/http_alternate_protocols.cc | 87 | ||||
-rw-r--r-- | net/http/http_alternate_protocols.h | 72 | ||||
-rw-r--r-- | net/http/http_alternate_protocols_unittest.cc | 53 | ||||
-rw-r--r-- | net/http/http_network_session.h | 9 | ||||
-rw-r--r-- | net/http/http_network_transaction.cc | 94 | ||||
-rw-r--r-- | net/http/http_network_transaction.h | 9 | ||||
-rw-r--r-- | net/http/http_network_transaction_unittest.cc | 170 | ||||
-rw-r--r-- | net/net.gyp | 5 | ||||
-rw-r--r-- | net/spdy/spdy_session_pool.h | 18 |
11 files changed, 546 insertions, 18 deletions
diff --git a/net/base/host_port_pair.cc b/net/base/host_port_pair.cc new file mode 100644 index 0000000..06a95fb --- /dev/null +++ b/net/base/host_port_pair.cc @@ -0,0 +1,14 @@ +// 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. + +#include "net/base/host_port_pair.h" +#include "base/string_util.h" + +namespace net { + +std::string HostPortPair::ToString() const { + return StringPrintf("[Host: %s, Port: %u]", host.c_str(), port); +} + +} // namespace net diff --git a/net/base/host_port_pair.h b/net/base/host_port_pair.h new file mode 100644 index 0000000..ab7f312 --- /dev/null +++ b/net/base/host_port_pair.h @@ -0,0 +1,33 @@ +// 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_BASE_HOST_PORT_PAIR_H_ +#define NET_BASE_HOST_PORT_PAIR_H_ + +#include <string> +#include "base/basictypes.h" + +namespace net { + +struct HostPortPair { + HostPortPair() {} + HostPortPair(const std::string& in_host, uint16 in_port) + : host(in_host), port(in_port) {} + + // Comparator function so this can be placed in a std::map. + bool operator<(const HostPortPair& other) const { + if (host != other.host) + return host < other.host; + return port < other.port; + } + + std::string ToString() const; + + std::string host; + uint16 port; +}; + +} // namespace net + +#endif // NET_BASE_HOST_PORT_PAIR_H_ diff --git a/net/http/http_alternate_protocols.cc b/net/http/http_alternate_protocols.cc new file mode 100644 index 0000000..7f8bdfa --- /dev/null +++ b/net/http/http_alternate_protocols.cc @@ -0,0 +1,87 @@ +// 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. + +#include "net/http/http_alternate_protocols.h" + +#include "base/logging.h" +#include "base/stl_util-inl.h" + +namespace net { + +const char HttpAlternateProtocols::kHeader[] = "Alternate-Protocol"; +const char HttpAlternateProtocols::kSpdyProtocol[] = "SPDY"; + +HttpAlternateProtocols::HttpAlternateProtocols() {} +HttpAlternateProtocols::~HttpAlternateProtocols() {} + +bool HttpAlternateProtocols::HasAlternateProtocolFor( + const HostPortPair& http_host_port_pair) const { + return ContainsKey(protocol_map_, http_host_port_pair); +} + +bool HttpAlternateProtocols::HasAlternateProtocolFor( + const std::string& host, uint16 port) const { + struct HostPortPair http_host_port_pair; + http_host_port_pair.host = host; + http_host_port_pair.port = port; + return HasAlternateProtocolFor(http_host_port_pair); +} + +HttpAlternateProtocols::PortProtocolPair +HttpAlternateProtocols::GetAlternateProtocolFor( + const HostPortPair& http_host_port_pair) const { + DCHECK(ContainsKey(protocol_map_, http_host_port_pair)); + return protocol_map_.find(http_host_port_pair)->second; +} + +HttpAlternateProtocols::PortProtocolPair +HttpAlternateProtocols::GetAlternateProtocolFor( + const std::string& host, uint16 port) const { + struct HostPortPair http_host_port_pair; + http_host_port_pair.host = host; + http_host_port_pair.port = port; + return GetAlternateProtocolFor(http_host_port_pair); +} + +void HttpAlternateProtocols::SetAlternateProtocolFor( + const HostPortPair& http_host_port_pair, + uint16 alternate_port, + Protocol alternate_protocol) { + if (alternate_protocol == BROKEN) { + LOG(DFATAL) << "Call MarkBrokenAlternateProtocolFor() instead."; + return; + } + + PortProtocolPair alternate; + alternate.port = alternate_port; + alternate.protocol = alternate_protocol; + if (HasAlternateProtocolFor(http_host_port_pair)) { + const PortProtocolPair existing_alternate = + GetAlternateProtocolFor(http_host_port_pair); + + if (existing_alternate.protocol == BROKEN) { + DLOG(INFO) << "Ignore alternate protocol since it's known to be broken."; + return; + } + + if (alternate_protocol != BROKEN && !existing_alternate.Equals(alternate)) { + LOG(WARNING) << "Changing the alternate protocol for: " + << http_host_port_pair.ToString() + << " from [Port: " << existing_alternate.port + << ", Protocol: " << existing_alternate.protocol + << "] to [Port: " << alternate_port + << ", Protocol: " << alternate_protocol + << "]."; + } + } + + protocol_map_[http_host_port_pair] = alternate; +} + +void HttpAlternateProtocols::MarkBrokenAlternateProtocolFor( + const HostPortPair& http_host_port_pair) { + protocol_map_[http_host_port_pair].protocol = BROKEN; +} + +} // namespace net diff --git a/net/http/http_alternate_protocols.h b/net/http/http_alternate_protocols.h new file mode 100644 index 0000000..9ce5157 --- /dev/null +++ b/net/http/http_alternate_protocols.h @@ -0,0 +1,72 @@ +// 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. +// +// HttpAlternateProtocols is an in-memory data structure used for keeping track +// of which HTTP HostPortPairs have an alternate protocol that can be used +// instead of HTTP on a different port. + +#ifndef NET_HTTP_HTTP_ALTERNATE_PROTOCOLS_H_ +#define NET_HTTP_HTTP_ALTERNATE_PROTOCOLS_H_ + +#include <map> +#include <utility> +#include "base/basictypes.h" +#include "net/base/host_port_pair.h" + +namespace net { + +class HttpAlternateProtocols { + public: + enum Protocol { + BROKEN, // The alternate protocol is known to be broken. + SPDY, + NUM_ALTERNATE_PROTOCOLS, + }; + + struct PortProtocolPair { + bool Equals(const PortProtocolPair& other) const { + return port == other.port && protocol == other.protocol; + } + + uint16 port; + Protocol protocol; + }; + + static const char kHeader[]; + static const char kSpdyProtocol[]; + + HttpAlternateProtocols(); + ~HttpAlternateProtocols(); + + // Reports whether or not we have received Alternate-Protocol for + // |http_host_port_pair|. + bool HasAlternateProtocolFor(const HostPortPair& http_host_port_pair) const; + bool HasAlternateProtocolFor(const std::string& host, uint16 port) const; + + PortProtocolPair GetAlternateProtocolFor( + const HostPortPair& http_host_port_pair) const; + PortProtocolPair GetAlternateProtocolFor( + const std::string& host, uint16 port) const; + + // SetAlternateProtocolFor() will ignore the request if the alternate protocol + // has already been marked broken via MarkBrokenAlternateProtocolFor(). + void SetAlternateProtocolFor(const HostPortPair& http_host_port_pair, + uint16 alternate_port, + Protocol alternate_protocol); + + // Marks the alternate protocol as broken. Once marked broken, any further + // attempts to set the alternate protocol for |http_host_port_pair| will fail. + void MarkBrokenAlternateProtocolFor(const HostPortPair& http_host_port_pair); + + private: + typedef std::map<HostPortPair, PortProtocolPair> ProtocolMap; + + ProtocolMap protocol_map_; + + DISALLOW_COPY_AND_ASSIGN(HttpAlternateProtocols); +}; + +} // namespace net + +#endif // NET_HTTP_HTTP_ALTERNATE_PROTOCOLS_H_ diff --git a/net/http/http_alternate_protocols_unittest.cc b/net/http/http_alternate_protocols_unittest.cc new file mode 100644 index 0000000..df1a832 --- /dev/null +++ b/net/http/http_alternate_protocols_unittest.cc @@ -0,0 +1,53 @@ +// 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. +// +// HttpAlternateProtocols is an in-memory data structure used for keeping track +// of which HTTP HostPortPairs have an alternate protocol that can be used +// instead of HTTP on a different port. + +#include "net/http/http_alternate_protocols.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace { + +TEST(HttpAlternateProtocols, Basic) { + HttpAlternateProtocols alternate_protocols; + HostPortPair test_host_port_pair; + test_host_port_pair.host = "foo"; + test_host_port_pair.port = 80; + EXPECT_FALSE( + alternate_protocols.HasAlternateProtocolFor(test_host_port_pair)); + alternate_protocols.SetAlternateProtocolFor(test_host_port_pair, + 443, + HttpAlternateProtocols::SPDY); + ASSERT_TRUE(alternate_protocols.HasAlternateProtocolFor(test_host_port_pair)); + const HttpAlternateProtocols::PortProtocolPair alternate = + alternate_protocols.GetAlternateProtocolFor(test_host_port_pair); + EXPECT_EQ(443, alternate.port); + EXPECT_EQ(HttpAlternateProtocols::SPDY, alternate.protocol); +} + +TEST(HttpAlternateProtocols, SetBroken) { + HttpAlternateProtocols alternate_protocols; + HostPortPair test_host_port_pair; + test_host_port_pair.host = "foo"; + test_host_port_pair.port = 80; + alternate_protocols.MarkBrokenAlternateProtocolFor(test_host_port_pair); + ASSERT_TRUE(alternate_protocols.HasAlternateProtocolFor(test_host_port_pair)); + HttpAlternateProtocols::PortProtocolPair alternate = + alternate_protocols.GetAlternateProtocolFor(test_host_port_pair); + EXPECT_EQ(HttpAlternateProtocols::BROKEN, alternate.protocol); + + alternate_protocols.SetAlternateProtocolFor( + test_host_port_pair, + 1234, + HttpAlternateProtocols::SPDY), + alternate = alternate_protocols.GetAlternateProtocolFor(test_host_port_pair); + EXPECT_EQ(HttpAlternateProtocols::BROKEN, alternate.protocol) + << "Second attempt should be ignored."; +} + +} // namespace +} // namespace net diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h index 3681970..633798ec 100644 --- a/net/http/http_network_session.h +++ b/net/http/http_network_session.h @@ -9,6 +9,7 @@ #include "net/base/host_resolver.h" #include "net/base/ssl_client_auth_cache.h" #include "net/base/ssl_config_service.h" +#include "net/http/http_alternate_protocols.h" #include "net/http/http_auth_cache.h" #include "net/proxy/proxy_service.h" #include "net/socket/tcp_client_socket_pool.h" @@ -37,6 +38,13 @@ class HttpNetworkSession : public base::RefCounted<HttpNetworkSession> { return &ssl_client_auth_cache_; } + const HttpAlternateProtocols& alternate_protocols() const { + return alternate_protocols_; + } + HttpAlternateProtocols* mutable_alternate_protocols() { + return &alternate_protocols_; + } + // TCP sockets come from the tcp_socket_pool(). TCPClientSocketPool* tcp_socket_pool() { return tcp_socket_pool_; } // SSL sockets come frmo the socket_factory(). @@ -82,6 +90,7 @@ class HttpNetworkSession : public base::RefCounted<HttpNetworkSession> { HttpAuthCache auth_cache_; SSLClientAuthCache ssl_client_auth_cache_; + HttpAlternateProtocols alternate_protocols_; NetworkChangeNotifier* const network_change_notifier_; scoped_refptr<TCPClientSocketPool> tcp_socket_pool_; ClientSocketFactory* socket_factory_; diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index d42dcda..fd26195 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -138,6 +138,55 @@ void BuildTunnelRequest(const HttpRequestInfo* request_info, *request_headers += "\r\n"; } +void ProcessAlternateProtocol(const HttpResponseHeaders& headers, + const HostPortPair& http_host_port_pair, + HttpAlternateProtocols* alternate_protocols) { + std::string alternate_protocol_str; + if (!headers.EnumerateHeader(NULL, HttpAlternateProtocols::kHeader, + &alternate_protocol_str)) { + // Header is not present. + return; + } + + std::vector<std::string> port_protocol_vector; + SplitString(alternate_protocol_str, ':', &port_protocol_vector); + if (port_protocol_vector.size() != 2) { + DLOG(WARNING) << HttpAlternateProtocols::kHeader + << " header has too many tokens: " + << alternate_protocol_str; + return; + } + + int port; + if (!StringToInt(port_protocol_vector[0], &port) || + port <= 0 || port >= 1 << 16) { + DLOG(WARNING) << HttpAlternateProtocols::kHeader + << " header has unrecognizable port: " + << port_protocol_vector[0]; + return; + } + + if (port_protocol_vector[1] != HttpAlternateProtocols::kSpdyProtocol) { + // Currently, we only recognize the Spdy protocol. + DLOG(WARNING) << HttpAlternateProtocols::kHeader + << " header has unrecognized protocol: " + << port_protocol_vector[1]; + return; + } + + if (alternate_protocols->HasAlternateProtocolFor(http_host_port_pair)) { + const HttpAlternateProtocols::PortProtocolPair existing_alternate = + alternate_protocols->GetAlternateProtocolFor(http_host_port_pair); + // If we think the alternate protocol is broken, don't change it. + if (existing_alternate.protocol == HttpAlternateProtocols::BROKEN) + return; + } + + alternate_protocols->SetAlternateProtocolFor(http_host_port_pair, + port, + HttpAlternateProtocols::SPDY); +} + } // namespace //----------------------------------------------------------------------------- @@ -161,6 +210,7 @@ HttpNetworkTransaction::HttpNetworkTransaction(HttpNetworkSession* session) proxy_mode_(kDirectConnection), establishing_tunnel_(false), using_spdy_(false), + alternate_protocol_mode_(kUnspecified), embedded_identity_used_(false), read_buf_len_(0), next_state_(STATE_NONE) { @@ -652,6 +702,20 @@ int HttpNetworkTransaction::DoInitConnection() { } else { host = request_->url.HostNoBrackets(); port = request_->url.EffectiveIntPort(); + if (alternate_protocol_mode_ == kUnspecified) { + const HttpAlternateProtocols& alternate_protocols = + session_->alternate_protocols(); + if (alternate_protocols.HasAlternateProtocolFor(host, port)) { + HttpAlternateProtocols::PortProtocolPair alternate = + alternate_protocols.GetAlternateProtocolFor(host, port); + if (alternate.protocol != HttpAlternateProtocols::BROKEN) { + DCHECK_EQ(HttpAlternateProtocols::SPDY, alternate.protocol); + port = alternate.port; + using_ssl_ = true; + alternate_protocol_mode_ = kUsingAlternateProtocol; + } + } + } } // Use the fixed testing ports if they've been provided. @@ -693,8 +757,28 @@ int HttpNetworkTransaction::DoInitConnection() { } int HttpNetworkTransaction::DoInitConnectionComplete(int result) { - if (result < 0) + if (result < 0) { + if (alternate_protocol_mode_ == kUsingAlternateProtocol) { + // Mark the alternate protocol as broken and fallback. + + HostPortPair http_host_port_pair; + http_host_port_pair.host = request_->url.host(); + http_host_port_pair.port = request_->url.EffectiveIntPort(); + + session_->mutable_alternate_protocols()->MarkBrokenAlternateProtocolFor( + http_host_port_pair); + + alternate_protocol_mode_ = kDoNotUseAlternateProtocol; + + if (connection_->socket()) + connection_->socket()->Disconnect(); + connection_->Reset(); + next_state_ = STATE_INIT_CONNECTION; + return OK; + } + return ReconsiderProxyAfterError(result); + } DCHECK_EQ(OK, result); @@ -1019,6 +1103,14 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) { return OK; } + HostPortPair http_host_port_pair; + http_host_port_pair.host = request_->url.host(); + http_host_port_pair.port = request_->url.EffectiveIntPort(); + + ProcessAlternateProtocol(*response_.headers, + http_host_port_pair, + session_->mutable_alternate_protocols()); + int rv = HandleAuthChallenge(); if (rv != OK) return rv; diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h index e4eef52..80957bf 100644 --- a/net/http/http_network_transaction.h +++ b/net/http/http_network_transaction.h @@ -103,6 +103,12 @@ class HttpNetworkTransaction : public HttpTransaction { kSOCKSProxy, // If using a SOCKS proxy }; + enum AlternateProtocolMode { + kUnspecified, // Unspecified, check HttpAlternateProtocols + kUsingAlternateProtocol, // Using an alternate protocol + kDoNotUseAlternateProtocol, // Failed to connect once, do not try again. + }; + void DoCallback(int result); void OnIOComplete(int result); @@ -321,6 +327,9 @@ class HttpNetworkTransaction : public HttpTransaction { // True if this network transaction is using SPDY instead of HTTP. bool using_spdy_; + // True if this network transaction is using an alternate protocol to connect. + AlternateProtocolMode alternate_protocol_mode_; + // True if we've used the username/password embedded in the URL. This // makes sure we use the embedded identity only once for the transaction, // preventing an infinite auth restart loop. diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index 8d547cf..1cdc818 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc @@ -4252,4 +4252,174 @@ TEST_F(HttpNetworkTransactionTest, ChangeAuthRealms) { EXPECT_TRUE(response->auth_challenge.get() == NULL); } +TEST_F(HttpNetworkTransactionTest, HonorAlternateProtocolHeader) { + SessionDependencies session_deps; + + MockRead data_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead("Alternate-Protocol: 443:SPDY\r\n\r\n"), + MockRead("hello world"), + MockRead(false, OK), + }; + + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + request.load_flags = 0; + + StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0); + + session_deps.socket_factory.AddSocketDataProvider(&data); + + TestCompletionCallback callback; + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); + + int rv = trans->Start(&request, &callback, NULL); + EXPECT_EQ(ERR_IO_PENDING, rv); + + HostPortPair http_host_port_pair; + http_host_port_pair.host = "www.google.com"; + http_host_port_pair.port = 80; + const HttpAlternateProtocols& alternate_protocols = + session->alternate_protocols(); + EXPECT_FALSE( + alternate_protocols.HasAlternateProtocolFor(http_host_port_pair)); + + 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); + + ASSERT_TRUE(alternate_protocols.HasAlternateProtocolFor(http_host_port_pair)); + const HttpAlternateProtocols::PortProtocolPair alternate = + alternate_protocols.GetAlternateProtocolFor(http_host_port_pair); + HttpAlternateProtocols::PortProtocolPair expected_alternate; + expected_alternate.port = 443; + expected_alternate.protocol = HttpAlternateProtocols::SPDY; + EXPECT_TRUE(expected_alternate.Equals(alternate)); +} + +TEST_F(HttpNetworkTransactionTest, MarkBrokenAlternateProtocol) { + SessionDependencies session_deps; + + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + request.load_flags = 0; + + MockConnect mock_connect(true, ERR_CONNECTION_REFUSED); + StaticSocketDataProvider first_data; + first_data.set_connect_data(mock_connect); + session_deps.socket_factory.AddSocketDataProvider(&first_data); + + MockRead data_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n\r\n"), + MockRead("hello world"), + MockRead(true, OK), + }; + StaticSocketDataProvider second_data( + data_reads, arraysize(data_reads), NULL, 0); + session_deps.socket_factory.AddSocketDataProvider(&second_data); + + // TODO(willchan): Delete this extra data provider. It's necessary due to a + // ClientSocketPoolBaseHelper bug that starts up too many ConnectJobs: + // http://crbug.com/37454. + session_deps.socket_factory.AddSocketDataProvider(&second_data); + + TestCompletionCallback callback; + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + + HostPortPair http_host_port_pair; + http_host_port_pair.host = "www.google.com"; + http_host_port_pair.port = 80; + HttpAlternateProtocols* alternate_protocols = + session->mutable_alternate_protocols(); + alternate_protocols->SetAlternateProtocolFor( + http_host_port_pair, 1234 /* port is ignored by MockConnect anyway */, + HttpAlternateProtocols::SPDY); + + scoped_ptr<HttpTransaction> 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); + + ASSERT_TRUE( + alternate_protocols->HasAlternateProtocolFor(http_host_port_pair)); + const HttpAlternateProtocols::PortProtocolPair alternate = + alternate_protocols->GetAlternateProtocolFor(http_host_port_pair); + EXPECT_EQ(HttpAlternateProtocols::BROKEN, alternate.protocol); +} + +// TODO(willchan): Redo this test to use TLS/NPN=>SPDY. Currently, the code +// says that it does SPDY, but it just does the TLS handshake, but the NPN +// response does not indicate SPDY, so we just do standard HTTPS over the port. +// We should add code such that we don't fallback to HTTPS, but fallback to HTTP +// on the original port. +TEST_F(HttpNetworkTransactionTest, UseAlternateProtocol) { + 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\r\n"), + MockRead("hello world"), + MockRead(true, OK), + }; + StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0); + session_deps.socket_factory.AddSocketDataProvider(&data); + + SSLSocketDataProvider ssl(true, OK); + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl); + + TestCompletionCallback callback; + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + + HostPortPair http_host_port_pair; + http_host_port_pair.host = "www.google.com"; + http_host_port_pair.port = 80; + HttpAlternateProtocols* alternate_protocols = + session->mutable_alternate_protocols(); + alternate_protocols->SetAlternateProtocolFor( + http_host_port_pair, 1234 /* port is ignored */, + HttpAlternateProtocols::SPDY); + + scoped_ptr<HttpTransaction> 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); +} + } // namespace net diff --git a/net/net.gyp b/net/net.gyp index 0addbbf..ddb42d5 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -64,6 +64,8 @@ 'base/gzip_header.h', 'base/host_cache.cc', 'base/host_cache.h', + 'base/host_port_pair.cc', + 'base/host_port_pair.h', 'base/host_resolver.cc', 'base/host_resolver.h', 'base/host_resolver_impl.cc', @@ -307,6 +309,8 @@ 'ftp/ftp_util.h', 'http/des.cc', 'http/des.h', + 'http/http_alternate_protocols.cc', + 'http/http_alternate_protocols.h', 'http/http_atom_list.h', 'http/http_auth.cc', 'http/http_auth.h', @@ -647,6 +651,7 @@ 'ftp/ftp_network_transaction_unittest.cc', 'ftp/ftp_util_unittest.cc', 'http/des_unittest.cc', + 'http/http_alternate_protocols_unittest.cc', 'http/http_auth_cache_unittest.cc', 'http/http_auth_filter_unittest.cc', 'http/http_auth_handler_basic_unittest.cc', diff --git a/net/spdy/spdy_session_pool.h b/net/spdy/spdy_session_pool.h index 8c50577..db4ec42 100644 --- a/net/spdy/spdy_session_pool.h +++ b/net/spdy/spdy_session_pool.h @@ -12,6 +12,7 @@ #include "base/basictypes.h" #include "base/ref_counted.h" #include "base/scoped_ptr.h" +#include "net/base/host_port_pair.h" namespace net { @@ -19,23 +20,6 @@ class ClientSocketHandle; class HttpNetworkSession; class SpdySession; -// TODO(willchan): Move this to net/base. -struct HostPortPair { - HostPortPair() {} - HostPortPair(const std::string& in_host, uint16 in_port) - : host(in_host), port(in_port) {} - - // Comparator function so this can be placed in a std::map. - bool operator<(const HostPortPair& other) const { - if (host != other.host) - return host < other.host; - return port < other.port; - } - - std::string host; - uint16 port; -}; - // This is a very simple pool for open SpdySessions. // TODO(mbelshe): Make this production ready. class SpdySessionPool : public base::RefCounted<SpdySessionPool> { |