summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/browser_main.cc6
-rw-r--r--net/http/http_auth_controller.cc1
-rw-r--r--net/http/http_basic_stream.cc21
-rw-r--r--net/http/http_basic_stream.h10
-rw-r--r--net/http/http_network_layer.cc20
-rw-r--r--net/http/http_network_session.cc2
-rw-r--r--net/http/http_network_session.h6
-rw-r--r--net/http/http_network_transaction.cc1202
-rw-r--r--net/http/http_network_transaction.h137
-rw-r--r--net/http/http_network_transaction_unittest.cc120
-rw-r--r--net/http/http_proxy_client_socket.h2
-rw-r--r--net/http/http_stream.h34
-rw-r--r--net/http/http_stream_factory.cc133
-rw-r--r--net/http/http_stream_factory.h113
-rw-r--r--net/http/http_stream_handle.h106
-rw-r--r--net/http/http_stream_parser.cc42
-rw-r--r--net/http/http_stream_parser.h12
-rw-r--r--net/http/http_stream_request.cc916
-rw-r--r--net/http/http_stream_request.h205
-rw-r--r--net/http/stream_factory.h150
-rw-r--r--net/net.gyp6
-rw-r--r--net/socket/client_socket_handle.h13
-rw-r--r--net/spdy/spdy_http_stream.cc28
-rw-r--r--net/spdy/spdy_http_stream.h17
-rw-r--r--net/spdy/spdy_network_transaction_unittest.cc47
-rw-r--r--net/spdy/spdy_session.cc11
-rw-r--r--net/spdy/spdy_session.h13
-rw-r--r--net/spdy/spdy_stream.cc4
-rw-r--r--net/spdy/spdy_stream.h6
29 files changed, 2230 insertions, 1153 deletions
diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc
index 9730dbe..fb7c6c3 100644
--- a/chrome/browser/browser_main.cc
+++ b/chrome/browser/browser_main.cc
@@ -83,7 +83,7 @@
#include "net/base/network_change_notifier.h"
#include "net/http/http_network_layer.h"
#include "net/http/http_network_session.h"
-#include "net/http/http_network_transaction.h"
+#include "net/http/http_stream_factory.h"
#include "net/socket/client_socket_pool_base.h"
#include "net/spdy/spdy_session_pool.h"
@@ -494,10 +494,10 @@ void InitializeNetworkOptions(const CommandLine& parsed_command_line) {
}
if (parsed_command_line.HasSwitch(switches::kIgnoreCertificateErrors))
- net::HttpNetworkTransaction::IgnoreCertificateErrors(true);
+ net::HttpStreamFactory::set_ignore_certificate_errors(true);
if (parsed_command_line.HasSwitch(switches::kHostRules))
- net::HttpNetworkTransaction::SetHostMappingRules(
+ net::HttpStreamFactory::SetHostMappingRules(
parsed_command_line.GetSwitchValueASCII(switches::kHostRules));
if (parsed_command_line.HasSwitch(switches::kMaxSpdySessionsPerDomain)) {
diff --git a/net/http/http_auth_controller.cc b/net/http/http_auth_controller.cc
index 71e08a3..e753b59 100644
--- a/net/http/http_auth_controller.cc
+++ b/net/http/http_auth_controller.cc
@@ -8,6 +8,7 @@
#include "net/base/auth.h"
#include "net/base/host_resolver.h"
#include "net/base/net_util.h"
+#include "net/http/http_auth_handler.h"
#include "net/http/http_auth_handler_factory.h"
#include "net/http/http_network_session.h"
#include "net/http/http_request_headers.h"
diff --git a/net/http/http_basic_stream.cc b/net/http/http_basic_stream.cc
index 0e71b45..0543112 100644
--- a/net/http/http_basic_stream.cc
+++ b/net/http/http_basic_stream.cc
@@ -52,6 +52,10 @@ int HttpBasicStream::ReadResponseBody(IOBuffer* buf, int buf_len,
return parser_->ReadResponseBody(buf, buf_len, callback);
}
+void HttpBasicStream::Close(bool not_reusable) {
+ parser_->Close(not_reusable);
+}
+
bool HttpBasicStream::IsResponseBodyComplete() const {
return parser_->IsResponseBodyComplete();
}
@@ -64,4 +68,21 @@ bool HttpBasicStream::IsMoreDataBuffered() const {
return parser_->IsMoreDataBuffered();
}
+bool HttpBasicStream::IsConnectionReused() const {
+ return parser_->IsConnectionReused();
+}
+
+void HttpBasicStream::SetConnectionReused() {
+ parser_->SetConnectionReused();
+}
+
+void HttpBasicStream::GetSSLInfo(SSLInfo* ssl_info) {
+ parser_->GetSSLInfo(ssl_info);
+}
+
+void HttpBasicStream::GetSSLCertRequestInfo(
+ SSLCertRequestInfo* cert_request_info) {
+ parser_->GetSSLCertRequestInfo(cert_request_info);
+}
+
} // namespace net
diff --git a/net/http/http_basic_stream.h b/net/http/http_basic_stream.h
index 0c1cbca..f6192e4 100644
--- a/net/http/http_basic_stream.h
+++ b/net/http/http_basic_stream.h
@@ -51,12 +51,22 @@ class HttpBasicStream : public HttpStream {
virtual int ReadResponseBody(IOBuffer* buf, int buf_len,
CompletionCallback* callback);
+ virtual void Close(bool not_reusable);
+
virtual bool IsResponseBodyComplete() const;
virtual bool CanFindEndOfResponse() const;
virtual bool IsMoreDataBuffered() const;
+ virtual bool IsConnectionReused() const;
+
+ virtual void SetConnectionReused();
+
+ virtual void GetSSLInfo(SSLInfo* ssl_info);
+
+ virtual void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info);
+
private:
scoped_refptr<GrowableIOBuffer> read_buf_;
diff --git a/net/http/http_network_layer.cc b/net/http/http_network_layer.cc
index c73410a..24c410e 100644
--- a/net/http/http_network_layer.cc
+++ b/net/http/http_network_layer.cc
@@ -187,27 +187,27 @@ void HttpNetworkLayer::EnableSpdy(const std::string& mode) {
const std::string& option = *it;
if (option == kDisableSSL) {
SpdySession::SetSSLMode(false); // Disable SSL
- HttpNetworkTransaction::SetUseSSLOverSpdyWithoutNPN(false);
- HttpNetworkTransaction::SetUseSpdyWithoutNPN(true);
+ HttpStreamFactory::set_force_spdy_over_ssl(false);
+ HttpStreamFactory::set_force_spdy_always(true);
} else if (option == kSSL) {
- HttpNetworkTransaction::SetUseSSLOverSpdyWithoutNPN(true);
- HttpNetworkTransaction::SetUseSpdyWithoutNPN(true);
+ HttpStreamFactory::set_force_spdy_over_ssl(true);
+ HttpStreamFactory::set_force_spdy_always(true);
} else if (option == kDisableCompression) {
spdy::SpdyFramer::set_enable_compression_default(false);
} else if (option == kEnableNPN) {
- HttpNetworkTransaction::SetUseAlternateProtocols(use_alt_protocols);
- HttpNetworkTransaction::SetNextProtos(kNpnProtosFull);
+ HttpStreamFactory::set_use_alternate_protocols(use_alt_protocols);
+ HttpStreamFactory::set_next_protos(kNpnProtosFull);
} else if (option == kEnableNpnHttpOnly) {
// Avoid alternate protocol in this case. Otherwise, browser will try SSL
// and then fallback to http. This introduces extra load.
- HttpNetworkTransaction::SetUseAlternateProtocols(false);
- HttpNetworkTransaction::SetNextProtos(kNpnProtosHttpOnly);
+ HttpStreamFactory::set_use_alternate_protocols(false);
+ HttpStreamFactory::set_next_protos(kNpnProtosHttpOnly);
} else if (option == kEnableVersionOne) {
spdy::SpdyFramer::set_protocol_version(1);
- HttpNetworkTransaction::SetNextProtos(kNpnProtosFullV1);
+ HttpStreamFactory::set_next_protos(kNpnProtosFullV1);
} else if (option == kDisableAltProtocols) {
use_alt_protocols = false;
- HttpNetworkTransaction::SetUseAlternateProtocols(false);
+ HttpStreamFactory::set_use_alternate_protocols(false);
} else if (option == kEnableFlowControl) {
SpdySession::SetFlowControl(true);
} else if (option.empty() && it == spdy_options.begin()) {
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc
index e49d66b..d85a4ef 100644
--- a/net/http/http_network_session.cc
+++ b/net/http/http_network_session.cc
@@ -32,6 +32,7 @@ int g_max_sockets_per_proxy_server = 32;
} // namespace
+// TODO(mbelshe): Move the socket factories into HttpStreamFactory.
HttpNetworkSession::HttpNetworkSession(
HostResolver* host_resolver,
ProxyService* proxy_service,
@@ -61,6 +62,7 @@ HttpNetworkSession::HttpNetworkSession(
proxy_service_(proxy_service),
ssl_config_service_(ssl_config_service),
spdy_session_pool_(spdy_session_pool),
+ http_stream_factory_(new HttpStreamFactory()),
http_auth_handler_factory_(http_auth_handler_factory),
network_delegate_(network_delegate),
net_log_(net_log) {
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h
index 650a831..ae1738e 100644
--- a/net/http/http_network_session.h
+++ b/net/http/http_network_session.h
@@ -19,6 +19,7 @@
#include "net/http/http_network_delegate.h"
#include "net/http/http_network_transaction.h"
#include "net/http/http_proxy_client_socket_pool.h"
+#include "net/http/http_stream_factory.h"
#include "net/proxy/proxy_service.h"
#include "net/socket/client_socket_pool_histograms.h"
#include "net/socket/socks_client_socket_pool.h"
@@ -101,6 +102,10 @@ class HttpNetworkSession : public base::RefCounted<HttpNetworkSession>,
return network_delegate_;
}
+ const scoped_refptr<HttpStreamFactory>& http_stream_factory() {
+ return http_stream_factory_;
+ }
+
static int max_sockets_per_group();
static void set_max_sockets_per_group(int socket_count);
static void set_max_sockets_per_proxy_server(int socket_count);
@@ -164,6 +169,7 @@ class HttpNetworkSession : public base::RefCounted<HttpNetworkSession>,
scoped_refptr<ProxyService> proxy_service_;
scoped_refptr<SSLConfigService> ssl_config_service_;
scoped_refptr<SpdySessionPool> spdy_session_pool_;
+ scoped_refptr<HttpStreamFactory> http_stream_factory_;
HttpAuthHandlerFactory* http_auth_handler_factory_;
HttpNetworkDelegate* const network_delegate_;
NetLog* net_log_;
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index 6b32d2f..74c2833 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -19,8 +19,6 @@
#include "build/build_config.h"
#include "googleurl/src/gurl.h"
#include "net/base/auth.h"
-#include "net/base/connection_type_histograms.h"
-#include "net/base/host_mapping_rules.h"
#include "net/base/io_buffer.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
@@ -41,6 +39,8 @@
#include "net/http/http_request_info.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_response_info.h"
+#include "net/http/http_stream_handle.h"
+#include "net/http/http_stream_request.h"
#include "net/http/http_util.h"
#include "net/http/url_security_manager.h"
#include "net/socket/client_socket_factory.h"
@@ -58,16 +58,6 @@ namespace net {
namespace {
-const HostMappingRules* g_host_mapping_rules = NULL;
-const std::string* g_next_protos = NULL;
-bool g_use_alternate_protocols = false;
-bool g_want_ssl_over_spdy_without_npn = true;
-bool g_want_spdy_without_npn = false;
-
-// A set of host:port strings. These are servers which we have needed to back
-// off to SSLv3 for.
-std::set<std::string>* g_tls_intolerant_servers = NULL;
-
void BuildRequestHeaders(const HttpRequestInfo* request_info,
const HttpRequestHeaders& authorization_headers,
const UploadDataStream* upload_data_stream,
@@ -137,81 +127,27 @@ void BuildRequestHeaders(const HttpRequestInfo* request_info,
request_headers->MergeFrom(stripped_extra_headers);
}
-void ProcessAlternateProtocol(const HttpResponseHeaders& headers,
- const HostPortPair& http_host_port_pair,
- HttpAlternateProtocols* alternate_protocols) {
+void ProcessAlternateProtocol(HttpStreamFactory* factory,
+ HttpAlternateProtocols* alternate_protocols,
+ const HttpResponseHeaders& headers,
+ const HostPortPair& http_host_port_pair) {
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 (!base::StringToInt(port_protocol_vector[0], &port) ||
- port <= 0 || port >= 1 << 16) {
- DLOG(WARNING) << HttpAlternateProtocols::kHeader
- << " header has unrecognizable port: "
- << port_protocol_vector[0];
- return;
- }
-
- HttpAlternateProtocols::Protocol protocol = HttpAlternateProtocols::BROKEN;
- for (int i = HttpAlternateProtocols::NPN_SPDY_1;
- i < HttpAlternateProtocols::NUM_ALTERNATE_PROTOCOLS; ++i) {
- if (port_protocol_vector[1] == HttpAlternateProtocols::kProtocolStrings[i])
- protocol = static_cast<HttpAlternateProtocols::Protocol>(i);
- }
-
- if (protocol == HttpAlternateProtocols::BROKEN) {
- // Currently, we only recognize the npn-spdy protocol.
- DLOG(WARNING) << HttpAlternateProtocols::kHeader
- << " header has unrecognized protocol: "
- << port_protocol_vector[1];
- return;
- }
-
- HostPortPair host_port(http_host_port_pair);
- if (g_host_mapping_rules)
- g_host_mapping_rules->RewriteHost(&host_port);
-
- if (alternate_protocols->HasAlternateProtocolFor(host_port)) {
- const HttpAlternateProtocols::PortProtocolPair existing_alternate =
- alternate_protocols->GetAlternateProtocolFor(host_port);
- // If we think the alternate protocol is broken, don't change it.
- if (existing_alternate.protocol == HttpAlternateProtocols::BROKEN)
- return;
- }
-
- alternate_protocols->SetAlternateProtocolFor(host_port, port, protocol);
-}
-
-GURL UpgradeUrlToHttps(const GURL& original_url) {
- GURL::Replacements replacements;
- // new_sheme and new_port need to be in scope here because GURL::Replacements
- // references the memory contained by them directly.
- const std::string new_scheme = "https";
- const std::string new_port = base::IntToString(443);
- replacements.SetSchemeStr(new_scheme);
- replacements.SetPortStr(new_port);
- return original_url.ReplaceComponents(replacements);
+ factory->ProcessAlternateProtocol(alternate_protocols,
+ alternate_protocol_str,
+ http_host_port_pair);
}
} // namespace
//-----------------------------------------------------------------------------
-bool HttpNetworkTransaction::g_ignore_certificate_errors = false;
-
HttpNetworkTransaction::HttpNetworkTransaction(HttpNetworkSession* session)
: pending_auth_target_(HttpAuth::AUTH_NONE),
ALLOW_THIS_IN_INITIALIZER_LIST(
@@ -219,61 +155,16 @@ HttpNetworkTransaction::HttpNetworkTransaction(HttpNetworkSession* session)
user_callback_(NULL),
session_(session),
request_(NULL),
- pac_request_(NULL),
- connection_(new ClientSocketHandle),
reused_socket_(false),
headers_valid_(false),
logged_response_time_(false),
- using_ssl_(false),
- using_spdy_(false),
- want_spdy_without_npn_(g_want_spdy_without_npn),
- want_ssl_over_spdy_without_npn_(g_want_ssl_over_spdy_without_npn),
- spdy_certificate_error_(OK),
- alternate_protocol_mode_(
- g_use_alternate_protocols ? kUnspecified :
- kDoNotUseAlternateProtocol),
read_buf_len_(0),
next_state_(STATE_NONE),
establishing_tunnel_(false) {
session->ssl_config_service()->GetSSLConfig(&ssl_config_);
- if (g_next_protos)
- ssl_config_.next_protos = *g_next_protos;
- if (!g_tls_intolerant_servers)
- g_tls_intolerant_servers = new std::set<std::string>;
-}
-
-// static
-void HttpNetworkTransaction::SetHostMappingRules(const std::string& rules) {
- HostMappingRules* host_mapping_rules = new HostMappingRules();
- host_mapping_rules->SetRulesFromString(rules);
- delete g_host_mapping_rules;
- g_host_mapping_rules = host_mapping_rules;
-}
-
-// static
-void HttpNetworkTransaction::SetUseAlternateProtocols(bool value) {
- g_use_alternate_protocols = value;
-}
-
-// static
-void HttpNetworkTransaction::SetUseSSLOverSpdyWithoutNPN(bool value) {
- g_want_ssl_over_spdy_without_npn = value;
-}
-
-// static
-void HttpNetworkTransaction::SetUseSpdyWithoutNPN(bool value) {
- g_want_spdy_without_npn = value;
-}
-
-// static
-void HttpNetworkTransaction::SetNextProtos(const std::string& next_protos) {
- delete g_next_protos;
- g_next_protos = new std::string(next_protos);
-}
+ if (session->http_stream_factory()->next_protos())
+ ssl_config_.next_protos = *session->http_stream_factory()->next_protos();
-// static
-void HttpNetworkTransaction::IgnoreCertificateErrors(bool enabled) {
- g_ignore_certificate_errors = enabled;
}
int HttpNetworkTransaction::Start(const HttpRequestInfo* request_info,
@@ -285,7 +176,7 @@ int HttpNetworkTransaction::Start(const HttpRequestInfo* request_info,
request_ = request_info;
start_time_ = base::Time::Now();
- next_state_ = STATE_RESOLVE_PROXY;
+ next_state_ = STATE_INIT_STREAM;
int rv = DoLoop(OK);
if (rv == ERR_IO_PENDING)
user_callback_ = callback;
@@ -294,21 +185,12 @@ int HttpNetworkTransaction::Start(const HttpRequestInfo* request_info,
int HttpNetworkTransaction::RestartIgnoringLastError(
CompletionCallback* callback) {
- if (connection_->socket() && connection_->socket()->IsConnectedAndIdle()) {
- // TODO(wtc): Should we update any of the connection histograms that we
- // update in DoSSLConnectComplete if |result| is OK?
- if (using_spdy_) {
- // TODO(cbentzel): Add auth support to spdy. See http://crbug.com/46620
- next_state_ = STATE_INIT_STREAM;
- } else {
- next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN;
- }
- } else {
- if (connection_->socket())
- connection_->socket()->Disconnect();
- connection_->Reset();
- next_state_ = STATE_INIT_CONNECTION;
- }
+ DCHECK(!stream_.get());
+ DCHECK(!stream_request_.get());
+ DCHECK_EQ(STATE_NONE, next_state_);
+
+ next_state_ = STATE_INIT_STREAM;
+
int rv = DoLoop(OK);
if (rv == ERR_IO_PENDING)
user_callback_ = callback;
@@ -318,16 +200,22 @@ int HttpNetworkTransaction::RestartIgnoringLastError(
int HttpNetworkTransaction::RestartWithCertificate(
X509Certificate* client_cert,
CompletionCallback* callback) {
+ // In HandleCertificateRequest(), we always tear down existing stream
+ // requests to force a new connection. So we shouldn't have one here.
+ DCHECK(!stream_request_.get());
+ DCHECK(!stream_.get());
+ DCHECK_EQ(STATE_NONE, next_state_);
+
ssl_config_.client_cert = client_cert;
if (client_cert) {
session_->ssl_client_auth_cache()->Add(GetHostAndPort(request_->url),
client_cert);
}
ssl_config_.send_client_cert = true;
- next_state_ = STATE_INIT_CONNECTION;
// Reset the other member variables.
// Note: this is necessary only with SSL renegotiation.
ResetStateForRestart();
+ next_state_ = STATE_INIT_STREAM;
int rv = DoLoop(OK);
if (rv == ERR_IO_PENDING)
user_callback_ = callback;
@@ -347,26 +235,34 @@ int HttpNetworkTransaction::RestartWithAuth(
auth_controllers_[target]->ResetAuth(username, password);
- if (target == HttpAuth::AUTH_PROXY && using_ssl_ && proxy_info_.is_http()) {
- DCHECK(establishing_tunnel_);
- next_state_ = STATE_RESTART_TUNNEL_AUTH;
+ DCHECK(user_callback_ == NULL);
+
+ int rv = OK;
+ if (target == HttpAuth::AUTH_PROXY && establishing_tunnel_) {
+ // In this case, we've gathered credentials for use with proxy
+ // authentication of a tunnel.
+ DCHECK_EQ(STATE_INIT_STREAM_COMPLETE, next_state_);
+ DCHECK(stream_request_ != NULL);
auth_controllers_[target] = NULL;
ResetStateForRestart();
+ rv = stream_request_->RestartTunnelWithProxyAuth(username, password);
} else {
+ // In this case, we've gathered credentials for the server or the proxy
+ // but it is not during the tunneling phase.
+ DCHECK(stream_request_ == NULL);
PrepareForAuthRestart(target);
+ rv = DoLoop(OK);
}
- DCHECK(user_callback_ == NULL);
- int rv = DoLoop(OK);
if (rv == ERR_IO_PENDING)
user_callback_ = callback;
-
return rv;
}
void HttpNetworkTransaction::PrepareForAuthRestart(HttpAuth::Target target) {
DCHECK(HaveAuth(target));
- DCHECK(!establishing_tunnel_);
+ DCHECK(!stream_request_.get());
+
bool keep_alive = false;
// Even if the server says the connection is keep-alive, we have to be
// able to find the end of each response in order to reuse the connection.
@@ -389,23 +285,27 @@ void HttpNetworkTransaction::PrepareForAuthRestart(HttpAuth::Target target) {
}
void HttpNetworkTransaction::DidDrainBodyForAuthRestart(bool keep_alive) {
- DCHECK(!establishing_tunnel_);
- if (keep_alive && connection_->socket()->IsConnectedAndIdle()) {
- // We should call connection_->set_idle_time(), but this doesn't occur
- // often enough to be worth the trouble.
- next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN;
- connection_->set_is_reused(true);
- reused_socket_ = true;
- } else {
- next_state_ = STATE_INIT_CONNECTION;
- connection_->socket()->Disconnect();
- connection_->Reset();
+ DCHECK(!stream_request_.get());
+
+ if (stream_.get()) {
+ if (keep_alive) {
+ // We should call connection_->set_idle_time(), but this doesn't occur
+ // often enough to be worth the trouble.
+ stream_->SetConnectionReused();
+ }
+ stream_->Close(!keep_alive);
+ next_state_ = STATE_INIT_STREAM;
}
// Reset the other member variables.
ResetStateForRestart();
}
+bool HttpNetworkTransaction::IsReadyToRestartForAuth() {
+ return pending_auth_target_ != HttpAuth::AUTH_NONE &&
+ HaveAuth(pending_auth_target_);
+}
+
int HttpNetworkTransaction::Read(IOBuffer* buf, int buf_len,
CompletionCallback* callback) {
DCHECK(buf);
@@ -414,7 +314,7 @@ int HttpNetworkTransaction::Read(IOBuffer* buf, int buf_len,
State next_state = STATE_NONE;
scoped_refptr<HttpResponseHeaders> headers = GetResponseHeaders();
- if (headers_valid_ && headers.get() && establishing_tunnel_) {
+ if (headers_valid_ && headers.get() && stream_request_.get()) {
// We're trying to read the body of the response but we're still trying
// to establish an SSL tunnel through the proxy. We can't read these
// bytes when establishing a tunnel because they might be controlled by
@@ -433,9 +333,6 @@ int HttpNetworkTransaction::Read(IOBuffer* buf, int buf_len,
// Are we using SPDY or HTTP?
next_state = STATE_READ_BODY;
DCHECK(stream_->GetResponseInfo()->headers);
- if (!using_spdy_ && !connection_->is_initialized()) {
- return 0; // |*connection_| has been reset. Treat like EOF.
- }
read_buf_ = buf;
read_buf_len_ = buf_len;
@@ -456,14 +353,11 @@ LoadState HttpNetworkTransaction::GetLoadState() const {
// TODO(wtc): Define a new LoadState value for the
// STATE_INIT_CONNECTION_COMPLETE state, which delays the HTTP request.
switch (next_state_) {
- case STATE_RESOLVE_PROXY_COMPLETE:
- return LOAD_STATE_RESOLVING_PROXY_FOR_URL;
- case STATE_INIT_CONNECTION_COMPLETE:
- return connection_->GetLoadState();
+ case STATE_INIT_STREAM_COMPLETE:
+ return stream_request_->GetLoadState();
case STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE:
case STATE_GENERATE_SERVER_AUTH_TOKEN_COMPLETE:
case STATE_SEND_REQUEST_COMPLETE:
- case STATE_INIT_STREAM_COMPLETE:
return LOAD_STATE_SENDING_REQUEST;
case STATE_READ_HEADERS_COMPLETE:
return LOAD_STATE_WAITING_FOR_RESPONSE;
@@ -481,28 +375,98 @@ uint64 HttpNetworkTransaction::GetUploadProgress() const {
return stream_->GetUploadProgress();
}
+void HttpNetworkTransaction::OnStreamReady(HttpStreamHandle* stream) {
+ DCHECK_EQ(STATE_INIT_STREAM_COMPLETE, next_state_);
+ DCHECK(stream_request_.get());
+
+ stream_.reset(stream);
+ response_.was_alternate_protocol_available =
+ stream_request_->was_alternate_protocol_available();
+ response_.was_npn_negotiated = stream_request_->was_npn_negotiated();
+ response_.was_fetched_via_spdy = stream_request_->using_spdy();
+ response_.was_fetched_via_proxy = !proxy_info_.is_direct();
+ reused_socket_ = stream->IsConnectionReused();
+
+ if (is_https_request())
+ stream->GetSSLInfo(&response_.ssl_info);
+
+ OnIOComplete(OK);
+}
+
+void HttpNetworkTransaction::OnStreamFailed(int result) {
+ DCHECK_EQ(STATE_INIT_STREAM_COMPLETE, next_state_);
+ DCHECK_NE(OK, result);
+ DCHECK(stream_request_.get());
+ DCHECK(!stream_.get());
+
+ OnIOComplete(result);
+}
+
+void HttpNetworkTransaction::OnCertificateError(int result,
+ const SSLInfo& ssl_info) {
+ DCHECK_EQ(STATE_INIT_STREAM_COMPLETE, next_state_);
+ DCHECK_NE(OK, result);
+ DCHECK(stream_request_.get());
+ DCHECK(!stream_.get());
+
+ response_.ssl_info = ssl_info;
+
+ // TODO(mbelshe): For now, we're going to pass the error through, and that
+ // will close the stream_request in all cases. This means that we're always
+ // going to restart an entire INIT_STREAM, even if the connection is good and
+ // the user chooses to ignore the error. This is not ideal, but not the end
+ // of the world either.
+
+ OnIOComplete(result);
+}
+
+void HttpNetworkTransaction::OnNeedsProxyAuth(
+ const scoped_refptr<HttpAuthController>& auth_controller,
+ const HttpResponseInfo& proxy_response) {
+ DCHECK(stream_request_.get());
+ DCHECK_EQ(STATE_INIT_STREAM_COMPLETE, next_state_);
+
+ establishing_tunnel_ = true;
+ response_.headers = proxy_response.headers;
+ response_.auth_challenge = proxy_response.auth_challenge;
+ headers_valid_ = true;
+
+ auth_controllers_[HttpAuth::AUTH_PROXY] = auth_controller;
+ pending_auth_target_ = HttpAuth::AUTH_PROXY;
+
+ DoCallback(OK);
+}
+
+void HttpNetworkTransaction::OnNeedsClientAuth(
+ const scoped_refptr<SSLCertRequestInfo>& cert_info) {
+ DCHECK_EQ(STATE_INIT_STREAM_COMPLETE, next_state_);
+
+ response_.cert_request_info = cert_info;
+ int result = HandleCertificateRequest(ERR_SSL_CLIENT_AUTH_CERT_NEEDED);
+ DoCallback(result);
+}
+
HttpNetworkTransaction::~HttpNetworkTransaction() {
- // If we still have an open socket, then make sure to disconnect it so it
- // won't call us back and we don't try to reuse it later on. However,
- // don't close the socket if we should keep the connection alive.
- if (connection_.get() && connection_->is_initialized()) {
- // The STATE_NONE check guarantees there are no pending socket IOs that
- // could try to call this object back after it is deleted.
+ if (stream_.get()) {
+ HttpResponseHeaders* headers = GetResponseHeaders();
+ // TODO(mbelshe): The stream_ should be able to compute whether or not the
+ // stream should be kept alive. No reason to compute here
+ // and pass it in.
bool keep_alive = next_state_ == STATE_NONE &&
- !using_spdy_ &&
- stream_.get() &&
stream_->IsResponseBodyComplete() &&
stream_->CanFindEndOfResponse() &&
- GetResponseHeaders()->IsKeepAlive();
- if (!keep_alive)
- connection_->socket()->Disconnect();
+ (!headers || headers->IsKeepAlive());
+ stream_->Close(!keep_alive);
}
- if (pac_request_)
- session_->proxy_service()->CancelPacRequest(pac_request_);
+ if (stream_request_.get()) {
+ stream_request_->Cancel();
+ stream_request_ = NULL;
+ }
+}
- if (using_spdy_ && stream_.get())
- static_cast<SpdyHttpStream*>(stream_.get())->Cancel();
+bool HttpNetworkTransaction::is_https_request() const {
+ return request_->url.SchemeIs("https");
}
void HttpNetworkTransaction::DoCallback(int rv) {
@@ -529,20 +493,6 @@ int HttpNetworkTransaction::DoLoop(int result) {
State state = next_state_;
next_state_ = STATE_NONE;
switch (state) {
- case STATE_RESOLVE_PROXY:
- DCHECK_EQ(OK, rv);
- rv = DoResolveProxy();
- break;
- case STATE_RESOLVE_PROXY_COMPLETE:
- rv = DoResolveProxyComplete(rv);
- break;
- case STATE_INIT_CONNECTION:
- DCHECK_EQ(OK, rv);
- rv = DoInitConnection();
- break;
- case STATE_INIT_CONNECTION_COMPLETE:
- rv = DoInitConnectionComplete(rv);
- break;
case STATE_INIT_STREAM:
DCHECK_EQ(OK, rv);
rv = DoInitStream();
@@ -550,13 +500,6 @@ int HttpNetworkTransaction::DoLoop(int result) {
case STATE_INIT_STREAM_COMPLETE:
rv = DoInitStreamComplete(rv);
break;
- case STATE_RESTART_TUNNEL_AUTH:
- DCHECK_EQ(OK, rv);
- rv = DoRestartTunnelAuth();
- break;
- case STATE_RESTART_TUNNEL_AUTH_COMPLETE:
- rv = DoRestartTunnelAuthComplete(rv);
- break;
case STATE_GENERATE_PROXY_AUTH_TOKEN:
DCHECK_EQ(OK, rv);
rv = DoGenerateProxyAuthToken();
@@ -619,429 +562,28 @@ int HttpNetworkTransaction::DoLoop(int result) {
return rv;
}
-int HttpNetworkTransaction::DoResolveProxy() {
- DCHECK(!pac_request_);
-
- next_state_ = STATE_RESOLVE_PROXY_COMPLETE;
-
- // |endpoint_| indicates the final destination endpoint.
- endpoint_ = HostPortPair(request_->url.HostNoBrackets(),
- request_->url.EffectiveIntPort());
-
- // Extra URL we might be attempting to resolve to.
- GURL alternate_endpoint_url;
-
- // Tracks whether we are using |request_->url| or |alternate_endpoint_url|.
- const GURL *curr_endpoint_url = &request_->url;
-
- if (g_host_mapping_rules && g_host_mapping_rules->RewriteHost(&endpoint_)) {
- url_canon::Replacements<char> replacements;
- const std::string port_str = base::IntToString(endpoint_.port());
- replacements.SetPort(port_str.c_str(),
- url_parse::Component(0, port_str.size()));
- replacements.SetHost(endpoint_.host().c_str(),
- url_parse::Component(0, endpoint_.host().size()));
- alternate_endpoint_url = curr_endpoint_url->ReplaceComponents(replacements);
- curr_endpoint_url = &alternate_endpoint_url;
- }
-
- const HttpAlternateProtocols& alternate_protocols =
- session_->alternate_protocols();
- if (alternate_protocols.HasAlternateProtocolFor(endpoint_)) {
- response_.was_alternate_protocol_available = true;
- if (alternate_protocol_mode_ == kUnspecified) {
- HttpAlternateProtocols::PortProtocolPair alternate =
- alternate_protocols.GetAlternateProtocolFor(endpoint_);
- if (alternate.protocol != HttpAlternateProtocols::BROKEN) {
- DCHECK_LE(HttpAlternateProtocols::NPN_SPDY_1, alternate.protocol);
- DCHECK_GT(HttpAlternateProtocols::NUM_ALTERNATE_PROTOCOLS,
- alternate.protocol);
- endpoint_.set_port(alternate.port);
- alternate_protocol_ = alternate.protocol;
- alternate_protocol_mode_ = kUsingAlternateProtocol;
- alternate_endpoint_url = UpgradeUrlToHttps(*curr_endpoint_url);
- curr_endpoint_url = &alternate_endpoint_url;
- }
- }
- }
-
- if (request_->load_flags & LOAD_BYPASS_PROXY) {
- proxy_info_.UseDirect();
- return OK;
- }
-
- return session_->proxy_service()->ResolveProxy(
- *curr_endpoint_url, &proxy_info_, &io_callback_, &pac_request_, net_log_);
-}
-
-int HttpNetworkTransaction::DoResolveProxyComplete(int result) {
- pac_request_ = NULL;
-
- if (result != OK)
- return result;
-
- // Remove unsupported proxies from the list.
- proxy_info_.RemoveProxiesWithoutScheme(
- ProxyServer::SCHEME_DIRECT | ProxyServer::SCHEME_HTTP |
- ProxyServer::SCHEME_SOCKS4 | ProxyServer::SCHEME_SOCKS5);
-
- if (proxy_info_.is_empty()) {
- // No proxies/direct to choose from. This happens when we don't support any
- // of the proxies in the returned list.
- return ERR_NO_SUPPORTED_PROXIES;
- }
-
- next_state_ = STATE_INIT_CONNECTION;
- return OK;
-}
-
-int HttpNetworkTransaction::DoInitConnection() {
- DCHECK(!connection_->is_initialized());
- DCHECK(proxy_info_.proxy_server().is_valid());
- next_state_ = STATE_INIT_CONNECTION_COMPLETE;
-
- bool want_spdy_over_npn = alternate_protocol_mode_ == kUsingAlternateProtocol
- && alternate_protocol_ == HttpAlternateProtocols::NPN_SPDY_2;
- using_ssl_ = request_->url.SchemeIs("https") ||
- (want_spdy_without_npn_ && want_ssl_over_spdy_without_npn_) ||
- want_spdy_over_npn;
- using_spdy_ = false;
- response_.was_fetched_via_proxy = !proxy_info_.is_direct();
-
- // Check first if we have a spdy session for this group. If so, then go
- // straight to using that.
- HostPortProxyPair pair(endpoint_, proxy_info_.ToPacString());
- if (session_->spdy_session_pool()->HasSession(pair)) {
- using_spdy_ = true;
- reused_socket_ = true;
- next_state_ = STATE_INIT_STREAM;
- return OK;
- }
-
- // Build the string used to uniquely identify connections of this type.
- // Determine the host and port to connect to.
- std::string connection_group = endpoint_.ToString();
- DCHECK(!connection_group.empty());
-
- if (using_ssl_)
- connection_group = StringPrintf("ssl/%s", connection_group.c_str());
-
- // If the user is refreshing the page, bypass the host cache.
- bool disable_resolver_cache = request_->load_flags & LOAD_BYPASS_CACHE ||
- request_->load_flags & LOAD_VALIDATE_CACHE ||
- request_->load_flags & LOAD_DISABLE_CACHE;
-
- // Build up the connection parameters.
- scoped_refptr<TCPSocketParams> tcp_params;
- scoped_refptr<HttpProxySocketParams> http_proxy_params;
- scoped_refptr<SOCKSSocketParams> socks_params;
- scoped_ptr<HostPortPair> proxy_host_port;
-
- if (proxy_info_.is_direct()) {
- tcp_params = new TCPSocketParams(endpoint_, request_->priority,
- request_->referrer,
- disable_resolver_cache);
- } else {
- ProxyServer proxy_server = proxy_info_.proxy_server();
- proxy_host_port.reset(new HostPortPair(proxy_server.host_port_pair()));
- scoped_refptr<TCPSocketParams> proxy_tcp_params =
- new TCPSocketParams(*proxy_host_port, request_->priority,
- request_->referrer, disable_resolver_cache);
-
- if (proxy_info_.is_http()) {
- GURL authentication_url = request_->url;
- if (using_ssl_ && !authentication_url.SchemeIs("https")) {
- // If a proxy tunnel connection needs to be established due to
- // an Alternate-Protocol, the URL needs to be changed to indicate
- // https or digest authentication attempts will fail.
- // For example, suppose the initial request was for
- // "http://www.example.com/index.html". If this is an SSL
- // upgrade due to alternate protocol, the digest authorization
- // should have a uri="www.example.com:443" field rather than a
- // "/index.html" entry, even though the original request URL has not
- // changed.
- authentication_url = UpgradeUrlToHttps(authentication_url);
- }
- establishing_tunnel_ = using_ssl_;
- std::string user_agent;
- request_->extra_headers.GetHeader(HttpRequestHeaders::kUserAgent,
- &user_agent);
- http_proxy_params = new HttpProxySocketParams(proxy_tcp_params,
- authentication_url,
- user_agent,
- endpoint_,
- session_, using_ssl_);
- } else {
- DCHECK(proxy_info_.is_socks());
- char socks_version;
- if (proxy_server.scheme() == ProxyServer::SCHEME_SOCKS5)
- socks_version = '5';
- else
- socks_version = '4';
- connection_group =
- StringPrintf("socks%c/%s", socks_version, connection_group.c_str());
-
- socks_params = new SOCKSSocketParams(proxy_tcp_params,
- socks_version == '5',
- endpoint_,
- request_->priority,
- request_->referrer);
- }
- }
-
- // Deal with SSL - which layers on top of any given proxy.
- if (using_ssl_) {
- if (ContainsKey(*g_tls_intolerant_servers, GetHostAndPort(request_->url))) {
- LOG(WARNING) << "Falling back to SSLv3 because host is TLS intolerant: "
- << GetHostAndPort(request_->url);
- ssl_config_.ssl3_fallback = true;
- ssl_config_.tls1_enabled = false;
- }
-
- UMA_HISTOGRAM_ENUMERATION("Net.ConnectionUsedSSLv3Fallback",
- (int) ssl_config_.ssl3_fallback, 2);
-
- int load_flags = request_->load_flags;
- if (g_ignore_certificate_errors)
- load_flags |= LOAD_IGNORE_ALL_CERT_ERRORS;
- if (request_->load_flags & LOAD_VERIFY_EV_CERT)
- ssl_config_.verify_ev_cert = true;
-
- scoped_refptr<SSLSocketParams> ssl_params =
- new SSLSocketParams(tcp_params, http_proxy_params, socks_params,
- proxy_info_.proxy_server().scheme(),
- request_->url.HostNoBrackets(), ssl_config_,
- load_flags,
- want_spdy_without_npn_ &&
- want_ssl_over_spdy_without_npn_,
- want_spdy_over_npn);
-
- scoped_refptr<SSLClientSocketPool> ssl_pool;
- if (proxy_info_.is_direct())
- ssl_pool = session_->ssl_socket_pool();
- else
- ssl_pool = session_->GetSocketPoolForSSLWithProxy(*proxy_host_port);
-
- return connection_->Init(connection_group, ssl_params, request_->priority,
- &io_callback_, ssl_pool, net_log_);
- }
-
- // Finally, get the connection started.
- if (proxy_info_.is_http()) {
- return connection_->Init(
- connection_group, http_proxy_params, request_->priority, &io_callback_,
- session_->GetSocketPoolForHTTPProxy(*proxy_host_port), net_log_);
- }
-
- if (proxy_info_.is_socks()) {
- return connection_->Init(
- connection_group, socks_params, request_->priority, &io_callback_,
- session_->GetSocketPoolForSOCKSProxy(*proxy_host_port), net_log_);
- }
-
- DCHECK(proxy_info_.is_direct());
- return connection_->Init(connection_group, tcp_params, request_->priority,
- &io_callback_, session_->tcp_socket_pool(),
- net_log_);
-}
-
-int HttpNetworkTransaction::DoInitConnectionComplete(int result) {
- // |result| may be the result of any of the stacked pools. The following
- // logic is used when determining how to interpret an error.
- // If |result| < 0:
- // and connection_->socket() != NULL, then the SSL handshake ran and it
- // is a potentially recoverable error.
- // and connection_->socket == NULL and connection_->is_ssl_error() is true,
- // then the SSL handshake ran with an unrecoverable error.
- // otherwise, the error came from one of the other pools.
- bool ssl_started = using_ssl_ && (result == OK || connection_->socket() ||
- connection_->is_ssl_error());
-
- if (ssl_started && (result == OK || IsCertificateError(result))) {
- SSLClientSocket* ssl_socket =
- static_cast<SSLClientSocket*>(connection_->socket());
- if (ssl_socket->wasNpnNegotiated()) {
- response_.was_npn_negotiated = true;
- std::string proto;
- ssl_socket->GetNextProto(&proto);
- SSLClientSocket::NextProto next_protocol =
- SSLClientSocket::NextProtoFromString(proto);
- // If we negotiated either version of SPDY, we must have
- // advertised it, so allow it.
- // TODO(mbelshe): verify it was a protocol we advertised?
- if (next_protocol == SSLClientSocket::kProtoSPDY1 ||
- next_protocol == SSLClientSocket::kProtoSPDY2) {
- using_spdy_ = true;
- }
- }
- if(want_ssl_over_spdy_without_npn_ && want_spdy_without_npn_)
- using_spdy_ = true;
- }
-
- // We may be using spdy without SSL
- if(!want_ssl_over_spdy_without_npn_ && want_spdy_without_npn_)
- using_spdy_ = true;
-
- if (result == ERR_PROXY_AUTH_REQUESTED) {
- DCHECK(!ssl_started);
- // Other state (i.e. |using_ssl_|) suggests that |connection_| will have an
- // SSL socket, but there was an error before that could happened. This
- // puts the in progress HttpProxy socket into |connection_| in order to
- // complete the auth. The tunnel restart code is carefully to remove it
- // before returning control to the rest of this class.
- connection_.reset(connection_->release_pending_http_proxy_connection());
- return HandleTunnelAuthFailure(result);
- }
-
- if ((!ssl_started && result < 0 &&
- alternate_protocol_mode_ == kUsingAlternateProtocol) ||
- result == ERR_NPN_NEGOTIATION_FAILED) {
- // Mark the alternate protocol as broken and fallback.
- MarkBrokenAlternateProtocolAndFallback();
- return OK;
- }
-
- if (result < 0 && !ssl_started) {
- // A temporary CHECK for tracking down http://crbug.com/49862.
- CHECK(!IsCertificateError(result));
- return ReconsiderProxyAfterError(result);
- }
- establishing_tunnel_ = false;
-
- if (connection_->socket()) {
- LogHttpConnectedMetrics(*connection_);
-
- // Set the reused_socket_ flag to indicate that we are using a keep-alive
- // connection. This flag is used to handle errors that occur while we are
- // trying to reuse a keep-alive connection.
- reused_socket_ = connection_->is_reused();
- // TODO(vandebo) should we exclude SPDY in the following if?
- if (!reused_socket_) {
- if (using_spdy_)
- UpdateConnectionTypeHistograms(CONNECTION_SPDY);
- else
- UpdateConnectionTypeHistograms(CONNECTION_HTTP);
- }
-
- if (!using_ssl_) {
- DCHECK_EQ(OK, result);
- if (using_spdy_)
- next_state_ = STATE_INIT_STREAM;
- else
- next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN;
- return result;
- }
- }
-
- // Handle SSL errors below.
- DCHECK(using_ssl_);
- DCHECK(ssl_started);
- if (IsCertificateError(result)) {
- if (using_spdy_ && request_->url.SchemeIs("http")) {
- // We ignore certificate errors for http over spdy.
- spdy_certificate_error_ = result;
- result = OK;
- } else {
- result = HandleCertificateError(result);
- if (result == OK && !connection_->socket()->IsConnectedAndIdle()) {
- connection_->socket()->Disconnect();
- connection_->Reset();
- next_state_ = STATE_INIT_CONNECTION;
- return result;
- }
- }
- }
-
- if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
- response_.cert_request_info =
- connection_->ssl_error_response_info().cert_request_info;
- return HandleCertificateRequest(result);
- }
- if (result < 0)
- return HandleSSLHandshakeError(result);
-
- if (using_spdy_) {
- UpdateConnectionTypeHistograms(CONNECTION_SPDY);
- // TODO(cbentzel): Add auth support to spdy. See http://crbug.com/46620
- next_state_ = STATE_INIT_STREAM;
- } else {
- next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN;
- }
- return OK;
-}
-
int HttpNetworkTransaction::DoInitStream() {
next_state_ = STATE_INIT_STREAM_COMPLETE;
- if (!using_spdy_) {
- stream_.reset(new HttpBasicStream(connection_.get()));
- return stream_->InitializeStream(request_, net_log_, &io_callback_);
- }
-
- CHECK(!stream_.get());
-
- const scoped_refptr<SpdySessionPool> spdy_pool =
- session_->spdy_session_pool();
- scoped_refptr<SpdySession> spdy_session;
-
- HostPortProxyPair pair(endpoint_, proxy_info_.ToPacString());
- if (session_->spdy_session_pool()->HasSession(pair)) {
- spdy_session =
- session_->spdy_session_pool()->Get(pair, session_, net_log_);
- } else {
- // SPDY can be negotiated using the TLS next protocol negotiation (NPN)
- // extension, or just directly using SSL. Either way, |connection_| must
- // contain an SSLClientSocket.
- CHECK(connection_->socket());
- int error = spdy_pool->GetSpdySessionFromSocket(
- pair, session_, connection_.release(), net_log_,
- spdy_certificate_error_, &spdy_session, using_ssl_);
- if (error != OK)
- return error;
- }
-
- if (spdy_session->IsClosed())
- return ERR_CONNECTION_CLOSED;
-
- headers_valid_ = false;
-
- stream_.reset(new SpdyHttpStream(spdy_session));
- return stream_->InitializeStream(request_, net_log_, &io_callback_);
+ session_->http_stream_factory()->RequestStream(request_,
+ &ssl_config_,
+ &proxy_info_,
+ this,
+ net_log_,
+ session_,
+ &stream_request_);
+ return ERR_IO_PENDING;
}
int HttpNetworkTransaction::DoInitStreamComplete(int result) {
- if (result < 0)
- return result;
-
- next_state_ = STATE_SEND_REQUEST;
- return OK;
-}
-
-int HttpNetworkTransaction::DoRestartTunnelAuth() {
- next_state_ = STATE_RESTART_TUNNEL_AUTH_COMPLETE;
- HttpProxyClientSocket* http_proxy_socket =
- static_cast<HttpProxyClientSocket*>(connection_->socket());
- return http_proxy_socket->RestartWithAuth(&io_callback_);
-}
-
-int HttpNetworkTransaction::DoRestartTunnelAuthComplete(int result) {
- if (result == ERR_PROXY_AUTH_REQUESTED)
- return HandleTunnelAuthFailure(result);
-
if (result == OK) {
- // Now that we've got the HttpProxyClientSocket connected. We have
- // to release it as an idle socket into the pool and start the connection
- // process from the beginning. Trying to pass it in with the
- // SSLSocketParams might cause a deadlock since params are dispatched
- // interchangeably. This request won't necessarily get this http proxy
- // socket, but there will be forward progress.
- connection_->Reset();
- establishing_tunnel_ = false;
- next_state_ = STATE_INIT_CONNECTION;
- return OK;
+ next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN;
+ DCHECK(stream_.get());
}
- return ReconsiderProxyAfterError(result);
+ // At this point we are done with the stream_request_.
+ stream_request_ = NULL;
+ return result;
}
int HttpNetworkTransaction::DoGenerateProxyAuthToken() {
@@ -1080,7 +622,7 @@ int HttpNetworkTransaction::DoGenerateServerAuthToken() {
int HttpNetworkTransaction::DoGenerateServerAuthTokenComplete(int rv) {
DCHECK_NE(ERR_IO_PENDING, rv);
if (rv == OK)
- next_state_ = STATE_INIT_STREAM;
+ next_state_ = STATE_SEND_REQUEST;
return rv;
}
@@ -1097,7 +639,7 @@ int HttpNetworkTransaction::DoSendRequest() {
// This is constructed lazily (instead of within our Start method), so that
// we have proxy info available.
- if (request_headers_.empty() && !using_spdy_) {
+ if (request_headers_.empty() && !response_.was_fetched_via_spdy) {
// Figure out if we can/should add Proxy-Authentication & Authentication
// headers.
HttpRequestHeaders authorization_headers;
@@ -1113,9 +655,10 @@ int HttpNetworkTransaction::DoSendRequest() {
&authorization_headers);
std::string request_line;
HttpRequestHeaders request_headers;
+
BuildRequestHeaders(request_, authorization_headers, request_body,
- !using_ssl_ && proxy_info_.is_http(), &request_line,
- &request_headers);
+ !is_https_request() && proxy_info_.is_http(),
+ &request_line, &request_headers);
if (session_->network_delegate())
session_->network_delegate()->OnSendHttpRequest(&request_headers);
@@ -1147,7 +690,7 @@ int HttpNetworkTransaction::DoReadHeaders() {
}
int HttpNetworkTransaction::HandleConnectionClosedBeforeEndOfHeaders() {
- if (!response_.headers) {
+ if (!response_.headers && !stream_->IsConnectionReused()) {
// The connection was closed before any data was sent. Likely an error
// rather than empty HTTP/0.9 response.
return ERR_EMPTY_RESPONSE;
@@ -1157,54 +700,41 @@ int HttpNetworkTransaction::HandleConnectionClosedBeforeEndOfHeaders() {
}
int HttpNetworkTransaction::DoReadHeadersComplete(int result) {
- if (using_spdy_) {
- // TODO(willchan): Flesh out the support for HTTP authentication here.
- if (result < 0)
- return HandleIOError(result);
-
- if (result == OK)
- headers_valid_ = true;
-
- LogTransactionConnectedMetrics();
- return result;
- }
-
// We can get a certificate error or ERR_SSL_CLIENT_AUTH_CERT_NEEDED here
// due to SSL renegotiation.
- if (using_ssl_) {
- if (IsCertificateError(result)) {
- // We don't handle a certificate error during SSL renegotiation, so we
- // have to return an error that's not in the certificate error range
- // (-2xx).
- LOG(ERROR) << "Got a server certificate with error " << result
- << " during SSL renegotiation";
- result = ERR_CERT_ERROR_IN_SSL_RENEGOTIATION;
- } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
- response_.cert_request_info = new SSLCertRequestInfo;
- SSLClientSocket* ssl_socket =
- static_cast<SSLClientSocket*>(connection_->socket());
- ssl_socket->GetSSLCertRequestInfo(response_.cert_request_info);
- result = HandleCertificateRequest(result);
- if (result == OK)
- return result;
- } else if ((result == ERR_SSL_DECOMPRESSION_FAILURE_ALERT ||
- result == ERR_SSL_BAD_RECORD_MAC_ALERT) &&
- ssl_config_.tls1_enabled &&
- !SSLConfigService::IsKnownStrictTLSServer(request_->url.host())){
- // Some buggy servers select DEFLATE compression when offered and then
- // fail to ever decompress anything. They will send a fatal alert telling
- // us this. Normally we would pick this up during the handshake because
- // our Finished message is compressed and we'll never get the server's
- // Finished if it fails to process ours.
- //
- // However, with False Start, we'll believe that the handshake is
- // complete as soon as we've /sent/ our Finished message. In this case,
- // we only find out that the server is buggy here, when we try to read
- // the initial reply.
- g_tls_intolerant_servers->insert(GetHostAndPort(request_->url));
- ResetConnectionAndRequestForResend();
- return OK;
- }
+ if (IsCertificateError(result)) {
+ // We don't handle a certificate error during SSL renegotiation, so we
+ // have to return an error that's not in the certificate error range
+ // (-2xx).
+ LOG(ERROR) << "Got a server certificate with error " << result
+ << " during SSL renegotiation";
+ result = ERR_CERT_ERROR_IN_SSL_RENEGOTIATION;
+ } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
+ // TODO(wtc): Need a test case for this code path!
+ DCHECK(stream_.get());
+ DCHECK(is_https_request());
+ response_.cert_request_info = new SSLCertRequestInfo;
+ stream_->GetSSLCertRequestInfo(response_.cert_request_info);
+ result = HandleCertificateRequest(result);
+ if (result == OK)
+ return result;
+ } else if ((result == ERR_SSL_DECOMPRESSION_FAILURE_ALERT ||
+ result == ERR_SSL_BAD_RECORD_MAC_ALERT) &&
+ ssl_config_.tls1_enabled &&
+ !SSLConfigService::IsKnownStrictTLSServer(request_->url.host())) {
+ // Some buggy servers select DEFLATE compression when offered and then
+ // fail to ever decompress anything. They will send a fatal alert telling
+ // us this. Normally we would pick this up during the handshake because
+ // our Finished message is compressed and we'll never get the server's
+ // Finished if it fails to process ours.
+ //
+ // However, with False Start, we'll believe that the handshake is
+ // complete as soon as we've /sent/ our Finished message. In this case,
+ // we only find out that the server is buggy here, when we try to read
+ // the initial reply.
+ session_->http_stream_factory()->AddTLSIntolerantServer(request_->url);
+ ResetConnectionAndRequestForResend();
+ return OK;
}
if (result < 0 && result != ERR_CONNECTION_CLOSED)
@@ -1218,7 +748,8 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) {
// After we call RestartWithAuth a new response_time will be recorded, and
// we need to be cautious about incorrectly logging the duration across the
// authentication activity.
- LogTransactionConnectedMetrics();
+ if (result == OK)
+ LogTransactionConnectedMetrics();
if (result == ERR_CONNECTION_CLOSED) {
// For now, if we get at least some data, we do the best we can to make
@@ -1253,20 +784,17 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) {
return OK;
}
- ProcessAlternateProtocol(*response_.headers,
- endpoint_,
- session_->mutable_alternate_protocols());
+ HostPortPair endpoint = HostPortPair(request_->url.HostNoBrackets(),
+ request_->url.EffectiveIntPort());
+ ProcessAlternateProtocol(session_->http_stream_factory(),
+ session_->mutable_alternate_protocols(),
+ *response_.headers,
+ endpoint);
int rv = HandleAuthChallenge();
if (rv != OK)
return rv;
- if (using_ssl_) {
- SSLClientSocket* ssl_socket =
- static_cast<SSLClientSocket*>(connection_->socket());
- ssl_socket->GetSSLInfo(&response_.ssl_info);
- }
-
headers_valid_ = true;
return OK;
}
@@ -1274,8 +802,7 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) {
int HttpNetworkTransaction::DoReadBody() {
DCHECK(read_buf_);
DCHECK_GT(read_buf_len_, 0);
- if (!using_spdy_)
- DCHECK(connection_->is_initialized());
+ DCHECK(stream_ != NULL);
next_state_ = STATE_READ_BODY_COMPLETE;
return stream_->ReadResponseBody(read_buf_, read_buf_len_, &io_callback_);
@@ -1283,25 +810,31 @@ int HttpNetworkTransaction::DoReadBody() {
int HttpNetworkTransaction::DoReadBodyComplete(int result) {
// We are done with the Read call.
- bool done = false, keep_alive = false;
- if (result <= 0)
+ bool done = false;
+ if (result <= 0) {
+ DCHECK_NE(ERR_IO_PENDING, result);
done = true;
+ }
+ bool keep_alive = false;
if (stream_->IsResponseBodyComplete()) {
- done = true;
+ // Note: Just because IsResponseBodyComplete is true, we're not
+ // necessarily "done". We're only "done" when it is the last
+ // read on this HttpNetworkTransaction, which will be signified
+ // by a zero-length read.
+ // TODO(mbelshe): The keepalive property is really a property of
+ // the stream. No need to compute it here just to pass back
+ // to the stream's Close function.
if (stream_->CanFindEndOfResponse())
keep_alive = GetResponseHeaders()->IsKeepAlive();
}
- // Clean up connection_->if we are done.
+ // Clean up connection if we are done.
if (done) {
LogTransactionMetrics();
- if (!using_spdy_) {
- if (!keep_alive)
- connection_->socket()->Disconnect();
- connection_->Reset();
- // The next Read call will return 0 (EOF).
- }
+ stream_->Close(!keep_alive);
+ stream_.reset();
+ // The next Read call will return 0 (EOF).
}
// Clear these to avoid leaving around old state.
@@ -1345,66 +878,6 @@ int HttpNetworkTransaction::DoDrainBodyForAuthRestartComplete(int result) {
return OK;
}
-// static
-void HttpNetworkTransaction::LogHttpConnectedMetrics(
- const ClientSocketHandle& handle) {
- UMA_HISTOGRAM_ENUMERATION("Net.HttpSocketType", handle.reuse_type(),
- ClientSocketHandle::NUM_TYPES);
-
- switch (handle.reuse_type()) {
- case ClientSocketHandle::UNUSED:
- UMA_HISTOGRAM_CUSTOM_TIMES("Net.HttpConnectionLatency",
- handle.setup_time(),
- base::TimeDelta::FromMilliseconds(1),
- base::TimeDelta::FromMinutes(10),
- 100);
- break;
- case ClientSocketHandle::UNUSED_IDLE:
- UMA_HISTOGRAM_CUSTOM_TIMES("Net.SocketIdleTimeBeforeNextUse_UnusedSocket",
- handle.idle_time(),
- base::TimeDelta::FromMilliseconds(1),
- base::TimeDelta::FromMinutes(6),
- 100);
- break;
- case ClientSocketHandle::REUSED_IDLE:
- UMA_HISTOGRAM_CUSTOM_TIMES("Net.SocketIdleTimeBeforeNextUse_ReusedSocket",
- handle.idle_time(),
- base::TimeDelta::FromMilliseconds(1),
- base::TimeDelta::FromMinutes(6),
- 100);
- break;
- default:
- NOTREACHED();
- break;
- }
-}
-
-void HttpNetworkTransaction::LogIOErrorMetrics(
- const ClientSocketHandle& handle) {
- UMA_HISTOGRAM_ENUMERATION("Net.IOError_SocketReuseType",
- handle.reuse_type(), ClientSocketHandle::NUM_TYPES);
-
- switch (handle.reuse_type()) {
- case ClientSocketHandle::UNUSED:
- break;
- case ClientSocketHandle::UNUSED_IDLE:
- UMA_HISTOGRAM_CUSTOM_TIMES(
- "Net.SocketIdleTimeOnIOError2_UnusedSocket",
- handle.idle_time(), base::TimeDelta::FromMilliseconds(1),
- base::TimeDelta::FromMinutes(6), 100);
- break;
- case ClientSocketHandle::REUSED_IDLE:
- UMA_HISTOGRAM_CUSTOM_TIMES(
- "Net.SocketIdleTimeOnIOError2_ReusedSocket",
- handle.idle_time(), base::TimeDelta::FromMilliseconds(1),
- base::TimeDelta::FromMinutes(6), 100);
- break;
- default:
- NOTREACHED();
- break;
- }
-}
-
void HttpNetworkTransaction::LogTransactionConnectedMetrics() {
if (logged_response_time_)
return;
@@ -1498,55 +971,34 @@ void HttpNetworkTransaction::LogTransactionMetrics() const {
}
}
-int HttpNetworkTransaction::HandleTunnelAuthFailure(int error) {
- DCHECK(establishing_tunnel_);
- DCHECK_EQ(ERR_PROXY_AUTH_REQUESTED, error);
- HttpProxyClientSocket* http_proxy_socket =
- static_cast<HttpProxyClientSocket*>(connection_->socket());
-
- const HttpResponseInfo* tunnel_auth_response =
- http_proxy_socket->GetResponseInfo();
- response_.headers = tunnel_auth_response->headers;
- response_.auth_challenge = tunnel_auth_response->auth_challenge;
- headers_valid_ = true;
-
- auth_controllers_[HttpAuth::AUTH_PROXY] =
- http_proxy_socket->auth_controller();
- pending_auth_target_ = HttpAuth::AUTH_PROXY;
- return OK;
-}
-
-int HttpNetworkTransaction::HandleCertificateError(int error) {
- DCHECK(using_ssl_);
- DCHECK(IsCertificateError(error));
-
- SSLClientSocket* ssl_socket =
- static_cast<SSLClientSocket*>(connection_->socket());
- ssl_socket->GetSSLInfo(&response_.ssl_info);
-
- // Add the bad certificate to the set of allowed certificates in the
- // SSL info object. This data structure will be consulted after calling
- // RestartIgnoringLastError(). And the user will be asked interactively
- // before RestartIgnoringLastError() is ever called.
- SSLConfig::CertAndStatus bad_cert;
- bad_cert.cert = response_.ssl_info.cert;
- bad_cert.cert_status = response_.ssl_info.cert_status;
- ssl_config_.allowed_bad_certs.push_back(bad_cert);
-
- int load_flags = request_->load_flags;
- if (g_ignore_certificate_errors)
- load_flags |= LOAD_IGNORE_ALL_CERT_ERRORS;
- if (ssl_socket->IgnoreCertError(error, load_flags))
- return OK;
- return error;
-}
-
int HttpNetworkTransaction::HandleCertificateRequest(int error) {
- // Close the connection while the user is selecting a certificate to send
- // to the server.
- if (connection_->socket())
- connection_->socket()->Disconnect();
- connection_->Reset();
+ // There are two paths through which the server can request a certificate
+ // from us. The first is during the initial handshake, the second is
+ // during SSL renegotiation.
+ //
+ // In both cases, we want to close the connection before proceeding.
+ // We do this for two reasons:
+ // First, we don't want to keep the connection to the server hung for a
+ // long time while the user selects a certificate.
+ // Second, even if we did keep the connection open, NSS has a bug where
+ // restarting the handshake for ClientAuth is currently broken.
+
+ if (stream_.get()) {
+ // Since we already have a stream, we're being called as part of SSL
+ // renegotiation.
+ DCHECK(!stream_request_.get());
+ stream_->Close(true);
+ stream_.reset();
+ }
+
+ if (stream_request_.get()) {
+ // The server is asking for a client certificate during the initial
+ // handshake.
+ DCHECK_EQ(STATE_INIT_STREAM_COMPLETE, next_state_);
+ stream_request_->Cancel();
+ stream_request_ = NULL;
+ next_state_ = STATE_INIT_STREAM;
+ }
// If the user selected one of the certificate in client_certs for this
// server before, use it automatically.
@@ -1559,7 +1011,7 @@ int HttpNetworkTransaction::HandleCertificateRequest(int error) {
if (client_cert->fingerprint().Equals(client_certs[i]->fingerprint())) {
ssl_config_.client_cert = client_cert;
ssl_config_.send_client_cert = true;
- next_state_ = STATE_INIT_CONNECTION;
+ next_state_ = STATE_INIT_STREAM;
// Reset the other member variables.
// Note: this is necessary only with SSL renegotiation.
ResetStateForRestart();
@@ -1570,32 +1022,6 @@ int HttpNetworkTransaction::HandleCertificateRequest(int error) {
return error;
}
-int HttpNetworkTransaction::HandleSSLHandshakeError(int error) {
- if (ssl_config_.send_client_cert &&
- (error == ERR_SSL_PROTOCOL_ERROR ||
- error == ERR_BAD_SSL_CLIENT_AUTH_CERT)) {
- session_->ssl_client_auth_cache()->Remove(GetHostAndPort(request_->url));
- }
-
- switch (error) {
- case ERR_SSL_PROTOCOL_ERROR:
- case ERR_SSL_VERSION_OR_CIPHER_MISMATCH:
- case ERR_SSL_DECOMPRESSION_FAILURE_ALERT:
- case ERR_SSL_BAD_RECORD_MAC_ALERT:
- if (ssl_config_.tls1_enabled &&
- !SSLConfigService::IsKnownStrictTLSServer(request_->url.host())) {
- // This could be a TLS-intolerant server, an SSL 3.0 server that
- // chose a TLS-only cipher suite or a server with buggy DEFLATE
- // support. Turn off TLS 1.0, DEFLATE support and retry.
- g_tls_intolerant_servers->insert(GetHostAndPort(request_->url));
- ResetConnectionAndRequestForResend();
- error = OK;
- }
- break;
- }
- return error;
-}
-
// This method determines whether it is safe to resend the request after an
// IO error. It can only be called in response to request header or body
// write errors or response header read errors. It should not be used in
@@ -1609,8 +1035,6 @@ int HttpNetworkTransaction::HandleIOError(int error) {
case ERR_CONNECTION_RESET:
case ERR_CONNECTION_CLOSED:
case ERR_CONNECTION_ABORTED:
- if (!using_spdy_)
- LogIOErrorMetrics(*connection_);
if (ShouldResendRequest(error)) {
ResetConnectionAndRequestForResend();
error = OK;
@@ -1628,6 +1052,7 @@ void HttpNetworkTransaction::ResetStateForRestart() {
headers_valid_ = false;
request_headers_.clear();
response_ = HttpResponseInfo();
+ establishing_tunnel_ = false;
}
HttpResponseHeaders* HttpNetworkTransaction::GetResponseHeaders() const {
@@ -1635,104 +1060,32 @@ HttpResponseHeaders* HttpNetworkTransaction::GetResponseHeaders() const {
}
bool HttpNetworkTransaction::ShouldResendRequest(int error) const {
- if (using_spdy_ && stream_ != NULL)
- return static_cast<SpdyHttpStream *>(stream_.get())->
- ShouldResendFailedRequest(error);
+ bool connection_is_proven = stream_->IsConnectionReused();
+ bool has_received_headers = GetResponseHeaders() != NULL;
// NOTE: we resend a request only if we reused a keep-alive connection.
// This automatically prevents an infinite resend loop because we'll run
// out of the cached keep-alive connections eventually.
- if (!connection_->ShouldResendFailedRequest(error) ||
- GetResponseHeaders()) { // We have received some response headers.
- return false;
- }
- return true;
+ if (connection_is_proven && !has_received_headers)
+ return true;
+ return false;
}
void HttpNetworkTransaction::ResetConnectionAndRequestForResend() {
- // Note: When using SPDY we may not own a connection.
- if (connection_.get()) {
- if (connection_->socket())
- connection_->socket()->Disconnect();
- connection_->Reset();
- } else {
- DCHECK(using_spdy_);
- connection_.reset(new ClientSocketHandle);
+ if (stream_.get()) {
+ stream_->Close(true);
+ stream_.reset();
}
// We need to clear request_headers_ because it contains the real request
// headers, but we may need to resend the CONNECT request first to recreate
// the SSL tunnel.
-
- stream_.reset(NULL);
-
request_headers_.clear();
- next_state_ = STATE_INIT_CONNECTION; // Resend the request.
-}
-
-int HttpNetworkTransaction::ReconsiderProxyAfterError(int error) {
- DCHECK(!pac_request_);
-
- // A failure to resolve the hostname or any error related to establishing a
- // TCP connection could be grounds for trying a new proxy configuration.
- //
- // Why do this when a hostname cannot be resolved? Some URLs only make sense
- // to proxy servers. The hostname in those URLs might fail to resolve if we
- // are still using a non-proxy config. We need to check if a proxy config
- // now exists that corresponds to a proxy server that could load the URL.
- //
- switch (error) {
- case ERR_NAME_NOT_RESOLVED:
- case ERR_INTERNET_DISCONNECTED:
- case ERR_ADDRESS_UNREACHABLE:
- case ERR_CONNECTION_CLOSED:
- case ERR_CONNECTION_RESET:
- case ERR_CONNECTION_REFUSED:
- case ERR_CONNECTION_ABORTED:
- case ERR_TIMED_OUT:
- case ERR_TUNNEL_CONNECTION_FAILED:
- case ERR_SOCKS_CONNECTION_FAILED:
- break;
- case ERR_SOCKS_CONNECTION_HOST_UNREACHABLE:
- // Remap the SOCKS-specific "host unreachable" error to a more
- // generic error code (this way consumers like the link doctor
- // know to substitute their error page).
- //
- // Note that if the host resolving was done by the SOCSK5 proxy, we can't
- // differentiate between a proxy-side "host not found" versus a proxy-side
- // "address unreachable" error, and will report both of these failures as
- // ERR_ADDRESS_UNREACHABLE.
- return ERR_ADDRESS_UNREACHABLE;
- default:
- return error;
- }
-
- if (request_->load_flags & LOAD_BYPASS_PROXY) {
- return error;
- }
-
- int rv = session_->proxy_service()->ReconsiderProxyAfterError(
- request_->url, &proxy_info_, &io_callback_, &pac_request_, net_log_);
- if (rv == OK || rv == ERR_IO_PENDING) {
- // If the error was during connection setup, there is no socket to
- // disconnect.
- if (connection_->socket())
- connection_->socket()->Disconnect();
- connection_->Reset();
- next_state_ = STATE_RESOLVE_PROXY_COMPLETE;
- } else {
- // If ReconsiderProxyAfterError() failed synchronously, it means
- // there was nothing left to fall-back to, so fail the transaction
- // with the last connection error we got.
- // TODO(eroman): This is a confusing contract, make it more obvious.
- rv = error;
- }
-
- return rv;
+ next_state_ = STATE_INIT_STREAM; // Resend the request.
}
bool HttpNetworkTransaction::ShouldApplyProxyAuth() const {
- return !using_ssl_ && proxy_info_.is_http();
+ return !is_https_request() && proxy_info_.is_http();
}
bool HttpNetworkTransaction::ShouldApplyServerAuth() const {
@@ -1765,6 +1118,12 @@ int HttpNetworkTransaction::HandleAuthChallenge() {
return rv;
}
+bool HttpNetworkTransaction::HaveAuth(HttpAuth::Target target) const {
+ return auth_controllers_[target].get() &&
+ auth_controllers_[target]->HaveAuth();
+}
+
+
GURL HttpNetworkTransaction::AuthURL(HttpAuth::Target target) const {
switch (target) {
case HttpAuth::AUTH_PROXY:
@@ -1781,27 +1140,6 @@ GURL HttpNetworkTransaction::AuthURL(HttpAuth::Target target) const {
}
}
-void HttpNetworkTransaction::MarkBrokenAlternateProtocolAndFallback() {
- // We have to:
- // * Reset the endpoint to be the unmodified URL specified destination.
- // * Mark the endpoint as broken so we don't try again.
- // * Set the alternate protocol mode to kDoNotUseAlternateProtocol so we
- // ignore future Alternate-Protocol headers from the HostPortPair.
- // * Reset the connection and go back to STATE_INIT_CONNECTION.
-
- endpoint_ = HostPortPair(request_->url.HostNoBrackets(),
- request_->url.EffectiveIntPort());
-
- session_->mutable_alternate_protocols()->MarkBrokenAlternateProtocolFor(
- endpoint_);
-
- alternate_protocol_mode_ = kDoNotUseAlternateProtocol;
- if (connection_->socket())
- connection_->socket()->Disconnect();
- connection_->Reset();
- next_state_ = STATE_INIT_CONNECTION;
-}
-
#define STATE_CASE(s) case s: \
description = StringPrintf("%s (0x%08X)", #s, s); \
break
@@ -1809,16 +1147,8 @@ void HttpNetworkTransaction::MarkBrokenAlternateProtocolAndFallback() {
std::string HttpNetworkTransaction::DescribeState(State state) {
std::string description;
switch (state) {
- STATE_CASE(STATE_RESOLVE_PROXY);
- STATE_CASE(STATE_RESOLVE_PROXY_COMPLETE);
- STATE_CASE(STATE_INIT_CONNECTION);
- STATE_CASE(STATE_INIT_CONNECTION_COMPLETE);
STATE_CASE(STATE_INIT_STREAM);
STATE_CASE(STATE_INIT_STREAM_COMPLETE);
- STATE_CASE(STATE_GENERATE_PROXY_AUTH_TOKEN);
- STATE_CASE(STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE);
- STATE_CASE(STATE_GENERATE_SERVER_AUTH_TOKEN);
- STATE_CASE(STATE_GENERATE_SERVER_AUTH_TOKEN_COMPLETE);
STATE_CASE(STATE_SEND_REQUEST);
STATE_CASE(STATE_SEND_REQUEST_COMPLETE);
STATE_CASE(STATE_READ_HEADERS);
diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h
index 23eb44f..a010f1b 100644
--- a/net/http/http_network_transaction.h
+++ b/net/http/http_network_transaction.h
@@ -12,55 +12,35 @@
#include "base/gtest_prod_util.h"
#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
-#include "base/string16.h"
#include "base/time.h"
-#include "net/base/address_list.h"
-#include "net/base/io_buffer.h"
-#include "net/base/load_flags.h"
-#include "net/base/load_states.h"
#include "net/base/net_log.h"
+#include "net/base/request_priority.h"
#include "net/base/ssl_config_service.h"
-#include "net/http/http_alternate_protocols.h"
#include "net/http/http_auth.h"
-#include "net/http/http_auth_controller.h"
-#include "net/http/http_auth_handler.h"
#include "net/http/http_response_info.h"
#include "net/http/http_transaction.h"
+#include "net/http/stream_factory.h"
#include "net/proxy/proxy_service.h"
-#include "net/socket/client_socket_pool.h"
namespace net {
class ClientSocketFactory;
class ClientSocketHandle;
+class HttpAuthController;
class HttpNetworkSession;
class HttpRequestHeaders;
class HttpStream;
+class HttpStreamHandle;
+class HttpStreamRequest;
+class IOBuffer;
-class HttpNetworkTransaction : public HttpTransaction {
+class HttpNetworkTransaction : public HttpTransaction,
+ public StreamFactory::StreamRequestDelegate {
public:
explicit HttpNetworkTransaction(HttpNetworkSession* session);
virtual ~HttpNetworkTransaction();
- static void SetHostMappingRules(const std::string& rules);
-
- // Controls whether or not we use the Alternate-Protocol header.
- static void SetUseAlternateProtocols(bool value);
-
- // Controls whether or not we use ssl when in spdy mode.
- static void SetUseSSLOverSpdyWithoutNPN(bool value);
-
- // Controls whether or not we use spdy without npn.
- static void SetUseSpdyWithoutNPN(bool value);
-
- // Sets the next protocol negotiation value used during the SSL handshake.
- static void SetNextProtos(const std::string& next_protos);
-
- // Sets the HttpNetworkTransaction into a mode where it can ignore
- // certificate errors. This is for testing.
- static void IgnoreCertificateErrors(bool enabled);
-
// HttpTransaction methods:
virtual int Start(const HttpRequestInfo* request_info,
CompletionCallback* callback,
@@ -71,10 +51,7 @@ class HttpNetworkTransaction : public HttpTransaction {
virtual int RestartWithAuth(const string16& username,
const string16& password,
CompletionCallback* callback);
- virtual bool IsReadyToRestartForAuth() {
- return pending_auth_target_ != HttpAuth::AUTH_NONE &&
- HaveAuth(pending_auth_target_);
- }
+ virtual bool IsReadyToRestartForAuth();
virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback);
virtual void StopCaching() {}
@@ -82,6 +59,16 @@ class HttpNetworkTransaction : public HttpTransaction {
virtual LoadState GetLoadState() const;
virtual uint64 GetUploadProgress() const;
+ // StreamRequestDelegate methods:
+ virtual void OnStreamReady(HttpStreamHandle* stream);
+ virtual void OnStreamFailed(int status);
+ virtual void OnCertificateError(int status, const SSLInfo& ssl_info);
+ virtual void OnNeedsProxyAuth(
+ const scoped_refptr<HttpAuthController>& auth_controller,
+ const HttpResponseInfo& response_info);
+ virtual void OnNeedsClientAuth(
+ const scoped_refptr<SSLCertRequestInfo>& cert_info);
+
private:
FRIEND_TEST_ALL_PREFIXES(HttpNetworkTransactionTest, ResetStateForRestart);
FRIEND_TEST_ALL_PREFIXES(SpdyNetworkTransactionTest, WindowUpdateReceived);
@@ -89,14 +76,8 @@ class HttpNetworkTransaction : public HttpTransaction {
FRIEND_TEST_ALL_PREFIXES(SpdyNetworkTransactionTest, FlowControlStallResume);
enum State {
- STATE_RESOLVE_PROXY,
- STATE_RESOLVE_PROXY_COMPLETE,
- STATE_INIT_CONNECTION,
- STATE_INIT_CONNECTION_COMPLETE,
STATE_INIT_STREAM,
STATE_INIT_STREAM_COMPLETE,
- STATE_RESTART_TUNNEL_AUTH,
- STATE_RESTART_TUNNEL_AUTH_COMPLETE,
STATE_GENERATE_PROXY_AUTH_TOKEN,
STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE,
STATE_GENERATE_SERVER_AUTH_TOKEN,
@@ -112,11 +93,7 @@ class HttpNetworkTransaction : public HttpTransaction {
STATE_NONE
};
- enum AlternateProtocolMode {
- kUnspecified, // Unspecified, check HttpAlternateProtocols
- kUsingAlternateProtocol, // Using an alternate protocol
- kDoNotUseAlternateProtocol, // Failed to connect once, do not try again.
- };
+ bool is_https_request() const;
void DoCallback(int result);
void OnIOComplete(int result);
@@ -128,14 +105,8 @@ class HttpNetworkTransaction : public HttpTransaction {
// argument receive the result from the previous state. If a method returns
// ERR_IO_PENDING, then the result from OnIOComplete will be passed to the
// next state method as the result arg.
- int DoResolveProxy();
- int DoResolveProxyComplete(int result);
- int DoInitConnection();
- int DoInitConnectionComplete(int result);
int DoInitStream();
int DoInitStreamComplete(int result);
- int DoRestartTunnelAuth();
- int DoRestartTunnelAuthComplete(int result);
int DoGenerateProxyAuthToken();
int DoGenerateProxyAuthTokenComplete(int result);
int DoGenerateServerAuthToken();
@@ -149,9 +120,6 @@ class HttpNetworkTransaction : public HttpTransaction {
int DoDrainBodyForAuthRestart();
int DoDrainBodyForAuthRestartComplete(int result);
- // Record histograms of latency until Connect() completes.
- static void LogHttpConnectedMetrics(const ClientSocketHandle& handle);
-
// Record histogram of time until first byte of header is received.
void LogTransactionConnectedMetrics();
@@ -162,24 +130,9 @@ class HttpNetworkTransaction : public HttpTransaction {
// response to a CONNECT request.
void LogBlockedTunnelResponse(int response_code) const;
- static void LogIOErrorMetrics(const ClientSocketHandle& handle);
-
- // Called to handle an HTTP proxy tunnel request for auth.
- int HandleTunnelAuthFailure(int error);
-
- // Called to handle a certificate error. Returns OK if the error should be
- // ignored. Otherwise, stores the certificate in response_.ssl_info and
- // returns the same error code.
- int HandleCertificateError(int error);
-
// Called to handle a client certificate request.
int HandleCertificateRequest(int error);
- // Called to possibly recover from an SSL handshake error. Sets next_state_
- // and returns OK if recovering from the error. Otherwise, the same error
- // code is returned.
- int HandleSSLHandshakeError(int error);
-
// Called to possibly recover from the given error. Sets next_state_ and
// returns OK if recovering from the error. Otherwise, the same error code
// is returned.
@@ -196,14 +149,6 @@ class HttpNetworkTransaction : public HttpTransaction {
// ShouldResendRequest() is true.
void ResetConnectionAndRequestForResend();
- // Called when we encounter a network error that could be resolved by trying
- // a new proxy configuration. If there is another proxy configuration to try
- // then this method sets next_state_ appropriately and returns either OK or
- // ERR_IO_PENDING depending on whether or not the new proxy configuration is
- // available synchronously or asynchronously. Otherwise, the given error
- // code is simply returned.
- int ReconsiderProxyAfterError(int error);
-
// Decides the policy when the connection is closed before the end of headers
// has been read. This only applies to reading responses, and not writing
// requests.
@@ -231,21 +176,15 @@ class HttpNetworkTransaction : public HttpTransaction {
// May update |pending_auth_target_| or |response_.auth_challenge|.
int HandleAuthChallenge();
- bool HaveAuth(HttpAuth::Target target) const {
- return auth_controllers_[target].get() &&
- auth_controllers_[target]->HaveAuth();
- }
+ // Returns true if we have auth credentials for the given target.
+ bool HaveAuth(HttpAuth::Target target) const;
// Get the {scheme, host, path, port} for the authentication target
GURL AuthURL(HttpAuth::Target target) const;
- void MarkBrokenAlternateProtocolAndFallback();
-
// Debug helper.
static std::string DescribeState(State state);
- static bool g_ignore_certificate_errors;
-
scoped_refptr<HttpAuthController>
auth_controllers_[HttpAuth::AUTH_NUM_TARGETS];
@@ -263,11 +202,12 @@ class HttpNetworkTransaction : public HttpTransaction {
const HttpRequestInfo* request_;
HttpResponseInfo response_;
- ProxyService::PacRequest* pac_request_;
ProxyInfo proxy_info_;
- scoped_ptr<ClientSocketHandle> connection_;
- scoped_ptr<HttpStream> stream_;
+ scoped_refptr<StreamFactory::StreamRequestJob> stream_request_;
+ scoped_ptr<HttpStreamHandle> stream_;
+
+ // True if this transaction was serviced on a reused socket.
bool reused_socket_;
// True if we've validated the headers that the stream parser has returned.
@@ -278,27 +218,6 @@ class HttpNetworkTransaction : public HttpTransaction {
// responses.
bool logged_response_time_;
- // True if handling a HTTPS request, or using SPDY with SSL
- bool using_ssl_;
-
- // True if this network transaction is using SPDY instead of HTTP.
- bool using_spdy_;
-
- // True if this network transaction wants to use SPDY (not over npn)
- bool want_spdy_without_npn_;
-
- // True if this network transaction wants to use SSL with SPDY (not over npn)
- bool want_ssl_over_spdy_without_npn_;
-
-
- // The certificate error while using SPDY over SSL for insecure URLs.
- int spdy_certificate_error_;
-
- AlternateProtocolMode alternate_protocol_mode_;
-
- // Only valid if |alternate_protocol_mode_| == kUsingAlternateProtocol.
- HttpAlternateProtocols::Protocol alternate_protocol_;
-
SSLConfig ssl_config_;
std::string request_headers_;
@@ -318,10 +237,6 @@ class HttpNetworkTransaction : public HttpTransaction {
// The next state in the state machine.
State next_state_;
- // The hostname and port of the endpoint. This is not necessarily the one
- // specified by the URL, due to Alternate-Protocol or fixed testing ports.
- HostPortPair endpoint_;
-
// True when the tunnel is in the process of being established - we can't
// read from the socket until the tunnel is done.
bool establishing_tunnel_;
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index d3cb28a..fbff846 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -30,6 +30,7 @@
#include "net/http/http_basic_stream.h"
#include "net/http/http_network_session.h"
#include "net/http/http_stream.h"
+#include "net/http/http_stream_factory.h"
#include "net/http/http_transaction_unittest.h"
#include "net/proxy/proxy_config_service_fixed.h"
#include "net/proxy/proxy_resolver.h"
@@ -4237,7 +4238,7 @@ TEST_F(HttpNetworkTransactionTest, GroupNameForDirectConnections) {
},
};
- HttpNetworkTransaction::SetUseAlternateProtocols(true);
+ HttpStreamFactory::set_use_alternate_protocols(true);
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
scoped_refptr<HttpNetworkSession> session(
@@ -4261,7 +4262,7 @@ TEST_F(HttpNetworkTransactionTest, GroupNameForDirectConnections) {
tcp_conn_pool->last_group_name_received());
}
- HttpNetworkTransaction::SetUseAlternateProtocols(false);
+ HttpStreamFactory::set_use_alternate_protocols(false);
}
TEST_F(HttpNetworkTransactionTest, GroupNameForHTTPProxyConnections) {
@@ -4289,7 +4290,7 @@ TEST_F(HttpNetworkTransactionTest, GroupNameForHTTPProxyConnections) {
},
};
- HttpNetworkTransaction::SetUseAlternateProtocols(true);
+ HttpStreamFactory::set_use_alternate_protocols(true);
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
scoped_refptr<HttpNetworkSession> session(
@@ -4315,7 +4316,7 @@ TEST_F(HttpNetworkTransactionTest, GroupNameForHTTPProxyConnections) {
http_proxy_pool->last_group_name_received());
}
- HttpNetworkTransaction::SetUseAlternateProtocols(false);
+ HttpStreamFactory::set_use_alternate_protocols(false);
}
TEST_F(HttpNetworkTransactionTest, GroupNameForSOCKSConnections) {
@@ -4355,7 +4356,7 @@ TEST_F(HttpNetworkTransactionTest, GroupNameForSOCKSConnections) {
},
};
- HttpNetworkTransaction::SetUseAlternateProtocols(true);
+ HttpStreamFactory::set_use_alternate_protocols(true);
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
scoped_refptr<HttpNetworkSession> session(
@@ -4382,7 +4383,7 @@ TEST_F(HttpNetworkTransactionTest, GroupNameForSOCKSConnections) {
socks_conn_pool->last_group_name_received());
}
- HttpNetworkTransaction::SetUseAlternateProtocols(false);
+ HttpStreamFactory::set_use_alternate_protocols(false);
}
TEST_F(HttpNetworkTransactionTest, ReconsiderProxyAfterFailedConnection) {
@@ -5124,8 +5125,8 @@ TEST_F(HttpNetworkTransactionTest, ChangeAuthRealms) {
}
TEST_F(HttpNetworkTransactionTest, HonorAlternateProtocolHeader) {
- HttpNetworkTransaction::SetNextProtos("needs_to_be_set_for_this_test");
- HttpNetworkTransaction::SetUseAlternateProtocols(true);
+ HttpStreamFactory::set_next_protos("needs_to_be_set_for_this_test");
+ HttpStreamFactory::set_use_alternate_protocols(true);
SessionDependencies session_deps;
@@ -5181,12 +5182,12 @@ TEST_F(HttpNetworkTransactionTest, HonorAlternateProtocolHeader) {
expected_alternate.protocol = HttpAlternateProtocols::NPN_SPDY_2;
EXPECT_TRUE(expected_alternate.Equals(alternate));
- HttpNetworkTransaction::SetUseAlternateProtocols(false);
- HttpNetworkTransaction::SetNextProtos("");
+ HttpStreamFactory::set_use_alternate_protocols(false);
+ HttpStreamFactory::set_next_protos("");
}
TEST_F(HttpNetworkTransactionTest, MarkBrokenAlternateProtocol) {
- HttpNetworkTransaction::SetUseAlternateProtocols(true);
+ HttpStreamFactory::set_use_alternate_protocols(true);
SessionDependencies session_deps;
HttpRequestInfo request;
@@ -5244,7 +5245,7 @@ TEST_F(HttpNetworkTransactionTest, MarkBrokenAlternateProtocol) {
const HttpAlternateProtocols::PortProtocolPair alternate =
alternate_protocols->GetAlternateProtocolFor(http_host_port_pair);
EXPECT_EQ(HttpAlternateProtocols::BROKEN, alternate.protocol);
- HttpNetworkTransaction::SetUseAlternateProtocols(false);
+ HttpStreamFactory::set_use_alternate_protocols(false);
}
// TODO(willchan): Redo this test to use TLS/NPN=>SPDY. Currently, the code
@@ -5301,8 +5302,8 @@ TEST_F(HttpNetworkTransactionTest, MarkBrokenAlternateProtocol) {
// }
TEST_F(HttpNetworkTransactionTest, FailNpnSpdyAndFallback) {
- HttpNetworkTransaction::SetUseAlternateProtocols(true);
- HttpNetworkTransaction::SetNextProtos(kExpectedNPNString);
+ HttpStreamFactory::set_use_alternate_protocols(true);
+ HttpStreamFactory::set_next_protos(kExpectedNPNString);
SessionDependencies session_deps;
HttpRequestInfo request;
@@ -5350,13 +5351,13 @@ 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);
+ HttpStreamFactory::set_next_protos("");
+ HttpStreamFactory::set_use_alternate_protocols(false);
}
TEST_F(HttpNetworkTransactionTest, UseAlternateProtocolForNpnSpdy) {
- HttpNetworkTransaction::SetUseAlternateProtocols(true);
- HttpNetworkTransaction::SetNextProtos(kExpectedNPNString);
+ HttpStreamFactory::set_use_alternate_protocols(true);
+ HttpStreamFactory::set_next_protos(kExpectedNPNString);
SessionDependencies session_deps;
HttpRequestInfo request;
@@ -5434,8 +5435,8 @@ TEST_F(HttpNetworkTransactionTest, UseAlternateProtocolForNpnSpdy) {
ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data));
EXPECT_EQ("hello!", response_data);
- HttpNetworkTransaction::SetNextProtos("");
- HttpNetworkTransaction::SetUseAlternateProtocols(false);
+ HttpStreamFactory::set_next_protos("");
+ HttpStreamFactory::set_use_alternate_protocols(false);
}
class CapturingProxyResolver : public ProxyResolver {
@@ -5473,8 +5474,8 @@ class CapturingProxyResolver : public ProxyResolver {
};
TEST_F(HttpNetworkTransactionTest, UseAlternateProtocolForTunneledNpnSpdy) {
- HttpNetworkTransaction::SetUseAlternateProtocols(true);
- HttpNetworkTransaction::SetNextProtos(kExpectedNPNString);
+ HttpStreamFactory::set_use_alternate_protocols(true);
+ HttpStreamFactory::set_next_protos(kExpectedNPNString);
ProxyConfig proxy_config;
proxy_config.set_auto_detect(true);
@@ -5574,14 +5575,14 @@ TEST_F(HttpNetworkTransactionTest, UseAlternateProtocolForTunneledNpnSpdy) {
EXPECT_EQ("https://www.google.com/",
capturing_proxy_resolver->resolved()[1].spec());
- HttpNetworkTransaction::SetNextProtos("");
- HttpNetworkTransaction::SetUseAlternateProtocols(false);
+ HttpStreamFactory::set_next_protos("");
+ HttpStreamFactory::set_use_alternate_protocols(false);
}
TEST_F(HttpNetworkTransactionTest,
UseAlternateProtocolForNpnSpdyWithExistingSpdySession) {
- HttpNetworkTransaction::SetUseAlternateProtocols(true);
- HttpNetworkTransaction::SetNextProtos(kExpectedNPNString);
+ HttpStreamFactory::set_use_alternate_protocols(true);
+ HttpStreamFactory::set_next_protos(kExpectedNPNString);
SessionDependencies session_deps;
HttpRequestInfo request;
@@ -5669,8 +5670,8 @@ TEST_F(HttpNetworkTransactionTest,
ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data));
EXPECT_EQ("hello!", response_data);
- HttpNetworkTransaction::SetNextProtos("");
- HttpNetworkTransaction::SetUseAlternateProtocols(false);
+ HttpStreamFactory::set_next_protos("");
+ HttpStreamFactory::set_use_alternate_protocols(false);
}
// GenerateAuthToken is a mighty big test.
@@ -6309,8 +6310,8 @@ TEST_F(HttpNetworkTransactionTest,
// This tests the case that a request is issued via http instead of spdy after
// npn is negotiated.
TEST_F(HttpNetworkTransactionTest, NpnWithHttpOverSSL) {
- HttpNetworkTransaction::SetUseAlternateProtocols(true);
- HttpNetworkTransaction::SetNextProtos("\x08http/1.1\x07http1.1");
+ HttpStreamFactory::set_use_alternate_protocols(true);
+ HttpStreamFactory::set_next_protos("\x08http/1.1\x07http1.1");
SessionDependencies session_deps;
HttpRequestInfo request;
request.method = "GET";
@@ -6363,16 +6364,16 @@ TEST_F(HttpNetworkTransactionTest, NpnWithHttpOverSSL) {
EXPECT_TRUE(response->was_npn_negotiated);
EXPECT_FALSE(response->was_alternate_protocol_available);
- HttpNetworkTransaction::SetNextProtos("");
- HttpNetworkTransaction::SetUseAlternateProtocols(false);
+ HttpStreamFactory::set_next_protos("");
+ HttpStreamFactory::set_use_alternate_protocols(false);
}
TEST_F(HttpNetworkTransactionTest, SpdyPostNPNServerHangup) {
// Simulate the SSL handshake completing with an NPN negotiation
// followed by an immediate server closing of the socket.
// Fix crash: http://crbug.com/46369
- HttpNetworkTransaction::SetUseAlternateProtocols(true);
- HttpNetworkTransaction::SetNextProtos(kExpectedNPNString);
+ HttpStreamFactory::set_use_alternate_protocols(true);
+ HttpStreamFactory::set_next_protos(kExpectedNPNString);
SessionDependencies session_deps;
HttpRequestInfo request;
@@ -6409,15 +6410,15 @@ TEST_F(HttpNetworkTransactionTest, SpdyPostNPNServerHangup) {
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(ERR_CONNECTION_CLOSED, callback.WaitForResult());
- HttpNetworkTransaction::SetNextProtos("");
- HttpNetworkTransaction::SetUseAlternateProtocols(false);
+ HttpStreamFactory::set_next_protos("");
+ HttpStreamFactory::set_use_alternate_protocols(false);
}
TEST_F(HttpNetworkTransactionTest, SpdyAlternateProtocolThroughProxy) {
// This test ensures that the URL passed into the proxy is upgraded
// to https when doing an Alternate Protocol upgrade.
- HttpNetworkTransaction::SetUseAlternateProtocols(true);
- HttpNetworkTransaction::SetNextProtos(
+ HttpStreamFactory::set_use_alternate_protocols(true);
+ HttpStreamFactory::set_next_protos(
"\x08http/1.1\x07http1.1\x06spdy/2\x04spdy");
SessionDependencies session_deps(CreateFixedProxyService("myproxy:70"));
@@ -6551,8 +6552,47 @@ TEST_F(HttpNetworkTransactionTest, SpdyAlternateProtocolThroughProxy) {
EXPECT_EQ("https", request_url.scheme());
EXPECT_EQ("www.google.com", request_url.host());
- HttpNetworkTransaction::SetNextProtos("");
- HttpNetworkTransaction::SetUseAlternateProtocols(false);
+ HttpStreamFactory::set_next_protos("");
+ HttpStreamFactory::set_use_alternate_protocols(false);
+}
+
+// Test that if we cancel the transaction as the connection is completing, that
+// everything tears down correctly.
+TEST_F(HttpNetworkTransactionTest, SimpleCancel) {
+ // Setup everything about the connection to complete synchronously, so that
+ // after calling HttpNetworkTransaction::Start, the only thing we're waiting
+ // for is the callback from the HttpStreamRequest.
+ // Then cancel the transaction.
+ // Verify that we don't crash.
+ MockConnect mock_connect(false, OK);
+ MockRead data_reads[] = {
+ MockRead(false, "HTTP/1.0 200 OK\r\n\r\n"),
+ MockRead(false, "hello world"),
+ MockRead(false, OK),
+ };
+
+ SessionDependencies session_deps;
+ session_deps.host_resolver->set_synchronous_mode(true);
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(CreateSession(&session_deps)));
+
+ 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);
+ data.set_connect_data(mock_connect);
+ session_deps.socket_factory.AddSocketDataProvider(&data);
+
+ TestCompletionCallback callback;
+
+ CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
+ int rv = trans->Start(&request, &callback, log.bound());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ trans.reset(); // Cancel the transaction here.
+
+ MessageLoop::current()->RunAllPending();
}
} // namespace net
diff --git a/net/http/http_proxy_client_socket.h b/net/http/http_proxy_client_socket.h
index 41bb50e..93afafa 100644
--- a/net/http/http_proxy_client_socket.h
+++ b/net/http/http_proxy_client_socket.h
@@ -26,7 +26,7 @@ namespace net {
class AddressList;
class ClientSocketHandle;
class HttpStream;
-class IOBuffer;;
+class IOBuffer;
class HttpProxyClientSocket : public ClientSocket {
public:
diff --git a/net/http/http_stream.h b/net/http/http_stream.h
index 2cdbc39..9981e09 100644
--- a/net/http/http_stream.h
+++ b/net/http/http_stream.h
@@ -18,11 +18,13 @@
namespace net {
-struct HttpRequestInfo;
+class BoundNetLog;
class HttpResponseInfo;
class IOBuffer;
+class SSLCertRequestInfo;
+class SSLInfo;
class UploadDataStream;
-class BoundNetLog;
+struct HttpRequestInfo;
class HttpStream {
public:
@@ -71,6 +73,17 @@ class HttpStream {
virtual int ReadResponseBody(IOBuffer* buf, int buf_len,
CompletionCallback* callback) = 0;
+ // Closes the stream.
+ // |not_reusable| indicates if the stream can be used for further requests.
+ // In the case of HTTP, where we re-use the byte-stream (e.g. the connection)
+ // this means we need to close the connection; in the case of SPDY, where the
+ // underlying stream is never reused, it has no effect.
+ // TODO(mbelshe): We should figure out how to fold the not_reusable flag
+ // into the stream implementation itself so that the caller
+ // does not need to pass it at all. We might also be able to
+ // eliminate the SetConnectionReused() below.
+ virtual void Close(bool not_reusable) = 0;
+
// Indicates if the response body has been completely read.
virtual bool IsResponseBodyComplete() const = 0;
@@ -85,6 +98,23 @@ class HttpStream {
// as part of the next pipelined response) has been read from the socket.
virtual bool IsMoreDataBuffered() const = 0;
+ // A stream exists on top of a connection. If the connection has been used
+ // to successfully exchange data in the past, error handling for the
+ // stream is done differently. This method returns true if the underlying
+ // connection is reused or has been connected and idle for some time.
+ virtual bool IsConnectionReused() const = 0;
+ virtual void SetConnectionReused() = 0;
+
+ // Get the SSLInfo associated with this stream's connection. This should
+ // only be called for streams over SSL sockets, otherwise the behavior is
+ // undefined.
+ virtual void GetSSLInfo(SSLInfo* ssl_info) = 0;
+
+ // Get the SSLCertRequestInfo associated with this stream's connection.
+ // This should only be called for streams over SSL sockets, otherwise the
+ // behavior is undefined.
+ virtual void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info) = 0;
+
private:
DISALLOW_COPY_AND_ASSIGN(HttpStream);
};
diff --git a/net/http/http_stream_factory.cc b/net/http/http_stream_factory.cc
new file mode 100644
index 0000000..e41003c
--- /dev/null
+++ b/net/http/http_stream_factory.cc
@@ -0,0 +1,133 @@
+// 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_stream_factory.h"
+
+#include "base/stl_util-inl.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "net/base/net_log.h"
+#include "net/base/net_util.h"
+#include "net/http/http_network_session.h"
+#include "net/http/http_stream_request.h"
+
+namespace net {
+
+// static
+const HostMappingRules* HttpStreamFactory::host_mapping_rules_ = NULL;
+// static
+const std::string* HttpStreamFactory::next_protos_ = NULL;
+// static
+bool HttpStreamFactory::use_alternate_protocols_ = false;
+// static
+bool HttpStreamFactory::force_spdy_over_ssl_ = true;
+// static
+bool HttpStreamFactory::force_spdy_always_ = false;
+// static
+bool HttpStreamFactory::ignore_certificate_errors_ = false;
+
+// static
+void HttpStreamFactory::SetHostMappingRules(const std::string& rules) {
+ HostMappingRules* host_mapping_rules = new HostMappingRules();
+ host_mapping_rules->SetRulesFromString(rules);
+ delete host_mapping_rules_;
+ host_mapping_rules_ = host_mapping_rules;
+}
+
+HttpStreamFactory::HttpStreamFactory() {
+}
+
+HttpStreamFactory::~HttpStreamFactory() {
+}
+
+void HttpStreamFactory::RequestStream(
+ const HttpRequestInfo* request_info,
+ SSLConfig* ssl_config,
+ ProxyInfo* proxy_info,
+ StreamFactory::StreamRequestDelegate* delegate,
+ const BoundNetLog& net_log,
+ const scoped_refptr<HttpNetworkSession>& session,
+ scoped_refptr<StreamRequestJob>* stream) {
+ DCHECK(stream != NULL);
+ *stream = new HttpStreamRequest(this, session);
+ (*stream)->Start(request_info, ssl_config, proxy_info, delegate, net_log);
+}
+
+void HttpStreamFactory::AddTLSIntolerantServer(const GURL& url) {
+ tls_intolerant_servers_.insert(GetHostAndPort(url));
+}
+
+bool HttpStreamFactory::IsTLSIntolerantServer(const GURL& url) {
+ return ContainsKey(tls_intolerant_servers_, GetHostAndPort(url));
+}
+
+void HttpStreamFactory::ProcessAlternateProtocol(
+ HttpAlternateProtocols* alternate_protocols,
+ const std::string& alternate_protocol_str,
+ const HostPortPair& http_host_port_pair) {
+ 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 (!base::StringToInt(port_protocol_vector[0], &port) ||
+ port <= 0 || port >= 1 << 16) {
+ DLOG(WARNING) << HttpAlternateProtocols::kHeader
+ << " header has unrecognizable port: "
+ << port_protocol_vector[0];
+ return;
+ }
+
+ HttpAlternateProtocols::Protocol protocol = HttpAlternateProtocols::BROKEN;
+ // We skip NPN_SPDY_1 here, because we've rolled the protocol version to 2.
+ for (int i = HttpAlternateProtocols::NPN_SPDY_2;
+ i < HttpAlternateProtocols::NUM_ALTERNATE_PROTOCOLS; ++i) {
+ if (port_protocol_vector[1] == HttpAlternateProtocols::kProtocolStrings[i])
+ protocol = static_cast<HttpAlternateProtocols::Protocol>(i);
+ }
+
+ if (protocol == HttpAlternateProtocols::BROKEN) {
+ // Currently, we only recognize the npn-spdy protocol.
+ DLOG(WARNING) << HttpAlternateProtocols::kHeader
+ << " header has unrecognized protocol: "
+ << port_protocol_vector[1];
+ return;
+ }
+
+ HostPortPair host_port(http_host_port_pair);
+ if (host_mapping_rules_)
+ host_mapping_rules_->RewriteHost(&host_port);
+
+ if (alternate_protocols->HasAlternateProtocolFor(host_port)) {
+ const HttpAlternateProtocols::PortProtocolPair existing_alternate =
+ alternate_protocols->GetAlternateProtocolFor(host_port);
+ // If we think the alternate protocol is broken, don't change it.
+ if (existing_alternate.protocol == HttpAlternateProtocols::BROKEN)
+ return;
+ }
+
+ alternate_protocols->SetAlternateProtocolFor(host_port, port, protocol);
+}
+
+GURL HttpStreamFactory::ApplyHostMappingRules(const GURL& url,
+ HostPortPair* endpoint) {
+ if (host_mapping_rules_ && host_mapping_rules_->RewriteHost(endpoint)) {
+ url_canon::Replacements<char> replacements;
+ const std::string port_str = base::IntToString(endpoint->port());
+ replacements.SetPort(port_str.c_str(),
+ url_parse::Component(0, port_str.size()));
+ replacements.SetHost(endpoint->host().c_str(),
+ url_parse::Component(0, endpoint->host().size()));
+ return url.ReplaceComponents(replacements);
+ }
+ return url;
+}
+
+} // namespace net
+
diff --git a/net/http/http_stream_factory.h b/net/http/http_stream_factory.h
new file mode 100644
index 0000000..bddac83
--- /dev/null
+++ b/net/http/http_stream_factory.h
@@ -0,0 +1,113 @@
+// 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_HTTP_HTTP_STREAM_FACTORY_H_
+#define NET_HTTP_HTTP_STREAM_FACTORY_H_
+
+#include <set>
+#include <string>
+
+
+#include "base/scoped_ptr.h"
+#include "net/base/completion_callback.h"
+#include "net/base/host_mapping_rules.h"
+#include "net/base/ssl_config_service.h"
+#include "net/http/http_auth.h"
+#include "net/http/http_auth_controller.h"
+#include "net/http/stream_factory.h"
+#include "net/proxy/proxy_service.h"
+#include "net/socket/client_socket_handle.h"
+
+
+namespace net {
+
+class ClientSocketHandle;
+class HttpAuthController;
+class HttpNetworkSession;
+class HttpStreamRequest;
+
+class HttpStreamFactory : public StreamFactory,
+ public base::RefCounted<HttpStreamFactory> {
+ public:
+ HttpStreamFactory();
+ virtual ~HttpStreamFactory();
+
+ // StreamFactory Interface
+ virtual void RequestStream(const HttpRequestInfo* info,
+ SSLConfig* ssl_config,
+ ProxyInfo* proxy_info,
+ StreamRequestDelegate* delegate,
+ const BoundNetLog& net_log,
+ const scoped_refptr<HttpNetworkSession>& session,
+ scoped_refptr<StreamRequestJob>* stream);
+
+ // TLS Intolerant Server API
+ void AddTLSIntolerantServer(const GURL& url);
+ bool IsTLSIntolerantServer(const GURL& url);
+
+ // Alternate Protocol API
+ void ProcessAlternateProtocol(HttpAlternateProtocols* alternate_protocols,
+ const std::string& alternate_protocol_str,
+ const HostPortPair& http_host_port_pair);
+
+ // Host Mapping Rules API
+ GURL ApplyHostMappingRules(const GURL& url, HostPortPair* endpoint);
+
+ // Static settings
+
+ // Controls whether or not we use the Alternate-Protocol header.
+ static void set_use_alternate_protocols(bool value) {
+ use_alternate_protocols_ = value;
+ }
+ static bool use_alternate_protocols() { return use_alternate_protocols_; }
+
+ // Controls whether or not we use ssl when in spdy mode.
+ static void set_force_spdy_over_ssl(bool value) {
+ force_spdy_over_ssl_ = value;
+ }
+ static bool force_spdy_over_ssl() {
+ return force_spdy_over_ssl_;
+ }
+
+ // Controls whether or not we use spdy without npn.
+ static void set_force_spdy_always(bool value) {
+ force_spdy_always_ = value;
+ }
+ static bool force_spdy_always() { return force_spdy_always_; }
+
+ // Sets the next protocol negotiation value used during the SSL handshake.
+ static void set_next_protos(const std::string& value) {
+ delete next_protos_;
+ next_protos_ = new std::string(value);
+ }
+ static const std::string* next_protos() { return next_protos_; }
+
+ // Sets the HttpStreamFactory into a mode where it can ignore certificate
+ // errors. This is for testing.
+ static void set_ignore_certificate_errors(bool value) {
+ ignore_certificate_errors_ = value;
+ }
+ static bool ignore_certificate_errors() {
+ return ignore_certificate_errors_;
+ }
+
+ static void SetHostMappingRules(const std::string& rules);
+
+ private:
+ std::set<std::string> tls_intolerant_servers_;
+
+ static const HostMappingRules* host_mapping_rules_;
+ static const std::string* next_protos_;
+ static bool use_alternate_protocols_;
+ static bool force_spdy_over_ssl_;
+ static bool force_spdy_always_;
+ static bool ignore_certificate_errors_;
+
+ DISALLOW_COPY_AND_ASSIGN(HttpStreamFactory);
+};
+
+} // namespace net
+
+#endif // NET_HTTP_HTTP_STREAM_FACTORY_H_
+
diff --git a/net/http/http_stream_handle.h b/net/http/http_stream_handle.h
new file mode 100644
index 0000000..213cc3a
--- /dev/null
+++ b/net/http/http_stream_handle.h
@@ -0,0 +1,106 @@
+// 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_HTTP_HTTP_STREAM_HANDLE_H_
+#define NET_HTTP_HTTP_STREAM_HANDLE_H_
+#pragma once
+
+#include <string>
+
+#include "net/http/http_stream.h"
+
+#include "net/socket/client_socket_handle.h"
+
+namespace net {
+
+// The HttpStreamHandle provides a container for a ClientSocketHandle and
+// a stream. The HttpStream does not own the ClientSocketHandle which it uses.
+// The HttpStreamHandle container can be used in cases where you just want a
+// handle to the stream but you don't want to manage the lifecycle of the
+// underlying ClientSocketHandle manually.
+class HttpStreamHandle : public HttpStream {
+ public:
+ HttpStreamHandle(ClientSocketHandle* connection, HttpStream* stream)
+ : connection_(connection), stream_(stream) {
+ DCHECK(stream_.get());
+ }
+ virtual ~HttpStreamHandle() {}
+
+ HttpStream* stream() { return stream_.get(); }
+
+ // HttpStream interface
+ int InitializeStream(const HttpRequestInfo* request_info,
+ const BoundNetLog& net_log,
+ CompletionCallback* callback) {
+ return stream_->InitializeStream(request_info, net_log, callback);
+ }
+
+ int SendRequest(const std::string& request_headers,
+ UploadDataStream* request_body,
+ HttpResponseInfo* response,
+ CompletionCallback* callback) {
+ return stream_->SendRequest(request_headers, request_body, response,
+ callback);
+ }
+
+ uint64 GetUploadProgress() const {
+ return stream_->GetUploadProgress();
+ }
+
+ int ReadResponseHeaders(CompletionCallback* callback) {
+ return stream_->ReadResponseHeaders(callback);
+ }
+
+ const HttpResponseInfo* GetResponseInfo() const {
+ return stream_->GetResponseInfo();
+ }
+
+ int ReadResponseBody(IOBuffer* buf, int buf_len,
+ CompletionCallback* callback) {
+ return stream_->ReadResponseBody(buf, buf_len, callback);
+ }
+
+ void Close(bool not_reusable) {
+ stream_->Close(not_reusable);
+ }
+
+ bool IsResponseBodyComplete() const {
+ return stream_->IsResponseBodyComplete();
+ }
+
+ bool CanFindEndOfResponse() const {
+ return stream_->CanFindEndOfResponse();
+ }
+
+ bool IsMoreDataBuffered() const {
+ return stream_->IsMoreDataBuffered();
+ }
+
+ bool IsConnectionReused() const {
+ return stream_->IsConnectionReused();
+ }
+
+ void SetConnectionReused() {
+ stream_->SetConnectionReused();
+ }
+
+ void GetSSLInfo(SSLInfo* ssl_info) {
+ stream_->GetSSLInfo(ssl_info);
+ }
+
+ void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info) {
+ stream_->GetSSLCertRequestInfo(cert_request_info);
+ }
+
+ private:
+ scoped_ptr<ClientSocketHandle> connection_;
+ scoped_ptr<HttpStream> stream_;
+
+ DISALLOW_COPY_AND_ASSIGN(HttpStreamHandle);
+};
+
+} // namespace net
+
+#endif // NET_HTTP_HTTP_STREAM_HANDLE_H_
+
diff --git a/net/http/http_stream_parser.cc b/net/http/http_stream_parser.cc
index 68f98b9..5fbcb5b 100644
--- a/net/http/http_stream_parser.cc
+++ b/net/http/http_stream_parser.cc
@@ -12,6 +12,7 @@
#include "net/http/http_request_info.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_util.h"
+#include "net/socket/ssl_client_socket.h"
#include "net/socket/client_socket_handle.h"
namespace net {
@@ -93,6 +94,12 @@ int HttpStreamParser::ReadResponseHeaders(CompletionCallback* callback) {
return result > 0 ? OK : result;
}
+void HttpStreamParser::Close(bool not_reusable) {
+ if (not_reusable && connection_->socket())
+ connection_->socket()->Disconnect();
+ connection_->Reset();
+}
+
int HttpStreamParser::ReadResponseBody(IOBuffer* buf, int buf_len,
CompletionCallback* callback) {
DCHECK(io_state_ == STATE_BODY_PENDING || io_state_ == STATE_DONE);
@@ -266,8 +273,10 @@ int HttpStreamParser::DoReadHeadersComplete(int result) {
io_state_ = STATE_DONE;
return result;
}
+ // If we've used the connection before, then we know it is not a HTTP/0.9
+ // response and return ERR_CONNECTION_CLOSED.
if (result == ERR_CONNECTION_CLOSED && read_buf_->offset() == 0 &&
- connection_->ShouldResendFailedRequest(result)) {
+ connection_->is_reused()) {
io_state_ = STATE_DONE;
return result;
}
@@ -558,4 +567,35 @@ bool HttpStreamParser::IsMoreDataBuffered() const {
return read_buf_->offset() > read_buf_unused_offset_;
}
+bool HttpStreamParser::IsConnectionReused() const {
+ ClientSocketHandle::SocketReuseType reuse_type = connection_->reuse_type();
+ return connection_->is_reused() ||
+ reuse_type == ClientSocketHandle::UNUSED_IDLE;
+}
+
+void HttpStreamParser::SetConnectionReused() {
+ connection_->set_is_reused(true);
+}
+
+void HttpStreamParser::GetSSLInfo(SSLInfo* ssl_info) {
+ if (request_->url.SchemeIs("https")) {
+ CHECK(connection_->socket()->IsConnected());
+ SSLClientSocket* ssl_socket =
+ static_cast<SSLClientSocket*>(connection_->socket());
+ ssl_socket->GetSSLInfo(ssl_info);
+ }
+}
+
+void HttpStreamParser::GetSSLCertRequestInfo(
+ SSLCertRequestInfo* cert_request_info) {
+ if (request_->url.SchemeIs("https")) {
+ if (!connection_->socket() || !connection_->socket()->IsConnected())
+ return;
+ CHECK(connection_->socket()->IsConnected());
+ SSLClientSocket* ssl_socket =
+ static_cast<SSLClientSocket*>(connection_->socket());
+ ssl_socket->GetSSLCertRequestInfo(cert_request_info);
+ }
+}
+
} // namespace net
diff --git a/net/http/http_stream_parser.h b/net/http/http_stream_parser.h
index feb34e2..1ef7644 100644
--- a/net/http/http_stream_parser.h
+++ b/net/http/http_stream_parser.h
@@ -21,6 +21,8 @@ class GrowableIOBuffer;
struct HttpRequestInfo;
class HttpResponseInfo;
class IOBuffer;
+class SSLCertRequestInfo;
+class SSLInfo;
class HttpStreamParser {
public:
@@ -45,6 +47,8 @@ class HttpStreamParser {
int ReadResponseBody(IOBuffer* buf, int buf_len,
CompletionCallback* callback);
+ void Close(bool not_reusable);
+
uint64 GetUploadProgress() const;
HttpResponseInfo* GetResponseInfo();
@@ -55,6 +59,14 @@ class HttpStreamParser {
bool IsMoreDataBuffered() const;
+ bool IsConnectionReused() const;
+
+ void SetConnectionReused();
+
+ void GetSSLInfo(SSLInfo* ssl_info);
+
+ void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info);
+
private:
// FOO_COMPLETE states implement the second half of potentially asynchronous
// operations and don't necessarily mean that FOO is complete.
diff --git a/net/http/http_stream_request.cc b/net/http/http_stream_request.cc
new file mode 100644
index 0000000..f50c3c2
--- /dev/null
+++ b/net/http/http_stream_request.cc
@@ -0,0 +1,916 @@
+// 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_stream_request.h"
+
+#include "base/stl_util-inl.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "net/base/connection_type_histograms.h"
+#include "net/base/net_log.h"
+#include "net/base/net_util.h"
+#include "net/base/ssl_cert_request_info.h"
+#include "net/http/http_basic_stream.h"
+#include "net/http/http_network_session.h"
+#include "net/http/http_proxy_client_socket.h"
+#include "net/http/http_stream_handle.h"
+#include "net/spdy/spdy_http_stream.h"
+#include "net/spdy/spdy_session.h"
+#include "net/spdy/spdy_session_pool.h"
+#include "net/socket/client_socket_handle.h"
+#include "net/socket/ssl_client_socket.h"
+
+namespace net {
+
+namespace {
+
+GURL UpgradeUrlToHttps(const GURL& original_url) {
+ GURL::Replacements replacements;
+ // new_sheme and new_port need to be in scope here because GURL::Replacements
+ // references the memory contained by them directly.
+ const std::string new_scheme = "https";
+ const std::string new_port = base::IntToString(443);
+ replacements.SetSchemeStr(new_scheme);
+ replacements.SetPortStr(new_port);
+ return original_url.ReplaceComponents(replacements);
+}
+
+}
+
+HttpStreamRequest::HttpStreamRequest(
+ HttpStreamFactory* factory,
+ const scoped_refptr<HttpNetworkSession>& session)
+ : request_info_(NULL),
+ proxy_info_(NULL),
+ ssl_config_(NULL),
+ session_(session),
+ ALLOW_THIS_IN_INITIALIZER_LIST(
+ io_callback_(this, &HttpStreamRequest::OnIOComplete)),
+ connection_(new ClientSocketHandle),
+ factory_(factory),
+ delegate_(NULL),
+ next_state_(STATE_NONE),
+ pac_request_(NULL),
+ using_ssl_(false),
+ using_spdy_(false),
+ force_spdy_always_(factory->force_spdy_always()),
+ force_spdy_over_ssl_(factory->force_spdy_over_ssl()),
+ spdy_certificate_error_(OK),
+ establishing_tunnel_(false),
+ was_alternate_protocol_available_(false),
+ was_npn_negotiated_(false),
+ cancelled_(false) {
+ if (factory->use_alternate_protocols())
+ alternate_protocol_mode_ = kUnspecified;
+ else
+ alternate_protocol_mode_ = kDoNotUseAlternateProtocol;
+}
+
+HttpStreamRequest::~HttpStreamRequest() {
+ // When we're in a partially constructed state, waiting for the user to
+ // provide certificate handling information or authentication, we can't reuse
+ // this stream at all.
+ if (next_state_ == STATE_WAITING_USER_ACTION) {
+ connection_->socket()->Disconnect();
+ connection_->Reset();
+ connection_.reset();
+ }
+
+ if (pac_request_)
+ session_->proxy_service()->CancelPacRequest(pac_request_);
+}
+
+void HttpStreamRequest::Start(const HttpRequestInfo* request_info,
+ SSLConfig* ssl_config,
+ ProxyInfo* proxy_info,
+ StreamFactory::StreamRequestDelegate* delegate,
+ const BoundNetLog& net_log) {
+ CHECK_EQ(STATE_NONE, next_state_);
+ CHECK(!cancelled_);
+
+ request_info_ = request_info;
+ ssl_config_ = ssl_config;
+ proxy_info_ = proxy_info;
+ delegate_ = delegate;
+ net_log_ = net_log;
+ next_state_ = STATE_RESOLVE_PROXY;
+ int rv = RunLoop(OK);
+ DCHECK_EQ(ERR_IO_PENDING, rv);
+}
+
+void HttpStreamRequest::Cancel() {
+ cancelled_ = true;
+
+ // If we were waiting for the user to take action, then the connection
+ // is in a partially connected state. All we can do is close it at this
+ // point.
+ if (next_state_ == STATE_WAITING_USER_ACTION) {
+ connection_->socket()->Disconnect();
+ connection_->Reset();
+ connection_.reset();
+ }
+
+ // The stream could be in a partial state. It is not reusable.
+ if (stream_.get()) {
+ stream_->Close(true);
+ stream_.reset();
+ }
+
+ delegate_ = NULL;
+
+ next_state_ = STATE_NONE;
+}
+
+int HttpStreamRequest::RestartWithCertificate(
+ const scoped_refptr<X509Certificate>& client_cert) {
+ ssl_config()->client_cert = client_cert;
+ ssl_config()->send_client_cert = true;
+ next_state_ = STATE_INIT_CONNECTION;
+ // Reset the other member variables.
+ // Note: this is necessary only with SSL renegotiation.
+ stream_.reset();
+ return RunLoop(OK);
+}
+
+int HttpStreamRequest::RestartTunnelWithProxyAuth(const string16& username,
+ const string16& password) {
+ DCHECK(establishing_tunnel_);
+ next_state_ = STATE_RESTART_TUNNEL_AUTH;
+ stream_.reset();
+ return RunLoop(OK);
+}
+
+LoadState HttpStreamRequest::GetLoadState() const {
+ // TODO(wtc): Define a new LoadState value for the
+ // STATE_INIT_CONNECTION_COMPLETE state, which delays the HTTP request.
+ switch (next_state_) {
+ case STATE_RESOLVE_PROXY_COMPLETE:
+ return LOAD_STATE_RESOLVING_PROXY_FOR_URL;
+ case STATE_INIT_CONNECTION_COMPLETE:
+ return connection_->GetLoadState();
+ case STATE_INIT_STREAM_COMPLETE:
+ return LOAD_STATE_SENDING_REQUEST;
+ default:
+ return LOAD_STATE_IDLE;
+ }
+}
+
+void HttpStreamRequest::GetSSLInfo() {
+ DCHECK(using_ssl_);
+ DCHECK(!establishing_tunnel_);
+ DCHECK(connection_.get() && connection_->socket());
+ SSLClientSocket* ssl_socket =
+ static_cast<SSLClientSocket*>(connection_->socket());
+ ssl_socket->GetSSLInfo(&ssl_info_);
+}
+
+const HttpRequestInfo& HttpStreamRequest::request_info() const {
+ DCHECK(!cancelled_); // Can't access this after cancellation.
+ return *request_info_;
+}
+
+ProxyInfo* HttpStreamRequest::proxy_info() const {
+ DCHECK(!cancelled_); // Can't access this after cancellation.
+ return proxy_info_;
+}
+
+SSLConfig* HttpStreamRequest::ssl_config() const {
+ DCHECK(!cancelled_); // Can't access this after cancellation.
+ return ssl_config_;
+}
+
+void HttpStreamRequest::OnStreamReadyCallback(HttpStreamHandle* stream) {
+ if (cancelled_) {
+ // The delegate is gone. We need to cleanup the stream.
+ delete stream;
+ return;
+ }
+ delegate_->OnStreamReady(stream);
+}
+
+void HttpStreamRequest::OnStreamFailedCallback(int result) {
+ if (cancelled_)
+ return;
+ delegate_->OnStreamFailed(result);
+}
+
+void HttpStreamRequest::OnCertificateErrorCallback(int result,
+ const SSLInfo& ssl_info) {
+ if (cancelled_)
+ return;
+ delegate_->OnCertificateError(result, ssl_info);
+}
+
+void HttpStreamRequest::OnNeedsProxyAuthCallback(
+ const scoped_refptr<HttpAuthController>& auth_controller,
+ const HttpResponseInfo& response) {
+ if (cancelled_)
+ return;
+ delegate_->OnNeedsProxyAuth(auth_controller, response);
+}
+
+void HttpStreamRequest::OnNeedsClientAuthCallback(
+ const scoped_refptr<SSLCertRequestInfo>& cert_info) {
+ if (cancelled_)
+ return;
+ delegate_->OnNeedsClientAuth(cert_info);
+}
+
+void HttpStreamRequest::OnIOComplete(int result) {
+ RunLoop(result);
+}
+
+int HttpStreamRequest::RunLoop(int result) {
+ if (cancelled_)
+ return ERR_ABORTED;
+
+ result = DoLoop(result);
+
+ DCHECK(delegate_);
+
+ if (IsCertificateError(result)) {
+ // Retrieve SSL information from the socket.
+ GetSSLInfo();
+
+ next_state_ = STATE_WAITING_USER_ACTION;
+ MessageLoop::current()->PostTask(FROM_HERE,
+ NewRunnableMethod(this,
+ &HttpStreamRequest::OnCertificateErrorCallback,
+ result, ssl_info_));
+ return ERR_IO_PENDING;
+ }
+
+ switch (result) {
+ case ERR_PROXY_AUTH_REQUESTED:
+ {
+ DCHECK(connection_.get());
+ DCHECK(connection_->socket());
+ DCHECK(establishing_tunnel_);
+
+ HttpProxyClientSocket* http_proxy_socket =
+ static_cast<HttpProxyClientSocket*>(connection_->socket());
+ const HttpResponseInfo* tunnel_auth_response =
+ http_proxy_socket->GetResponseInfo();
+
+ next_state_ = STATE_WAITING_USER_ACTION;
+ MessageLoop::current()->PostTask(FROM_HERE,
+ NewRunnableMethod(this,
+ &HttpStreamRequest::OnNeedsProxyAuthCallback,
+ http_proxy_socket->auth_controller(),
+ *tunnel_auth_response));
+ }
+ return ERR_IO_PENDING;
+
+ case ERR_SSL_CLIENT_AUTH_CERT_NEEDED:
+ MessageLoop::current()->PostTask(FROM_HERE,
+ NewRunnableMethod(this,
+ &HttpStreamRequest::OnNeedsClientAuthCallback,
+ connection_->ssl_error_response_info().cert_request_info));
+ return ERR_IO_PENDING;
+
+ case ERR_IO_PENDING:
+ break;
+
+ case OK:
+ MessageLoop::current()->PostTask(FROM_HERE,
+ NewRunnableMethod(this,
+ &HttpStreamRequest::OnStreamReadyCallback,
+ stream_.release()));
+ return ERR_IO_PENDING;
+
+ default:
+ MessageLoop::current()->PostTask(FROM_HERE,
+ NewRunnableMethod(this,
+ &HttpStreamRequest::OnStreamFailedCallback,
+ result));
+ return ERR_IO_PENDING;
+ }
+ return result;
+}
+
+int HttpStreamRequest::DoLoop(int result) {
+ DCHECK(next_state_ != STATE_NONE);
+ int rv = result;
+ do {
+ State state = next_state_;
+ next_state_ = STATE_NONE;
+ switch (state) {
+ case STATE_RESOLVE_PROXY:
+ DCHECK_EQ(OK, rv);
+ rv = DoResolveProxy();
+ break;
+ case STATE_RESOLVE_PROXY_COMPLETE:
+ rv = DoResolveProxyComplete(rv);
+ break;
+ case STATE_INIT_CONNECTION:
+ DCHECK_EQ(OK, rv);
+ rv = DoInitConnection();
+ break;
+ case STATE_INIT_CONNECTION_COMPLETE:
+ rv = DoInitConnectionComplete(rv);
+ break;
+ case STATE_WAITING_USER_ACTION:
+ rv = DoWaitingUserAction(rv);
+ break;
+ case STATE_RESTART_TUNNEL_AUTH:
+ DCHECK_EQ(OK, rv);
+ rv = DoRestartTunnelAuth();
+ break;
+ case STATE_RESTART_TUNNEL_AUTH_COMPLETE:
+ rv = DoRestartTunnelAuthComplete(rv);
+ break;
+ case STATE_INIT_STREAM:
+ DCHECK_EQ(OK, rv);
+ rv = DoInitStream();
+ break;
+ case STATE_INIT_STREAM_COMPLETE:
+ rv = DoInitStreamComplete(rv);
+ break;
+ default:
+ NOTREACHED() << "bad state";
+ rv = ERR_FAILED;
+ break;
+ }
+ } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
+ return rv;
+}
+
+int HttpStreamRequest::DoResolveProxy() {
+ DCHECK(!pac_request_);
+
+ next_state_ = STATE_RESOLVE_PROXY_COMPLETE;
+
+ // |endpoint_| indicates the final destination endpoint.
+ endpoint_ = HostPortPair(request_info().url.HostNoBrackets(),
+ request_info().url.EffectiveIntPort());
+
+ // Extra URL we might be attempting to resolve to.
+ GURL alternate_endpoint_url = request_info().url;
+
+ // Tracks whether we are using |request_.url| or |alternate_endpoint_url|.
+ const GURL *curr_endpoint_url = &request_info().url;
+
+ alternate_endpoint_url =
+ factory_->ApplyHostMappingRules(alternate_endpoint_url, &endpoint_);
+
+ const HttpAlternateProtocols& alternate_protocols =
+ session_->alternate_protocols();
+ if (alternate_protocols.HasAlternateProtocolFor(endpoint_)) {
+ was_alternate_protocol_available_ = true;
+ if (alternate_protocol_mode_ == kUnspecified) {
+ HttpAlternateProtocols::PortProtocolPair alternate =
+ alternate_protocols.GetAlternateProtocolFor(endpoint_);
+ if (alternate.protocol != HttpAlternateProtocols::BROKEN) {
+ DCHECK_LE(HttpAlternateProtocols::NPN_SPDY_1, alternate.protocol);
+ DCHECK_GT(HttpAlternateProtocols::NUM_ALTERNATE_PROTOCOLS,
+ alternate.protocol);
+ endpoint_.set_port(alternate.port);
+ alternate_protocol_ = alternate.protocol;
+ alternate_protocol_mode_ = kUsingAlternateProtocol;
+ alternate_endpoint_url = UpgradeUrlToHttps(*curr_endpoint_url);
+ curr_endpoint_url = &alternate_endpoint_url;
+ }
+ }
+ }
+
+ if (request_info().load_flags & LOAD_BYPASS_PROXY) {
+ proxy_info()->UseDirect();
+ return OK;
+ }
+
+ return session_->proxy_service()->ResolveProxy(
+ *curr_endpoint_url, proxy_info(), &io_callback_, &pac_request_,
+ net_log_);
+}
+
+int HttpStreamRequest::DoResolveProxyComplete(int result) {
+ pac_request_ = NULL;
+
+ if (result != OK)
+ return result;
+
+ // TODO(mbelshe): consider retrying ResolveProxy if we came here via use of
+ // AlternateProtocol.
+
+ // Remove unsupported proxies from the list.
+ proxy_info()->RemoveProxiesWithoutScheme(
+ ProxyServer::SCHEME_DIRECT | ProxyServer::SCHEME_HTTP |
+ ProxyServer::SCHEME_SOCKS4 | ProxyServer::SCHEME_SOCKS5);
+
+ if (proxy_info()->is_empty()) {
+ // No proxies/direct to choose from. This happens when we don't support any
+ // of the proxies in the returned list.
+ return ERR_NO_SUPPORTED_PROXIES;
+ }
+
+ next_state_ = STATE_INIT_CONNECTION;
+ return OK;
+}
+
+int HttpStreamRequest::DoInitConnection() {
+ DCHECK(!connection_->is_initialized());
+ DCHECK(proxy_info()->proxy_server().is_valid());
+ next_state_ = STATE_INIT_CONNECTION_COMPLETE;
+
+ bool want_spdy_over_npn =
+ alternate_protocol_mode_ == kUsingAlternateProtocol &&
+ alternate_protocol_ == HttpAlternateProtocols::NPN_SPDY_2;
+ using_ssl_ = request_info().url.SchemeIs("https") ||
+ (force_spdy_always_ && force_spdy_over_ssl_) ||
+ want_spdy_over_npn;
+ using_spdy_ = false;
+
+ // Check first if we have a spdy session for this group. If so, then go
+ // straight to using that.
+ HostPortProxyPair pair(endpoint_, proxy_info()->ToPacString());
+ if (session_->spdy_session_pool()->HasSession(pair)) {
+ using_spdy_ = true;
+ next_state_ = STATE_INIT_STREAM;
+ return OK;
+ }
+
+ // Build the string used to uniquely identify connections of this type.
+ // Determine the host and port to connect to.
+ std::string connection_group = endpoint_.ToString();
+ DCHECK(!connection_group.empty());
+
+ if (using_ssl_)
+ connection_group = StringPrintf("ssl/%s", connection_group.c_str());
+
+ // If the user is refreshing the page, bypass the host cache.
+ bool disable_resolver_cache =
+ request_info().load_flags & LOAD_BYPASS_CACHE ||
+ request_info().load_flags & LOAD_VALIDATE_CACHE ||
+ request_info().load_flags & LOAD_DISABLE_CACHE;
+
+ // Build up the connection parameters.
+ scoped_refptr<TCPSocketParams> tcp_params;
+ scoped_refptr<HttpProxySocketParams> http_proxy_params;
+ scoped_refptr<SOCKSSocketParams> socks_params;
+ scoped_ptr<HostPortPair> proxy_host_port;
+
+ if (proxy_info()->is_direct()) {
+ tcp_params = new TCPSocketParams(endpoint_, request_info().priority,
+ request_info().referrer,
+ disable_resolver_cache);
+ } else {
+ ProxyServer proxy_server = proxy_info()->proxy_server();
+ proxy_host_port.reset(new HostPortPair(proxy_server.host_port_pair()));
+ scoped_refptr<TCPSocketParams> proxy_tcp_params =
+ new TCPSocketParams(*proxy_host_port, request_info().priority,
+ request_info().referrer, disable_resolver_cache);
+
+ if (proxy_info()->is_http()) {
+ GURL authentication_url = request_info().url;
+ if (using_ssl_ && !authentication_url.SchemeIs("https")) {
+ // If a proxy tunnel connection needs to be established due to
+ // an Alternate-Protocol, the URL needs to be changed to indicate
+ // https or digest authentication attempts will fail.
+ // For example, suppose the initial request was for
+ // "http://www.example.com/index.html". If this is an SSL
+ // upgrade due to alternate protocol, the digest authorization
+ // should have a uri="www.example.com:443" field rather than a
+ // "/index.html" entry, even though the original request URL has not
+ // changed.
+ authentication_url = UpgradeUrlToHttps(authentication_url);
+ }
+ establishing_tunnel_ = using_ssl_;
+ std::string user_agent;
+ request_info().extra_headers.GetHeader(HttpRequestHeaders::kUserAgent,
+ &user_agent);
+ http_proxy_params = new HttpProxySocketParams(proxy_tcp_params,
+ authentication_url,
+ user_agent,
+ endpoint_,
+ session_, using_ssl_);
+ } else {
+ DCHECK(proxy_info()->is_socks());
+ char socks_version;
+ if (proxy_server.scheme() == ProxyServer::SCHEME_SOCKS5)
+ socks_version = '5';
+ else
+ socks_version = '4';
+ connection_group =
+ StringPrintf("socks%c/%s", socks_version, connection_group.c_str());
+
+ socks_params = new SOCKSSocketParams(proxy_tcp_params,
+ socks_version == '5',
+ endpoint_,
+ request_info().priority,
+ request_info().referrer);
+ }
+ }
+
+ // Deal with SSL - which layers on top of any given proxy.
+ if (using_ssl_) {
+ if (factory_->IsTLSIntolerantServer(request_info().url)) {
+ LOG(WARNING) << "Falling back to SSLv3 because host is TLS intolerant: "
+ << GetHostAndPort(request_info().url);
+ ssl_config()->ssl3_fallback = true;
+ ssl_config()->tls1_enabled = false;
+ }
+
+ UMA_HISTOGRAM_ENUMERATION("Net.ConnectionUsedSSLv3Fallback",
+ static_cast<int>(ssl_config()->ssl3_fallback), 2);
+
+ int load_flags = request_info().load_flags;
+ if (factory_->ignore_certificate_errors())
+ load_flags |= LOAD_IGNORE_ALL_CERT_ERRORS;
+ if (request_info().load_flags & LOAD_VERIFY_EV_CERT)
+ ssl_config()->verify_ev_cert = true;
+
+ scoped_refptr<SSLSocketParams> ssl_params =
+ new SSLSocketParams(tcp_params, http_proxy_params, socks_params,
+ proxy_info()->proxy_server().scheme(),
+ request_info().url.HostNoBrackets(), *ssl_config(),
+ load_flags,
+ force_spdy_always_ && force_spdy_over_ssl_,
+ want_spdy_over_npn);
+
+ scoped_refptr<SSLClientSocketPool> ssl_pool;
+ if (proxy_info()->is_direct())
+ ssl_pool = session_->ssl_socket_pool();
+ else
+ ssl_pool = session_->GetSocketPoolForSSLWithProxy(*proxy_host_port);
+
+ return connection_->Init(connection_group, ssl_params,
+ request_info().priority, &io_callback_, ssl_pool,
+ net_log_);
+ }
+
+ // Finally, get the connection started.
+ if (proxy_info()->is_http()) {
+ return connection_->Init(
+ connection_group, http_proxy_params, request_info().priority,
+ &io_callback_, session_->GetSocketPoolForHTTPProxy(*proxy_host_port),
+ net_log_);
+ }
+
+ if (proxy_info()->is_socks()) {
+ return connection_->Init(
+ connection_group, socks_params, request_info().priority, &io_callback_,
+ session_->GetSocketPoolForSOCKSProxy(*proxy_host_port), net_log_);
+ }
+
+ DCHECK(proxy_info()->is_direct());
+ return connection_->Init(connection_group, tcp_params,
+ request_info().priority, &io_callback_,
+ session_->tcp_socket_pool(), net_log_);
+}
+
+int HttpStreamRequest::DoInitConnectionComplete(int result) {
+ // |result| may be the result of any of the stacked pools. The following
+ // logic is used when determining how to interpret an error.
+ // If |result| < 0:
+ // and connection_->socket() != NULL, then the SSL handshake ran and it
+ // is a potentially recoverable error.
+ // and connection_->socket == NULL and connection_->is_ssl_error() is true,
+ // then the SSL handshake ran with an unrecoverable error.
+ // otherwise, the error came from one of the other pools.
+ bool ssl_started = using_ssl_ && (result == OK || connection_->socket() ||
+ connection_->is_ssl_error());
+
+ if (ssl_started && (result == OK || IsCertificateError(result))) {
+ SSLClientSocket* ssl_socket =
+ static_cast<SSLClientSocket*>(connection_->socket());
+ if (ssl_socket->wasNpnNegotiated()) {
+ was_npn_negotiated_ = true;
+ std::string proto;
+ ssl_socket->GetNextProto(&proto);
+ SSLClientSocket::NextProto next_protocol =
+ SSLClientSocket::NextProtoFromString(proto);
+ // If we negotiated either version of SPDY, we must have
+ // advertised it, so allow it.
+ // TODO(mbelshe): verify it was a protocol we advertised?
+ if (next_protocol == SSLClientSocket::kProtoSPDY1 ||
+ next_protocol == SSLClientSocket::kProtoSPDY2) {
+ using_spdy_ = true;
+ }
+ }
+ if (force_spdy_over_ssl_ && force_spdy_always_)
+ using_spdy_ = true;
+ }
+
+ // We may be using spdy without SSL
+ if (!force_spdy_over_ssl_ && force_spdy_always_)
+ using_spdy_ = true;
+
+ if (result == ERR_PROXY_AUTH_REQUESTED) {
+ DCHECK(!ssl_started);
+ // Other state (i.e. |using_ssl_|) suggests that |connection_| will have an
+ // SSL socket, but there was an error before that could happen. This
+ // puts the in progress HttpProxy socket into |connection_| in order to
+ // complete the auth. The tunnel restart code is careful to remove it
+ // before returning control to the rest of this class.
+ connection_.reset(connection_->release_pending_http_proxy_connection());
+ return result;
+ }
+
+ if ((!ssl_started && result < 0 &&
+ alternate_protocol_mode_ == kUsingAlternateProtocol) ||
+ result == ERR_NPN_NEGOTIATION_FAILED) {
+ // Mark the alternate protocol as broken and fallback.
+ MarkBrokenAlternateProtocolAndFallback();
+ return OK;
+ }
+
+ if (result < 0 && !ssl_started) {
+ // A temporary CHECK for tracking down http://crbug.com/49862.
+ CHECK(!IsCertificateError(result));
+ return ReconsiderProxyAfterError(result);
+ }
+ establishing_tunnel_ = false;
+
+ if (connection_->socket()) {
+ LogHttpConnectedMetrics(*connection_);
+
+ // We officially have a new connection. Record the type.
+ if (!connection_->is_reused()) {
+ ConnectionType type = using_spdy_ ? CONNECTION_SPDY : CONNECTION_HTTP;
+ UpdateConnectionTypeHistograms(type);
+ }
+ }
+
+ // Handle SSL errors below.
+ if (using_ssl_) {
+ DCHECK(ssl_started);
+ if (IsCertificateError(result)) {
+ if (using_spdy_ && request_info().url.SchemeIs("http")) {
+ // We ignore certificate errors for http over spdy.
+ spdy_certificate_error_ = result;
+ result = OK;
+ } else {
+ result = HandleCertificateError(result);
+ if (result == OK && !connection_->socket()->IsConnectedAndIdle()) {
+ connection_->socket()->Disconnect();
+ connection_->Reset();
+ next_state_ = STATE_INIT_CONNECTION;
+ return result;
+ }
+ }
+ }
+ if (result < 0)
+ return HandleSSLHandshakeError(result);
+ }
+
+ next_state_ = STATE_INIT_STREAM;
+ return OK;
+}
+
+int HttpStreamRequest::DoWaitingUserAction(int result) {
+ // This state indicates that the stream request is in a partially
+ // completed state, and we've called back to the delegate for more
+ // information.
+
+ // We're always waiting here for the delegate to call us back.
+ return ERR_IO_PENDING;
+}
+
+int HttpStreamRequest::DoInitStream() {
+ next_state_ = STATE_INIT_STREAM_COMPLETE;
+
+ if (!using_spdy_) {
+ HttpBasicStream* stream = new HttpBasicStream(connection_.get());
+ stream_.reset(new HttpStreamHandle(connection_.release(), stream));
+ return stream_->InitializeStream(&request_info(), net_log_, &io_callback_);
+ }
+
+ CHECK(!stream_.get());
+
+ const scoped_refptr<SpdySessionPool> spdy_pool =
+ session_->spdy_session_pool();
+ scoped_refptr<SpdySession> spdy_session;
+
+ HostPortProxyPair pair(endpoint_, proxy_info()->ToPacString());
+ if (session_->spdy_session_pool()->HasSession(pair)) {
+ spdy_session =
+ session_->spdy_session_pool()->Get(pair, session_, net_log_);
+ } else {
+ // SPDY can be negotiated using the TLS next protocol negotiation (NPN)
+ // extension, or just directly using SSL. Either way, |connection_| must
+ // contain an SSLClientSocket.
+ CHECK(connection_->socket());
+ int error = spdy_pool->GetSpdySessionFromSocket(
+ pair, session_, connection_.release(), net_log_,
+ spdy_certificate_error_, &spdy_session, using_ssl_);
+ if (error != OK)
+ return error;
+ }
+
+ if (spdy_session->IsClosed())
+ return ERR_CONNECTION_CLOSED;
+
+ SpdyHttpStream* stream = new SpdyHttpStream(spdy_session);
+ stream_.reset(new HttpStreamHandle(NULL, stream));
+ return stream_->InitializeStream(&request_info(), net_log_, &io_callback_);
+}
+
+int HttpStreamRequest::DoInitStreamComplete(int result) {
+ if (result < 0)
+ return result;
+
+ next_state_ = STATE_NONE;
+ return OK;
+}
+
+int HttpStreamRequest::DoRestartTunnelAuth() {
+ next_state_ = STATE_RESTART_TUNNEL_AUTH_COMPLETE;
+ HttpProxyClientSocket* http_proxy_socket =
+ static_cast<HttpProxyClientSocket*>(connection_->socket());
+ return http_proxy_socket->RestartWithAuth(&io_callback_);
+}
+
+int HttpStreamRequest::DoRestartTunnelAuthComplete(int result) {
+ if (result == ERR_PROXY_AUTH_REQUESTED)
+ return result;
+
+ if (result == OK) {
+ // Now that we've got the HttpProxyClientSocket connected. We have
+ // to release it as an idle socket into the pool and start the connection
+ // process from the beginning. Trying to pass it in with the
+ // SSLSocketParams might cause a deadlock since params are dispatched
+ // interchangeably. This request won't necessarily get this http proxy
+ // socket, but there will be forward progress.
+ connection_->Reset();
+ establishing_tunnel_ = false;
+ next_state_ = STATE_INIT_CONNECTION;
+ return OK;
+ }
+
+ return ReconsiderProxyAfterError(result);
+}
+
+void HttpStreamRequest::MarkBrokenAlternateProtocolAndFallback() {
+ // We have to:
+ // * Reset the endpoint to be the unmodified URL specified destination.
+ // * Mark the endpoint as broken so we don't try again.
+ // * Set the alternate protocol mode to kDoNotUseAlternateProtocol so we
+ // ignore future Alternate-Protocol headers from the HostPortPair.
+ // * Reset the connection and go back to STATE_INIT_CONNECTION.
+
+ endpoint_ = HostPortPair(request_info().url.HostNoBrackets(),
+ request_info().url.EffectiveIntPort());
+
+ session_->mutable_alternate_protocols()->MarkBrokenAlternateProtocolFor(
+ endpoint_);
+
+ alternate_protocol_mode_ = kDoNotUseAlternateProtocol;
+ if (connection_->socket())
+ connection_->socket()->Disconnect();
+ connection_->Reset();
+ next_state_ = STATE_INIT_CONNECTION;
+}
+
+int HttpStreamRequest::ReconsiderProxyAfterError(int error) {
+ DCHECK(!pac_request_);
+
+ // A failure to resolve the hostname or any error related to establishing a
+ // TCP connection could be grounds for trying a new proxy configuration.
+ //
+ // Why do this when a hostname cannot be resolved? Some URLs only make sense
+ // to proxy servers. The hostname in those URLs might fail to resolve if we
+ // are still using a non-proxy config. We need to check if a proxy config
+ // now exists that corresponds to a proxy server that could load the URL.
+ //
+ switch (error) {
+ case ERR_NAME_NOT_RESOLVED:
+ case ERR_INTERNET_DISCONNECTED:
+ case ERR_ADDRESS_UNREACHABLE:
+ case ERR_CONNECTION_CLOSED:
+ case ERR_CONNECTION_RESET:
+ case ERR_CONNECTION_REFUSED:
+ case ERR_CONNECTION_ABORTED:
+ case ERR_TIMED_OUT:
+ case ERR_TUNNEL_CONNECTION_FAILED:
+ case ERR_SOCKS_CONNECTION_FAILED:
+ break;
+ case ERR_SOCKS_CONNECTION_HOST_UNREACHABLE:
+ // Remap the SOCKS-specific "host unreachable" error to a more
+ // generic error code (this way consumers like the link doctor
+ // know to substitute their error page).
+ //
+ // Note that if the host resolving was done by the SOCSK5 proxy, we can't
+ // differentiate between a proxy-side "host not found" versus a proxy-side
+ // "address unreachable" error, and will report both of these failures as
+ // ERR_ADDRESS_UNREACHABLE.
+ return ERR_ADDRESS_UNREACHABLE;
+ default:
+ return error;
+ }
+
+ if (request_info().load_flags & LOAD_BYPASS_PROXY) {
+ return error;
+ }
+
+ int rv = session_->proxy_service()->ReconsiderProxyAfterError(
+ request_info().url, proxy_info(), &io_callback_, &pac_request_,
+ net_log_);
+ if (rv == OK || rv == ERR_IO_PENDING) {
+ // If the error was during connection setup, there is no socket to
+ // disconnect.
+ if (connection_->socket())
+ connection_->socket()->Disconnect();
+ connection_->Reset();
+ next_state_ = STATE_RESOLVE_PROXY_COMPLETE;
+ } else {
+ // If ReconsiderProxyAfterError() failed synchronously, it means
+ // there was nothing left to fall-back to, so fail the transaction
+ // with the last connection error we got.
+ // TODO(eroman): This is a confusing contract, make it more obvious.
+ rv = error;
+ }
+
+ return rv;
+}
+
+int HttpStreamRequest::HandleCertificateError(int error) {
+ DCHECK(using_ssl_);
+ DCHECK(IsCertificateError(error));
+
+ SSLClientSocket* ssl_socket =
+ static_cast<SSLClientSocket*>(connection_->socket());
+ ssl_socket->GetSSLInfo(&ssl_info_);
+
+ // Add the bad certificate to the set of allowed certificates in the
+ // SSL info object. This data structure will be consulted after calling
+ // RestartIgnoringLastError(). And the user will be asked interactively
+ // before RestartIgnoringLastError() is ever called.
+ SSLConfig::CertAndStatus bad_cert;
+ bad_cert.cert = ssl_info_.cert;
+ bad_cert.cert_status = ssl_info_.cert_status;
+ ssl_config()->allowed_bad_certs.push_back(bad_cert);
+
+ int load_flags = request_info().load_flags;
+ if (factory_->ignore_certificate_errors())
+ load_flags |= LOAD_IGNORE_ALL_CERT_ERRORS;
+ if (ssl_socket->IgnoreCertError(error, load_flags))
+ return OK;
+ return error;
+}
+
+int HttpStreamRequest::HandleSSLHandshakeError(int error) {
+ if (ssl_config()->send_client_cert &&
+ (error == ERR_SSL_PROTOCOL_ERROR ||
+ error == ERR_BAD_SSL_CLIENT_AUTH_CERT)) {
+ session_->ssl_client_auth_cache()->Remove(
+ GetHostAndPort(request_info().url));
+ }
+
+ switch (error) {
+ case ERR_SSL_PROTOCOL_ERROR:
+ case ERR_SSL_VERSION_OR_CIPHER_MISMATCH:
+ case ERR_SSL_DECOMPRESSION_FAILURE_ALERT:
+ case ERR_SSL_BAD_RECORD_MAC_ALERT:
+ if (ssl_config()->tls1_enabled &&
+ !SSLConfigService::IsKnownStrictTLSServer(
+ request_info().url.host())) {
+ // This could be a TLS-intolerant server, an SSL 3.0 server that
+ // chose a TLS-only cipher suite or a server with buggy DEFLATE
+ // support. Turn off TLS 1.0, DEFLATE support and retry.
+ factory_->AddTLSIntolerantServer(request_info().url);
+ next_state_ = STATE_INIT_CONNECTION;
+ DCHECK(!connection_.get() || !connection_->socket());
+ error = OK;
+ }
+ break;
+ }
+ return error;
+}
+
+// static
+void HttpStreamRequest::LogHttpConnectedMetrics(
+ const ClientSocketHandle& handle) {
+ UMA_HISTOGRAM_ENUMERATION("Net.HttpSocketType", handle.reuse_type(),
+ ClientSocketHandle::NUM_TYPES);
+
+ switch (handle.reuse_type()) {
+ case ClientSocketHandle::UNUSED:
+ UMA_HISTOGRAM_CUSTOM_TIMES("Net.HttpConnectionLatency",
+ handle.setup_time(),
+ base::TimeDelta::FromMilliseconds(1),
+ base::TimeDelta::FromMinutes(10),
+ 100);
+ break;
+ case ClientSocketHandle::UNUSED_IDLE:
+ UMA_HISTOGRAM_CUSTOM_TIMES("Net.SocketIdleTimeBeforeNextUse_UnusedSocket",
+ handle.idle_time(),
+ base::TimeDelta::FromMilliseconds(1),
+ base::TimeDelta::FromMinutes(6),
+ 100);
+ break;
+ case ClientSocketHandle::REUSED_IDLE:
+ UMA_HISTOGRAM_CUSTOM_TIMES("Net.SocketIdleTimeBeforeNextUse_ReusedSocket",
+ handle.idle_time(),
+ base::TimeDelta::FromMilliseconds(1),
+ base::TimeDelta::FromMinutes(6),
+ 100);
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+}
+
+} // namespace net
+
diff --git a/net/http/http_stream_request.h b/net/http/http_stream_request.h
new file mode 100644
index 0000000..3039839
--- /dev/null
+++ b/net/http/http_stream_request.h
@@ -0,0 +1,205 @@
+// 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_HTTP_HTTP_STREAM_REQUEST_H_
+#define NET_HTTP_HTTP_STREAM_REQUEST_H_
+
+#include "base/scoped_ptr.h"
+#include "net/base/completion_callback.h"
+#include "net/base/host_mapping_rules.h"
+#include "net/base/ssl_config_service.h"
+#include "net/http/http_auth.h"
+#include "net/http/http_auth_controller.h"
+#include "net/http/http_alternate_protocols.h"
+#include "net/http/http_stream_factory.h"
+#include "net/http/stream_factory.h"
+#include "net/proxy/proxy_service.h"
+#include "net/socket/client_socket_handle.h"
+
+namespace net {
+
+class ClientSocketHandle;
+class HttpAuthController;
+class HttpNetworkSession;
+class HttpStreamFactory;
+class StreamRequestDelegate;
+
+// An HttpStreamRequest exists for each stream which is in progress of being
+// created for the StreamFactory.
+class HttpStreamRequest : public StreamFactory::StreamRequestJob {
+ public:
+ HttpStreamRequest(HttpStreamFactory* factory,
+ const scoped_refptr<HttpNetworkSession>& session);
+ virtual ~HttpStreamRequest();
+
+ // StreamRequest interface
+ virtual void Start(const HttpRequestInfo* request_info,
+ SSLConfig* ssl_config,
+ ProxyInfo* proxy_info,
+ StreamFactory::StreamRequestDelegate* delegate,
+ const BoundNetLog& net_log);
+ virtual void Cancel();
+ virtual int RestartWithCertificate(
+ const scoped_refptr<X509Certificate>& client_cert);
+ virtual int RestartTunnelWithProxyAuth(const string16& username,
+ const string16& password);
+ virtual LoadState GetLoadState() const;
+
+ virtual bool was_alternate_protocol_available() const {
+ return was_alternate_protocol_available_;
+ }
+ virtual bool was_npn_negotiated() const { return was_npn_negotiated_; }
+ virtual bool using_spdy() const { return using_spdy_; }
+
+ private:
+ enum AlternateProtocolMode {
+ kUnspecified, // Unspecified, check HttpAlternateProtocols
+ kUsingAlternateProtocol, // Using an alternate protocol
+ kDoNotUseAlternateProtocol, // Failed to connect once, do not try again.
+ };
+
+ enum State {
+ STATE_RESOLVE_PROXY,
+ STATE_RESOLVE_PROXY_COMPLETE,
+ STATE_INIT_CONNECTION,
+ STATE_INIT_CONNECTION_COMPLETE,
+ STATE_WAITING_USER_ACTION,
+ STATE_RESTART_TUNNEL_AUTH,
+ STATE_RESTART_TUNNEL_AUTH_COMPLETE,
+ STATE_INIT_STREAM,
+ STATE_INIT_STREAM_COMPLETE,
+ STATE_DRAIN_BODY_FOR_AUTH_RESTART,
+ STATE_DRAIN_BODY_FOR_AUTH_RESTART_COMPLETE,
+ STATE_NONE
+ };
+
+ const HttpRequestInfo& request_info() const;
+ ProxyInfo* proxy_info() const;
+ SSLConfig* ssl_config() const;
+
+ // Callbacks to the delegate.
+ void OnStreamReadyCallback(HttpStreamHandle* stream);
+ void OnStreamFailedCallback(int result);
+ void OnCertificateErrorCallback(int result, const SSLInfo& ssl_info);
+ void OnNeedsProxyAuthCallback(
+ const scoped_refptr<HttpAuthController>& auth_controller,
+ const HttpResponseInfo& response_info);
+ void OnNeedsClientAuthCallback(
+ const scoped_refptr<SSLCertRequestInfo>& cert_info);
+
+ void OnIOComplete(int result);
+ int RunLoop(int result);
+ int DoLoop(int result);
+
+ // Each of these methods corresponds to a State value. Those with an input
+ // argument receive the result from the previous state. If a method returns
+ // ERR_IO_PENDING, then the result from OnIOComplete will be passed to the
+ // next state method as the result arg.
+ int DoResolveProxy();
+ int DoResolveProxyComplete(int result);
+ int DoInitConnection();
+ int DoInitConnectionComplete(int result);
+ int DoWaitingUserAction(int result);
+ int DoInitStream();
+ int DoInitStreamComplete(int result);
+ int DoRestartTunnelAuth();
+ int DoRestartTunnelAuthComplete(int result);
+
+ // AlternateProtocol API
+ void MarkBrokenAlternateProtocolAndFallback();
+
+ // Retrieve SSLInfo from our SSL Socket.
+ // This must only be called when we are using an SSLSocket.
+ // After calling, the caller can use ssl_info_.
+ void GetSSLInfo();
+
+ // Called when we encounter a network error that could be resolved by trying
+ // a new proxy configuration. If there is another proxy configuration to try
+ // then this method sets next_state_ appropriately and returns either OK or
+ // ERR_IO_PENDING depending on whether or not the new proxy configuration is
+ // available synchronously or asynchronously. Otherwise, the given error
+ // code is simply returned.
+ int ReconsiderProxyAfterError(int error);
+
+ // Called to handle a certificate error. Stores the certificate in the
+ // allowed_bad_certs list, and checks if the error can be ignored. Returns
+ // OK if it can be ignored, or the error code otherwise.
+ int HandleCertificateError(int error);
+
+ // Called to handle a client certificate request.
+ int HandleCertificateRequest(int error);
+
+ // Called to possibly recover from an SSL handshake error. Sets next_state_
+ // and returns OK if recovering from the error. Otherwise, the same error
+ // code is returned.
+ int HandleSSLHandshakeError(int error);
+
+ // Record histograms of latency until Connect() completes.
+ static void LogHttpConnectedMetrics(const ClientSocketHandle& handle);
+
+ const HttpRequestInfo* request_info_; // Use request_info().
+ ProxyInfo* proxy_info_; // Use proxy_info().
+ SSLConfig* ssl_config_; // Use ssl_config().
+
+ scoped_refptr<HttpNetworkSession> session_;
+ CompletionCallbackImpl<HttpStreamRequest> io_callback_;
+ scoped_ptr<ClientSocketHandle> connection_;
+ scoped_refptr<HttpStreamFactory> factory_;
+ StreamFactory::StreamRequestDelegate* delegate_;
+ BoundNetLog net_log_;
+ State next_state_;
+ ProxyService::PacRequest* pac_request_;
+ SSLInfo ssl_info_;
+ // The hostname and port of the endpoint. This is not necessarily the one
+ // specified by the URL, due to Alternate-Protocol or fixed testing ports.
+ HostPortPair endpoint_;
+
+ // True if handling a HTTPS request, or using SPDY with SSL
+ bool using_ssl_;
+
+ // True if this network transaction is using SPDY instead of HTTP.
+ bool using_spdy_;
+
+ // Force spdy for all connections.
+ bool force_spdy_always_;
+
+ // Force spdy only for SSL connections.
+ bool force_spdy_over_ssl_;
+
+ // The certificate error while using SPDY over SSL for insecure URLs.
+ int spdy_certificate_error_;
+
+ scoped_refptr<HttpAuthController>
+ auth_controllers_[HttpAuth::AUTH_NUM_TARGETS];
+
+ AlternateProtocolMode alternate_protocol_mode_;
+
+ // Only valid if |alternate_protocol_mode_| == kUsingAlternateProtocol.
+ HttpAlternateProtocols::Protocol alternate_protocol_;
+
+ // True when the tunnel is in the process of being established - we can't
+ // read from the socket until the tunnel is done.
+ bool establishing_tunnel_;
+
+ scoped_ptr<HttpStreamHandle> stream_;
+
+ // True if finding the connection for this request found an alternate
+ // protocol was available.
+ bool was_alternate_protocol_available_;
+
+ // True if we negotiated NPN.
+ bool was_npn_negotiated_;
+
+ // Indicates that this StreamRequest has been cancelled. Note that once
+ // this has been cancelled, input parameters passed into the StreamRequest
+ // can no longer be touched (as they belong to the requestor).
+ bool cancelled_;
+
+ DISALLOW_COPY_AND_ASSIGN(HttpStreamRequest);
+};
+
+} // namespace net
+
+#endif // NET_HTTP_HTTP_STREAM_REQUEST_H_
+
diff --git a/net/http/stream_factory.h b/net/http/stream_factory.h
new file mode 100644
index 0000000..39b2241
--- /dev/null
+++ b/net/http/stream_factory.h
@@ -0,0 +1,150 @@
+// 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_HTTP_STREAM_FACTORY_H_
+#define NET_HTTP_STREAM_FACTORY_H_
+
+#include <string>
+
+#include "base/ref_counted.h"
+#include "net/base/load_states.h"
+
+namespace net {
+
+struct HttpRequestInfo;
+class BoundNetLog;
+class HostPortPair;
+class HttpAlternateProtocols;
+class HttpAuthController;
+class HttpNetworkSession;
+class HttpResponseInfo;
+class HttpStream;
+class HttpStreamHandle;
+class ProxyInfo;
+class SSLCertRequestInfo;
+class SSLInfo;
+
+// The StreamFactory defines an interface for creating usable HttpStreams.
+class StreamFactory {
+ public:
+ // The StreamRequestDelegate is a set of callback methods for a
+ // StreamRequestJob. Generally, only one of these methods will be
+ // called as a result of a stream request.
+ class StreamRequestDelegate {
+ public:
+ virtual ~StreamRequestDelegate() {}
+
+ // This is the success case.
+ // |stream| is now owned by the delegate.
+ virtual void OnStreamReady(HttpStreamHandle* stream) = 0;
+
+ // This is the failure to create a stream case.
+ virtual void OnStreamFailed(int status) = 0;
+
+ // Called when we have a certificate error for the request.
+ virtual void OnCertificateError(int status, const SSLInfo& ssl_info) = 0;
+
+ // This is the failure case where we need proxy authentication during
+ // proxy tunnel establishment. For the tunnel case, we were unable to
+ // create the HttpStream, so the caller provides the auth and then resumes
+ // the StreamRequest. For the non-tunnel case, the caller will handle
+ // the authentication failure and restart the StreamRequest entirely.
+ // Ownership of |auth_controller| and |proxy_response| are maintained
+ // by the StreamRequest. They are not guaranteed to be usable after the
+ // lifetime of this callback.
+ virtual void OnNeedsProxyAuth(
+ const scoped_refptr<HttpAuthController>& auth_controller,
+ const HttpResponseInfo& proxy_response) = 0;
+
+ // This is the failure for SSL Client Auth
+ // Ownership of |cert_info| is retained by the StreamRequest. The delegate
+ // may take a reference if it needs the cert_info beyond the lifetime of
+ // this callback.
+ virtual void OnNeedsClientAuth(
+ const scoped_refptr<SSLCertRequestInfo>& cert_info) = 0;
+ };
+
+ // The StreamRequestJob is the worker object which handles the creation
+ // of an HttpStream. While the HttpStream is being created, this job
+ // is the creator's handle for interacting with the HttpStream creation
+ // process.
+ class StreamRequestJob : public base::RefCounted<StreamRequestJob> {
+ public:
+ virtual ~StreamRequestJob() {}
+
+ // Start initiates the process of creating a new HttpStream.
+ // 3 parameters are passed in by reference. The caller asserts that the
+ // lifecycle of these parameters will remain valid until the stream is
+ // created, failed, or until the caller calls Cancel() on the stream
+ // request. In all cases, the delegate will be called to notify
+ // completion of the request.
+ virtual void Start(const HttpRequestInfo* request_info,
+ SSLConfig* ssl_config,
+ ProxyInfo* proxy_info,
+ StreamRequestDelegate* delegate,
+ const BoundNetLog& net_log) = 0;
+
+ // Cancel can be used to abort the HttpStream creation. Once cancelled,
+ // the delegates associated with the request will not be invoked.
+ virtual void Cancel() = 0;
+
+ // When a HttpStream creation process requires a SSL Certificate,
+ // the delegate OnNeedsClientAuth handler will have been called.
+ // It now becomes the delegate's responsibility to collect the certificate
+ // (probably from the user), and then call this method to resume
+ // the HttpStream creation process.
+ // Ownership of |client_cert| remains with the StreamRequest. The
+ // delegate can take a reference if needed beyond the lifetime of this
+ // call.
+ virtual int RestartWithCertificate(
+ const scoped_refptr<X509Certificate>& client_cert) = 0;
+
+ // When a HttpStream creation process is stalled due to necessity
+ // of Proxy authentication credentials, the delegate OnNeedsProxyAuth
+ // will have been called. It now becomes the delegate's responsibility
+ // to collect the necessary credentials, and then call this method to
+ // resume the HttpStream creation process.
+ virtual int RestartTunnelWithProxyAuth(const string16& username,
+ const string16& password) = 0;
+
+ // Returns the LoadState for the request.
+ virtual LoadState GetLoadState() const = 0;
+
+ // Returns true if an AlternateProtocol for this request was available.
+ virtual bool was_alternate_protocol_available() const = 0;
+
+ // Returns true if TLS/NPN was negotiated for this stream.
+ virtual bool was_npn_negotiated() const = 0;
+
+ // Returns true if this stream is being fetched over SPDY.
+ virtual bool using_spdy() const = 0;
+ };
+
+ virtual ~StreamFactory() {}
+
+ // Request a stream.
+ // Will callback to the StreamRequestDelegate upon completion.
+ virtual void RequestStream(const HttpRequestInfo* info,
+ SSLConfig* ssl_config,
+ ProxyInfo* proxy_info,
+ StreamRequestDelegate* delegate,
+ const BoundNetLog& net_log,
+ const scoped_refptr<HttpNetworkSession>& session,
+ scoped_refptr<StreamRequestJob>* stream) = 0;
+
+ // TLS Intolerant Server API
+ virtual void AddTLSIntolerantServer(const GURL& url) = 0;
+ virtual bool IsTLSIntolerantServer(const GURL& url) = 0;
+
+ // Alternate Protocol API
+ virtual void ProcessAlternateProtocol(
+ HttpAlternateProtocols* alternate_protocols,
+ const std::string& alternate_protocol_str,
+ const HostPortPair& http_host_port_pair) = 0;
+};
+
+} // namespace net
+
+#endif // NET_HTTP_STREAM_FACTORY_H_
+
diff --git a/net/net.gyp b/net/net.gyp
index 0b5b5d4..c370020 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -397,8 +397,13 @@
'http/http_response_info.cc',
'http/http_response_info.h',
'http/http_stream.h',
+ 'http/http_stream_factory.cc',
+ 'http/http_stream_factory.h',
+ 'http/http_stream_handle.h',
'http/http_stream_parser.cc',
'http/http_stream_parser.h',
+ 'http/http_stream_request.cc',
+ 'http/http_stream_request.h',
'http/http_transaction.h',
'http/http_transaction_factory.h',
'http/url_security_manager.h',
@@ -419,6 +424,7 @@
'http/md4.h',
'http/partial_data.cc',
'http/partial_data.h',
+ 'http/stream_factory.h',
'ocsp/nss_ocsp.cc',
'ocsp/nss_ocsp.h',
'proxy/init_proxy_resolver.cc',
diff --git a/net/socket/client_socket_handle.h b/net/socket/client_socket_handle.h
index a3de0d6..c9d636b 100644
--- a/net/socket/client_socket_handle.h
+++ b/net/socket/client_socket_handle.h
@@ -147,19 +147,6 @@ class ClientSocketHandle {
return UNUSED_IDLE;
}
}
- bool ShouldResendFailedRequest(int error) const {
- // NOTE: we resend a request only if we reused a keep-alive connection.
- // This automatically prevents an infinite resend loop because we'll run
- // out of the cached keep-alive connections eventually.
- if ( // We used a socket that was never idle.
- reuse_type() == ClientSocketHandle::UNUSED ||
- // We used an unused, idle socket and got a error that wasn't a TCP RST.
- (reuse_type() == ClientSocketHandle::UNUSED_IDLE &&
- (error != OK && error != ERR_CONNECTION_RESET))) {
- return false;
- }
- return true;
- }
private:
// Called on asynchronous completion of an Init() request.
diff --git a/net/spdy/spdy_http_stream.cc b/net/spdy/spdy_http_stream.cc
index 52c99f4..41b18aa 100644
--- a/net/spdy/spdy_http_stream.cc
+++ b/net/spdy/spdy_http_stream.cc
@@ -14,6 +14,7 @@
#include "base/string_util.h"
#include "net/base/load_flags.h"
#include "net/base/net_util.h"
+#include "net/base/ssl_cert_request_info.h"
#include "net/http/http_request_info.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_response_info.h"
@@ -257,6 +258,12 @@ int SpdyHttpStream::ReadResponseBody(
return ERR_IO_PENDING;
}
+void SpdyHttpStream::Close(bool not_reusable) {
+ // Note: the not_reusable flag has no meaning for SPDY streams.
+
+ Cancel();
+}
+
int SpdyHttpStream::SendRequest(const std::string& /*headers_string*/,
UploadDataStream* request_body,
HttpResponseInfo* response,
@@ -358,6 +365,11 @@ int SpdyHttpStream::OnResponseReceived(const spdy::SpdyHeaderBlock& response,
response_info_ = push_response_info_.get();
}
+ // TODO(mbelshe): This is the time of all headers received, not just time
+ // to first byte.
+ DCHECK(response_info_->response_time.is_null());
+ response_info_->response_time = base::Time::Now();
+
if (!SpdyHeadersToHttpResponse(response, response_info_)) {
status = ERR_INVALID_RESPONSE;
} else {
@@ -409,10 +421,6 @@ void SpdyHttpStream::OnClose(int status) {
DoCallback(status);
}
-bool SpdyHttpStream::ShouldResendFailedRequest(int error) const {
- return spdy_session_->ShouldResendFailedRequest(error);
-}
-
void SpdyHttpStream::ScheduleBufferedReadCallback() {
// If there is already a scheduled DoBufferedReadCallback, don't issue
// another one. Mark that we have received more data and return.
@@ -485,4 +493,16 @@ void SpdyHttpStream::DoCallback(int rv) {
c->Run(rv);
}
+void SpdyHttpStream::GetSSLInfo(SSLInfo* ssl_info) {
+ DCHECK(stream_);
+ bool using_npn;
+ stream_->GetSSLInfo(ssl_info, &using_npn);
+}
+
+void SpdyHttpStream::GetSSLCertRequestInfo(
+ SSLCertRequestInfo* cert_request_info) {
+ DCHECK(stream_);
+ stream_->GetSSLCertRequestInfo(cert_request_info);
+}
+
} // namespace net
diff --git a/net/spdy/spdy_http_stream.h b/net/spdy/spdy_http_stream.h
index b333742..2fe15c8 100644
--- a/net/spdy/spdy_http_stream.h
+++ b/net/spdy/spdy_http_stream.h
@@ -17,6 +17,7 @@
#include "net/http/http_request_info.h"
#include "net/http/http_stream.h"
#include "net/spdy/spdy_protocol.h"
+#include "net/spdy/spdy_session.h"
#include "net/spdy/spdy_stream.h"
namespace net {
@@ -66,6 +67,9 @@ class SpdyHttpStream : public SpdyStream::Delegate, public HttpStream {
virtual int ReadResponseBody(
IOBuffer* buf, int buf_len, CompletionCallback* callback);
+ // Closes the stream.
+ virtual void Close(bool not_reusable);
+
// Indicates if the response body has been completely read.
virtual bool IsResponseBodyComplete() const {
return stream_->closed();
@@ -77,6 +81,17 @@ class SpdyHttpStream : public SpdyStream::Delegate, public HttpStream {
// A SPDY stream never has more data after the FIN.
virtual bool IsMoreDataBuffered() const { return false; }
+ virtual bool IsConnectionReused() const {
+ return spdy_session_->IsReused();
+ }
+
+ virtual void SetConnectionReused() {
+ // SPDY doesn't need an indicator here.
+ }
+
+ virtual void GetSSLInfo(SSLInfo* ssl_info);
+ virtual void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info);
+
// ===================================================
// SpdyStream::Delegate.
@@ -113,8 +128,6 @@ class SpdyHttpStream : public SpdyStream::Delegate, public HttpStream {
// ReadResponseHeaders or ReadResponseBody.
virtual void OnClose(int status);
- bool ShouldResendFailedRequest(int error) const;
-
private:
FRIEND_TEST_ALL_PREFIXES(SpdyNetworkTransactionTest, FlowControlStallResume);
diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc
index a4bd90d..112ade7 100644
--- a/net/spdy/spdy_network_transaction_unittest.cc
+++ b/net/spdy/spdy_network_transaction_unittest.cc
@@ -6,6 +6,7 @@
#include <vector>
#include "net/base/net_log_unittest.h"
+#include "net/http/http_stream_handle.h"
#include "net/http/http_transaction_unittest.h"
#include "net/spdy/spdy_http_stream.h"
#include "net/spdy/spdy_session.h"
@@ -93,24 +94,24 @@ class SpdyNetworkTransactionTest
if (!session_.get())
session_ = SpdySessionDependencies::SpdyCreateSession(
session_deps_.get());
- HttpNetworkTransaction::SetUseAlternateProtocols(false);
- HttpNetworkTransaction::SetUseSSLOverSpdyWithoutNPN(false);
- HttpNetworkTransaction::SetUseSpdyWithoutNPN(false);
+ HttpStreamFactory::set_use_alternate_protocols(false);
+ HttpStreamFactory::set_force_spdy_over_ssl(false);
+ HttpStreamFactory::set_force_spdy_always(false);
switch (test_type_) {
case SPDYNPN:
session_->mutable_alternate_protocols()->SetAlternateProtocolFor(
HostPortPair("www.google.com", 80), 443,
HttpAlternateProtocols::NPN_SPDY_2);
- HttpNetworkTransaction::SetUseAlternateProtocols(true);
- HttpNetworkTransaction::SetNextProtos(kExpectedNPNString);
+ HttpStreamFactory::set_use_alternate_protocols(true);
+ HttpStreamFactory::set_next_protos(kExpectedNPNString);
break;
case SPDYNOSSL:
- HttpNetworkTransaction::SetUseSSLOverSpdyWithoutNPN(false);
- HttpNetworkTransaction::SetUseSpdyWithoutNPN(true);
+ HttpStreamFactory::set_force_spdy_over_ssl(false);
+ HttpStreamFactory::set_force_spdy_always(true);
break;
case SPDYSSL:
- HttpNetworkTransaction::SetUseSSLOverSpdyWithoutNPN(true);
- HttpNetworkTransaction::SetUseSpdyWithoutNPN(true);
+ HttpStreamFactory::set_force_spdy_over_ssl(true);
+ HttpStreamFactory::set_force_spdy_always(true);
break;
default:
NOTREACHED();
@@ -1274,12 +1275,7 @@ TEST_P(SpdyNetworkTransactionTest, PostWithEarlySynReply) {
request.upload_data, NULL));
ASSERT_EQ(request.upload_data->GetContentLength(), stream->size());
- scoped_ptr<spdy::SpdyFrame>
- req(ConstructSpdyPost(request.upload_data->GetContentLength(), NULL, 0));
scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true));
- MockWrite writes[] = {
- CreateMockWrite(*req.get(), 2),
- };
scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyPostSynReply(NULL, 0));
MockRead reads[] = {
@@ -1289,8 +1285,7 @@ TEST_P(SpdyNetworkTransactionTest, PostWithEarlySynReply) {
};
scoped_refptr<DelayedSocketData> data(
- new DelayedSocketData(0, reads, arraysize(reads),
- writes, arraysize(writes)));
+ new DelayedSocketData(0, reads, arraysize(reads), NULL, 0));
NormalSpdyTransactionHelper helper(request,
BoundNetLog(), GetParam());
helper.RunPreTestSetup();
@@ -1424,7 +1419,8 @@ TEST_P(SpdyNetworkTransactionTest, ResponseWithTwoSynReplies) {
// writes are ordered so that writing tests like these are easy, right now
// we are working around the limitations as described above and it's not
// deterministic, tests may fail under specific circumstances.
-TEST_P(SpdyNetworkTransactionTest, WindowUpdateReceived) {
+// TODO(mbelshe): Disabling until we have deterministic sockets!
+TEST_P(SpdyNetworkTransactionTest, DISABLED_WindowUpdateReceived) {
SpdySession::SetFlowControl(true);
static int kFrameCount = 2;
@@ -1488,7 +1484,7 @@ TEST_P(SpdyNetworkTransactionTest, WindowUpdateReceived) {
EXPECT_EQ(OK, rv);
SpdyHttpStream* stream =
- static_cast<SpdyHttpStream*>(trans->stream_.get());
+ static_cast<SpdyHttpStream*>(trans->stream_->stream());
ASSERT_TRUE(stream != NULL);
ASSERT_TRUE(stream->stream() != NULL);
EXPECT_EQ(spdy::kInitialWindowSize +
@@ -1538,6 +1534,7 @@ TEST_P(SpdyNetworkTransactionTest, WindowUpdateOverflow) {
CreateMockRead(*window_update),
CreateMockRead(*window_update),
CreateMockRead(*window_update),
+ MockRead(true, ERR_IO_PENDING, 0), // Wait for the RST to be written.
MockRead(true, 0, 0) // EOF
};
@@ -1567,6 +1564,8 @@ TEST_P(SpdyNetworkTransactionTest, WindowUpdateOverflow) {
rv = callback.WaitForResult();
EXPECT_EQ(ERR_SPDY_PROTOCOL_ERROR, rv);
+ data->CompleteRead();
+
ASSERT_TRUE(helper.session() != NULL);
ASSERT_TRUE(helper.session()->spdy_session_pool() != NULL);
helper.session()->spdy_session_pool()->ClearSessions();
@@ -1671,7 +1670,7 @@ TEST_P(SpdyNetworkTransactionTest, FlowControlStallResume) {
MessageLoop::current()->RunAllPending(); // Write as much as we can.
SpdyHttpStream* stream =
- static_cast<SpdyHttpStream*>(trans->stream_.get());
+ static_cast<SpdyHttpStream*>(trans->stream_->stream());
ASSERT_TRUE(stream != NULL);
ASSERT_TRUE(stream->stream() != NULL);
EXPECT_EQ(0, stream->stream()->send_window_size());
@@ -1825,7 +1824,7 @@ TEST_P(SpdyNetworkTransactionTest, StartTransactionOnReadCallback) {
new OrderedSocketData(reads, arraysize(reads),
writes, arraysize(writes)));
scoped_refptr<DelayedSocketData> data2(
- new DelayedSocketData(0, reads2, arraysize(reads2),
+ new DelayedSocketData(1, reads2, arraysize(reads2),
writes2, arraysize(writes2)));
NormalSpdyTransactionHelper helper(CreateGetRequest(),
@@ -1989,8 +1988,8 @@ TEST_P(SpdyNetworkTransactionTest, RedirectGetRequest) {
writes2, arraysize(writes2)));
// TODO(erikchen): Make test support SPDYSSL, SPDYNPN
- HttpNetworkTransaction::SetUseSSLOverSpdyWithoutNPN(false);
- HttpNetworkTransaction::SetUseSpdyWithoutNPN(true);
+ HttpStreamFactory::set_force_spdy_over_ssl(false);
+ HttpStreamFactory::set_force_spdy_always(true);
TestDelegate d;
{
URLRequest r(GURL("http://www.google.com/"), &d);
@@ -2109,8 +2108,8 @@ TEST_P(SpdyNetworkTransactionTest, RedirectServerPush) {
writes2, arraysize(writes2)));
// TODO(erikchen): Make test support SPDYSSL, SPDYNPN
- HttpNetworkTransaction::SetUseSSLOverSpdyWithoutNPN(false);
- HttpNetworkTransaction::SetUseSpdyWithoutNPN(true);
+ HttpStreamFactory::set_force_spdy_over_ssl(false);
+ HttpStreamFactory::set_force_spdy_always(true);
TestDelegate d;
TestDelegate d2;
{
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index b1e9c23..07de80f 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -916,6 +916,17 @@ bool SpdySession::GetSSLInfo(SSLInfo* ssl_info, bool* was_npn_negotiated) {
return false;
}
+bool SpdySession::GetSSLCertRequestInfo(
+ SSLCertRequestInfo* cert_request_info) {
+ if (is_secure_) {
+ SSLClientSocket* ssl_socket =
+ reinterpret_cast<SSLClientSocket*>(connection_->socket());
+ ssl_socket->GetSSLCertRequestInfo(cert_request_info);
+ return true;
+ }
+ return false;
+}
+
void SpdySession::OnError(spdy::SpdyFramer* framer) {
LOG(ERROR) << "SpdySession error: " << framer->error_code();
CloseSessionOnError(net::ERR_SPDY_PROTOCOL_ERROR);
diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h
index 72e8c06..4230289 100644
--- a/net/spdy/spdy_session.h
+++ b/net/spdy/spdy_session.h
@@ -132,6 +132,10 @@ class SpdySession : public base::RefCounted<SpdySession>,
// Fills SSL info in |ssl_info| and returns true when SSL is in use.
bool GetSSLInfo(SSLInfo* ssl_info, bool* was_npn_negotiated);
+ // Fills SSL Certificate Request info |cert_request_info| and returns
+ // true when SSL is in use.
+ bool GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info);
+
// Enable or disable SSL.
static void SetSSLMode(bool enable) { use_ssl_ = enable; }
static bool SSLMode() { return use_ssl_; }
@@ -148,12 +152,9 @@ class SpdySession : public base::RefCounted<SpdySession>,
// error.
void CloseSessionOnError(net::Error err);
- // Indicates whether we should retry failed requets on a session.
- bool ShouldResendFailedRequest(int error) const {
- // NOTE: we resend a request only if this connection has successfully
- // been used for some data receiving. Otherwise, we assume the error
- // is not transient.
- // This is primarily for use with recovery from a TCP RESET.
+ // Indicates whether the session is being reused after having successfully
+ // used to send/receive data in the past.
+ bool IsReused() const {
return frames_received_ > 0;
}
diff --git a/net/spdy/spdy_stream.cc b/net/spdy/spdy_stream.cc
index 09129fc..71c0901 100644
--- a/net/spdy/spdy_stream.cc
+++ b/net/spdy/spdy_stream.cc
@@ -277,6 +277,10 @@ bool SpdyStream::GetSSLInfo(SSLInfo* ssl_info, bool* was_npn_negotiated) {
return session_->GetSSLInfo(ssl_info, was_npn_negotiated);
}
+bool SpdyStream::GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info) {
+ return session_->GetSSLCertRequestInfo(cert_request_info);
+}
+
int SpdyStream::DoLoop(int result) {
do {
State state = io_state_;
diff --git a/net/spdy/spdy_stream.h b/net/spdy/spdy_stream.h
index 1b0a3f6..7cabdc1 100644
--- a/net/spdy/spdy_stream.h
+++ b/net/spdy/spdy_stream.h
@@ -22,6 +22,7 @@
namespace net {
class SpdySession;
+class SSLCertRequestInfo;
class SSLInfo;
// The SpdyStream is used by the SpdySession to represent each stream known
@@ -168,8 +169,13 @@ class SpdyStream : public base::RefCounted<SpdyStream> {
int WriteStreamData(IOBuffer* data, int length,
spdy::SpdyDataFlags flags);
+ // Fills SSL info in |ssl_info| and returns true when SSL is in use.
bool GetSSLInfo(SSLInfo* ssl_info, bool* was_npn_negotiated);
+ // Fills SSL Certificate Request info |cert_request_info| and returns
+ // true when SSL is in use.
+ bool GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info);
+
bool is_idle() const {
return io_state_ == STATE_OPEN || io_state_ == STATE_DONE;
}