diff options
author | mbelshe@chromium.org <mbelshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-19 05:56:38 +0000 |
---|---|---|
committer | mbelshe@chromium.org <mbelshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-19 05:56:38 +0000 |
commit | 8e6441caa3adedf373b8ff80bb1fb48caff81556 (patch) | |
tree | 4a37d2fe5aaa3aad04b01171feb04e329c7e0383 /net | |
parent | 33f275a2e2e703c58f514af8efe963e2e29792f9 (diff) | |
download | chromium_src-8e6441caa3adedf373b8ff80bb1fb48caff81556.zip chromium_src-8e6441caa3adedf373b8ff80bb1fb48caff81556.tar.gz chromium_src-8e6441caa3adedf373b8ff80bb1fb48caff81556.tar.bz2 |
Extract connection logic from HttpNetworkTransaction into a new
HttpStreamFactory. The HttpNetworkTransaction now deals exclusively with
streams rather than connections directly. This cut the size of HTN roughly in
in half.
The HttpNetworkTransaction is still responsible for all proxy and server
authentication functions. This is because the streams may come and go - we
could create a stream, have the server declare auth is needed, and then the
next attempt would be on a different stream. So Auth belongs on the HNT.
The HNT no longer has direct access to the connection itself; instead, it
only knows of an HttpStream.
The StreamRequest, however, is responsible for determining whether the
connection needs to use a proxy, whether AlternateProtocols are available, and
whether the connection should be SPDY or HTTP.
Other changes:
- moved some static configuration methods from HNT to HttpStreamFactory.
- added some methods to the HttpStream.
BUG=none
TEST=all
Review URL: http://codereview.chromium.org/3171002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@56646 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
28 files changed, 2227 insertions, 1150 deletions
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; } |