This source file includes following definitions.
- NetLogHttpStreamJobCallback
- NetLogHttpStreamProtoCallback
- ptr_factory_
- Start
- Preconnect
- RestartTunnelWithProxyAuth
- GetLoadState
- MarkAsAlternate
- WaitFor
- Resume
- Orphan
- SetPriority
- was_npn_negotiated
- protocol_negotiated
- using_spdy
- server_ssl_config
- proxy_ssl_config
- proxy_info
- GetSSLInfo
- GetSpdySessionKey
- CanUseExistingSpdySession
- OnStreamReadyCallback
- OnWebSocketHandshakeStreamReadyCallback
- OnNewSpdySessionReadyCallback
- OnStreamFailedCallback
- OnCertificateErrorCallback
- OnNeedsProxyAuthCallback
- OnNeedsClientAuthCallback
- OnHttpsProxyTunnelResponseCallback
- OnPreconnectsComplete
- OnHostResolution
- OnIOComplete
- RunLoop
- DoLoop
- StartInternal
- DoStart
- DoResolveProxy
- DoResolveProxyComplete
- ShouldForceSpdySSL
- ShouldForceSpdyWithoutSSL
- ShouldForceQuic
- DoWaitForJob
- DoWaitForJobComplete
- DoInitConnection
- DoInitConnectionComplete
- DoWaitingUserAction
- DoCreateStream
- DoCreateStreamComplete
- DoRestartTunnelAuth
- DoRestartTunnelAuthComplete
- ReturnToStateInitConnection
- SetSocketMotivation
- IsHttpsProxyAndHttpUrl
- InitSSLConfig
- ReconsiderProxyAfterError
- HandleCertificateError
- SwitchToSpdyMode
- LogHttpConnectedMetrics
- IsPreconnecting
- IsOrphaned
- IsRequestEligibleForPipelining
#include "net/http/http_stream_factory_impl_job.h"
#include <algorithm>
#include <string>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "build/build_config.h"
#include "net/base/connection_type_histograms.h"
#include "net/base/net_log.h"
#include "net/base/net_util.h"
#include "net/http/http_basic_stream.h"
#include "net/http/http_network_session.h"
#include "net/http/http_pipelined_connection.h"
#include "net/http/http_pipelined_host.h"
#include "net/http/http_pipelined_host_pool.h"
#include "net/http/http_pipelined_stream.h"
#include "net/http/http_proxy_client_socket.h"
#include "net/http/http_proxy_client_socket_pool.h"
#include "net/http/http_request_info.h"
#include "net/http/http_server_properties.h"
#include "net/http/http_stream_factory.h"
#include "net/http/http_stream_factory_impl_request.h"
#include "net/quic/quic_http_stream.h"
#include "net/socket/client_socket_handle.h"
#include "net/socket/client_socket_pool.h"
#include "net/socket/client_socket_pool_manager.h"
#include "net/socket/socks_client_socket_pool.h"
#include "net/socket/ssl_client_socket.h"
#include "net/socket/ssl_client_socket_pool.h"
#include "net/spdy/spdy_http_stream.h"
#include "net/spdy/spdy_session.h"
#include "net/spdy/spdy_session_pool.h"
#include "net/ssl/ssl_cert_request_info.h"
namespace net {
base::Value* NetLogHttpStreamJobCallback(const GURL* original_url,
const GURL* url,
RequestPriority priority,
NetLog::LogLevel ) {
base::DictionaryValue* dict = new base::DictionaryValue();
dict->SetString("original_url", original_url->GetOrigin().spec());
dict->SetString("url", url->GetOrigin().spec());
dict->SetString("priority", RequestPriorityToString(priority));
return dict;
}
base::Value* NetLogHttpStreamProtoCallback(
const SSLClientSocket::NextProtoStatus status,
const std::string* proto,
const std::string* server_protos,
NetLog::LogLevel ) {
base::DictionaryValue* dict = new base::DictionaryValue();
dict->SetString("next_proto_status",
SSLClientSocket::NextProtoStatusToString(status));
dict->SetString("proto", *proto);
dict->SetString("server_protos",
SSLClientSocket::ServerProtosToString(*server_protos));
return dict;
}
HttpStreamFactoryImpl::Job::Job(HttpStreamFactoryImpl* stream_factory,
HttpNetworkSession* session,
const HttpRequestInfo& request_info,
RequestPriority priority,
const SSLConfig& server_ssl_config,
const SSLConfig& proxy_ssl_config,
NetLog* net_log)
: request_(NULL),
request_info_(request_info),
priority_(priority),
server_ssl_config_(server_ssl_config),
proxy_ssl_config_(proxy_ssl_config),
net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_HTTP_STREAM_JOB)),
io_callback_(base::Bind(&Job::OnIOComplete, base::Unretained(this))),
connection_(new ClientSocketHandle),
session_(session),
stream_factory_(stream_factory),
next_state_(STATE_NONE),
pac_request_(NULL),
blocking_job_(NULL),
waiting_job_(NULL),
using_ssl_(false),
using_spdy_(false),
using_quic_(false),
quic_request_(session_->quic_stream_factory()),
force_spdy_always_(HttpStreamFactory::force_spdy_always()),
force_spdy_over_ssl_(HttpStreamFactory::force_spdy_over_ssl()),
spdy_certificate_error_(OK),
establishing_tunnel_(false),
was_npn_negotiated_(false),
protocol_negotiated_(kProtoUnknown),
num_streams_(0),
spdy_session_direct_(false),
existing_available_pipeline_(false),
ptr_factory_(this) {
DCHECK(stream_factory);
DCHECK(session);
}
HttpStreamFactoryImpl::Job::~Job() {
net_log_.EndEvent(NetLog::TYPE_HTTP_STREAM_JOB);
if (next_state_ == STATE_WAITING_USER_ACTION) {
connection_->socket()->Disconnect();
connection_.reset();
}
if (pac_request_)
session_->proxy_service()->CancelPacRequest(pac_request_);
if (stream_.get() && next_state_ != STATE_DONE)
stream_->Close(true );
}
void HttpStreamFactoryImpl::Job::Start(Request* request) {
DCHECK(request);
request_ = request;
StartInternal();
}
int HttpStreamFactoryImpl::Job::Preconnect(int num_streams) {
DCHECK_GT(num_streams, 0);
HostPortPair origin_server =
HostPortPair(request_info_.url.HostNoBrackets(),
request_info_.url.EffectiveIntPort());
base::WeakPtr<HttpServerProperties> http_server_properties =
session_->http_server_properties();
if (http_server_properties &&
http_server_properties->SupportsSpdy(origin_server)) {
num_streams_ = 1;
} else {
num_streams_ = num_streams;
}
return StartInternal();
}
int HttpStreamFactoryImpl::Job::RestartTunnelWithProxyAuth(
const AuthCredentials& credentials) {
DCHECK(establishing_tunnel_);
next_state_ = STATE_RESTART_TUNNEL_AUTH;
stream_.reset();
return RunLoop(OK);
}
LoadState HttpStreamFactoryImpl::Job::GetLoadState() const {
switch (next_state_) {
case STATE_RESOLVE_PROXY_COMPLETE:
return session_->proxy_service()->GetLoadState(pac_request_);
case STATE_INIT_CONNECTION_COMPLETE:
case STATE_CREATE_STREAM_COMPLETE:
return using_quic_ ? LOAD_STATE_CONNECTING : connection_->GetLoadState();
default:
return LOAD_STATE_IDLE;
}
}
void HttpStreamFactoryImpl::Job::MarkAsAlternate(
const GURL& original_url,
PortAlternateProtocolPair alternate) {
DCHECK(!original_url_.get());
original_url_.reset(new GURL(original_url));
if (alternate.protocol == QUIC) {
DCHECK(session_->params().enable_quic);
using_quic_ = true;
}
}
void HttpStreamFactoryImpl::Job::WaitFor(Job* job) {
DCHECK_EQ(STATE_NONE, next_state_);
DCHECK_EQ(STATE_NONE, job->next_state_);
DCHECK(!blocking_job_);
DCHECK(!job->waiting_job_);
blocking_job_ = job;
job->waiting_job_ = this;
}
void HttpStreamFactoryImpl::Job::Resume(Job* job) {
DCHECK_EQ(blocking_job_, job);
blocking_job_ = NULL;
if (next_state_ == STATE_WAIT_FOR_JOB_COMPLETE) {
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&HttpStreamFactoryImpl::Job::OnIOComplete,
ptr_factory_.GetWeakPtr(), OK));
}
}
void HttpStreamFactoryImpl::Job::Orphan(const Request* request) {
DCHECK_EQ(request_, request);
request_ = NULL;
if (blocking_job_) {
DCHECK(blocking_job_->waiting_job_);
blocking_job_->waiting_job_ = NULL;
blocking_job_ = NULL;
if (stream_factory_->for_websockets_ &&
connection_ && connection_->socket())
connection_->socket()->Disconnect();
stream_factory_->OnOrphanedJobComplete(this);
} else if (stream_factory_->for_websockets_) {
if (connection_ && connection_->socket())
connection_->socket()->Disconnect();
stream_factory_->OnOrphanedJobComplete(this);
}
}
void HttpStreamFactoryImpl::Job::SetPriority(RequestPriority priority) {
priority_ = priority;
}
bool HttpStreamFactoryImpl::Job::was_npn_negotiated() const {
return was_npn_negotiated_;
}
NextProto HttpStreamFactoryImpl::Job::protocol_negotiated() const {
return protocol_negotiated_;
}
bool HttpStreamFactoryImpl::Job::using_spdy() const {
return using_spdy_;
}
const SSLConfig& HttpStreamFactoryImpl::Job::server_ssl_config() const {
return server_ssl_config_;
}
const SSLConfig& HttpStreamFactoryImpl::Job::proxy_ssl_config() const {
return proxy_ssl_config_;
}
const ProxyInfo& HttpStreamFactoryImpl::Job::proxy_info() const {
return proxy_info_;
}
void HttpStreamFactoryImpl::Job::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_);
}
SpdySessionKey HttpStreamFactoryImpl::Job::GetSpdySessionKey() const {
PrivacyMode privacy_mode = request_info_.privacy_mode;
if (IsHttpsProxyAndHttpUrl()) {
return SpdySessionKey(proxy_info_.proxy_server().host_port_pair(),
ProxyServer::Direct(),
privacy_mode);
} else {
return SpdySessionKey(origin_,
proxy_info_.proxy_server(),
privacy_mode);
}
}
bool HttpStreamFactoryImpl::Job::CanUseExistingSpdySession() const {
return request_info_.url.SchemeIs("https") ||
proxy_info_.proxy_server().is_https() ||
force_spdy_always_;
}
void HttpStreamFactoryImpl::Job::OnStreamReadyCallback() {
DCHECK(stream_.get());
DCHECK(!IsPreconnecting());
DCHECK(!stream_factory_->for_websockets_);
if (IsOrphaned()) {
stream_factory_->OnOrphanedJobComplete(this);
} else {
request_->Complete(was_npn_negotiated(),
protocol_negotiated(),
using_spdy(),
net_log_);
request_->OnStreamReady(this, server_ssl_config_, proxy_info_,
stream_.release());
}
}
void HttpStreamFactoryImpl::Job::OnWebSocketHandshakeStreamReadyCallback() {
DCHECK(websocket_stream_);
DCHECK(!IsPreconnecting());
DCHECK(stream_factory_->for_websockets_);
DCHECK(!IsOrphaned());
request_->Complete(was_npn_negotiated(),
protocol_negotiated(),
using_spdy(),
net_log_);
request_->OnWebSocketHandshakeStreamReady(this,
server_ssl_config_,
proxy_info_,
websocket_stream_.release());
}
void HttpStreamFactoryImpl::Job::OnNewSpdySessionReadyCallback() {
DCHECK(stream_.get());
DCHECK(!IsPreconnecting());
DCHECK(using_spdy());
base::WeakPtr<SpdySession> spdy_session = new_spdy_session_;
new_spdy_session_.reset();
if (IsOrphaned()) {
if (spdy_session) {
stream_factory_->OnNewSpdySessionReady(
spdy_session, spdy_session_direct_, server_ssl_config_, proxy_info_,
was_npn_negotiated(), protocol_negotiated(), using_spdy(), net_log_);
}
stream_factory_->OnOrphanedJobComplete(this);
} else {
request_->OnNewSpdySessionReady(
this, stream_.Pass(), spdy_session, spdy_session_direct_);
}
}
void HttpStreamFactoryImpl::Job::OnStreamFailedCallback(int result) {
DCHECK(!IsPreconnecting());
if (IsOrphaned())
stream_factory_->OnOrphanedJobComplete(this);
else
request_->OnStreamFailed(this, result, server_ssl_config_);
}
void HttpStreamFactoryImpl::Job::OnCertificateErrorCallback(
int result, const SSLInfo& ssl_info) {
DCHECK(!IsPreconnecting());
if (IsOrphaned())
stream_factory_->OnOrphanedJobComplete(this);
else
request_->OnCertificateError(this, result, server_ssl_config_, ssl_info);
}
void HttpStreamFactoryImpl::Job::OnNeedsProxyAuthCallback(
const HttpResponseInfo& response,
HttpAuthController* auth_controller) {
DCHECK(!IsPreconnecting());
if (IsOrphaned())
stream_factory_->OnOrphanedJobComplete(this);
else
request_->OnNeedsProxyAuth(
this, response, server_ssl_config_, proxy_info_, auth_controller);
}
void HttpStreamFactoryImpl::Job::OnNeedsClientAuthCallback(
SSLCertRequestInfo* cert_info) {
DCHECK(!IsPreconnecting());
if (IsOrphaned())
stream_factory_->OnOrphanedJobComplete(this);
else
request_->OnNeedsClientAuth(this, server_ssl_config_, cert_info);
}
void HttpStreamFactoryImpl::Job::OnHttpsProxyTunnelResponseCallback(
const HttpResponseInfo& response_info,
HttpStream* stream) {
DCHECK(!IsPreconnecting());
if (IsOrphaned())
stream_factory_->OnOrphanedJobComplete(this);
else
request_->OnHttpsProxyTunnelResponse(
this, response_info, server_ssl_config_, proxy_info_, stream);
}
void HttpStreamFactoryImpl::Job::OnPreconnectsComplete() {
DCHECK(!request_);
if (new_spdy_session_.get()) {
stream_factory_->OnNewSpdySessionReady(new_spdy_session_,
spdy_session_direct_,
server_ssl_config_,
proxy_info_,
was_npn_negotiated(),
protocol_negotiated(),
using_spdy(),
net_log_);
}
stream_factory_->OnPreconnectsComplete(this);
}
int HttpStreamFactoryImpl::Job::OnHostResolution(
SpdySessionPool* spdy_session_pool,
const SpdySessionKey& spdy_session_key,
const AddressList& addresses,
const BoundNetLog& net_log) {
return
spdy_session_pool->FindAvailableSession(spdy_session_key, net_log) ?
ERR_SPDY_SESSION_ALREADY_EXISTS : OK;
}
void HttpStreamFactoryImpl::Job::OnIOComplete(int result) {
RunLoop(result);
}
int HttpStreamFactoryImpl::Job::RunLoop(int result) {
result = DoLoop(result);
if (result == ERR_IO_PENDING)
return result;
DCHECK(result == OK || waiting_job_ == NULL);
if (IsPreconnecting()) {
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&HttpStreamFactoryImpl::Job::OnPreconnectsComplete,
ptr_factory_.GetWeakPtr()));
return ERR_IO_PENDING;
}
if (IsCertificateError(result)) {
GetSSLInfo();
next_state_ = STATE_WAITING_USER_ACTION;
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&HttpStreamFactoryImpl::Job::OnCertificateErrorCallback,
ptr_factory_.GetWeakPtr(), result, ssl_info_));
return ERR_IO_PENDING;
}
switch (result) {
case ERR_PROXY_AUTH_REQUESTED: {
DCHECK(connection_.get());
DCHECK(connection_->socket());
DCHECK(establishing_tunnel_);
next_state_ = STATE_WAITING_USER_ACTION;
ProxyClientSocket* proxy_socket =
static_cast<ProxyClientSocket*>(connection_->socket());
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&Job::OnNeedsProxyAuthCallback, ptr_factory_.GetWeakPtr(),
*proxy_socket->GetConnectResponseInfo(),
proxy_socket->GetAuthController()));
return ERR_IO_PENDING;
}
case ERR_SSL_CLIENT_AUTH_CERT_NEEDED:
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&Job::OnNeedsClientAuthCallback, ptr_factory_.GetWeakPtr(),
connection_->ssl_error_response_info().cert_request_info));
return ERR_IO_PENDING;
case ERR_HTTPS_PROXY_TUNNEL_RESPONSE: {
DCHECK(connection_.get());
DCHECK(connection_->socket());
DCHECK(establishing_tunnel_);
ProxyClientSocket* proxy_socket =
static_cast<ProxyClientSocket*>(connection_->socket());
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&Job::OnHttpsProxyTunnelResponseCallback,
ptr_factory_.GetWeakPtr(),
*proxy_socket->GetConnectResponseInfo(),
proxy_socket->CreateConnectResponseStream()));
return ERR_IO_PENDING;
}
case OK:
next_state_ = STATE_DONE;
if (new_spdy_session_.get()) {
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&Job::OnNewSpdySessionReadyCallback,
ptr_factory_.GetWeakPtr()));
} else if (stream_factory_->for_websockets_) {
DCHECK(websocket_stream_);
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&Job::OnWebSocketHandshakeStreamReadyCallback,
ptr_factory_.GetWeakPtr()));
} else {
DCHECK(stream_.get());
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&Job::OnStreamReadyCallback, ptr_factory_.GetWeakPtr()));
}
return ERR_IO_PENDING;
default:
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&Job::OnStreamFailedCallback, ptr_factory_.GetWeakPtr(),
result));
return ERR_IO_PENDING;
}
}
int HttpStreamFactoryImpl::Job::DoLoop(int result) {
DCHECK_NE(next_state_, STATE_NONE);
int rv = result;
do {
State state = next_state_;
next_state_ = STATE_NONE;
switch (state) {
case STATE_START:
DCHECK_EQ(OK, rv);
rv = DoStart();
break;
case STATE_RESOLVE_PROXY:
DCHECK_EQ(OK, rv);
rv = DoResolveProxy();
break;
case STATE_RESOLVE_PROXY_COMPLETE:
rv = DoResolveProxyComplete(rv);
break;
case STATE_WAIT_FOR_JOB:
DCHECK_EQ(OK, rv);
rv = DoWaitForJob();
break;
case STATE_WAIT_FOR_JOB_COMPLETE:
rv = DoWaitForJobComplete(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_CREATE_STREAM:
DCHECK_EQ(OK, rv);
rv = DoCreateStream();
break;
case STATE_CREATE_STREAM_COMPLETE:
rv = DoCreateStreamComplete(rv);
break;
default:
NOTREACHED() << "bad state";
rv = ERR_FAILED;
break;
}
} while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
return rv;
}
int HttpStreamFactoryImpl::Job::StartInternal() {
CHECK_EQ(STATE_NONE, next_state_);
next_state_ = STATE_START;
int rv = RunLoop(OK);
DCHECK_EQ(ERR_IO_PENDING, rv);
return rv;
}
int HttpStreamFactoryImpl::Job::DoStart() {
int port = request_info_.url.EffectiveIntPort();
origin_ = HostPortPair(request_info_.url.HostNoBrackets(), port);
origin_url_ = stream_factory_->ApplyHostMappingRules(
request_info_.url, &origin_);
http_pipelining_key_.reset(new HttpPipelinedHost::Key(origin_));
net_log_.BeginEvent(NetLog::TYPE_HTTP_STREAM_JOB,
base::Bind(&NetLogHttpStreamJobCallback,
&request_info_.url, &origin_url_,
priority_));
bool is_port_allowed = IsPortAllowedByDefault(port);
if (request_info_.url.SchemeIs("ftp")) {
DCHECK(!waiting_job_);
is_port_allowed = IsPortAllowedByFtp(port);
}
if (!is_port_allowed && !IsPortAllowedByOverride(port)) {
if (waiting_job_) {
waiting_job_->Resume(this);
waiting_job_ = NULL;
}
return ERR_UNSAFE_PORT;
}
next_state_ = STATE_RESOLVE_PROXY;
return OK;
}
int HttpStreamFactoryImpl::Job::DoResolveProxy() {
DCHECK(!pac_request_);
next_state_ = STATE_RESOLVE_PROXY_COMPLETE;
if (request_info_.load_flags & LOAD_BYPASS_PROXY) {
proxy_info_.UseDirect();
return OK;
}
return session_->proxy_service()->ResolveProxy(
request_info_.url, &proxy_info_, io_callback_, &pac_request_, net_log_);
}
int HttpStreamFactoryImpl::Job::DoResolveProxyComplete(int result) {
pac_request_ = NULL;
if (result == OK) {
proxy_info_.RemoveProxiesWithoutScheme(
ProxyServer::SCHEME_DIRECT | ProxyServer::SCHEME_QUIC |
ProxyServer::SCHEME_HTTP | ProxyServer::SCHEME_HTTPS |
ProxyServer::SCHEME_SOCKS4 | ProxyServer::SCHEME_SOCKS5);
if (proxy_info_.is_empty()) {
result = ERR_NO_SUPPORTED_PROXIES;
} else if (using_quic_ &&
(!proxy_info_.is_quic() && !proxy_info_.is_direct())) {
result = ERR_NO_SUPPORTED_PROXIES;
}
}
if (result != OK) {
if (waiting_job_) {
waiting_job_->Resume(this);
waiting_job_ = NULL;
}
return result;
}
if (blocking_job_)
next_state_ = STATE_WAIT_FOR_JOB;
else
next_state_ = STATE_INIT_CONNECTION;
return OK;
}
bool HttpStreamFactoryImpl::Job::ShouldForceSpdySSL() const {
bool rv = force_spdy_always_ && force_spdy_over_ssl_;
return rv && !HttpStreamFactory::HasSpdyExclusion(origin_);
}
bool HttpStreamFactoryImpl::Job::ShouldForceSpdyWithoutSSL() const {
bool rv = force_spdy_always_ && !force_spdy_over_ssl_;
return rv && !HttpStreamFactory::HasSpdyExclusion(origin_);
}
bool HttpStreamFactoryImpl::Job::ShouldForceQuic() const {
return session_->params().enable_quic &&
session_->params().origin_to_force_quic_on.Equals(origin_) &&
proxy_info_.is_direct();
}
int HttpStreamFactoryImpl::Job::DoWaitForJob() {
DCHECK(blocking_job_);
next_state_ = STATE_WAIT_FOR_JOB_COMPLETE;
return ERR_IO_PENDING;
}
int HttpStreamFactoryImpl::Job::DoWaitForJobComplete(int result) {
DCHECK(!blocking_job_);
DCHECK_EQ(OK, result);
next_state_ = STATE_INIT_CONNECTION;
return OK;
}
int HttpStreamFactoryImpl::Job::DoInitConnection() {
DCHECK(!blocking_job_);
DCHECK(!connection_->is_initialized());
DCHECK(proxy_info_.proxy_server().is_valid());
next_state_ = STATE_INIT_CONNECTION_COMPLETE;
using_ssl_ = request_info_.url.SchemeIs("https") ||
request_info_.url.SchemeIs("wss") || ShouldForceSpdySSL();
using_spdy_ = false;
if (ShouldForceQuic())
using_quic_ = true;
if (proxy_info_.is_quic())
using_quic_ = true;
if (using_quic_) {
DCHECK(session_->params().enable_quic);
if (proxy_info_.is_quic() && !request_info_.url.SchemeIs("http")) {
NOTREACHED();
return ERR_NOT_IMPLEMENTED;
}
HostPortPair destination = proxy_info_.is_quic() ?
proxy_info_.proxy_server().host_port_pair() : origin_;
next_state_ = STATE_INIT_CONNECTION_COMPLETE;
bool secure_quic = using_ssl_ || proxy_info_.is_quic();
int rv = quic_request_.Request(
destination, secure_quic, request_info_.privacy_mode,
request_info_.method, net_log_, io_callback_);
if (rv != OK) {
if (waiting_job_) {
waiting_job_->Resume(this);
waiting_job_ = NULL;
}
}
return rv;
}
SpdySessionKey spdy_session_key = GetSpdySessionKey();
base::WeakPtr<SpdySession> spdy_session =
session_->spdy_session_pool()->FindAvailableSession(
spdy_session_key, net_log_);
if (spdy_session && CanUseExistingSpdySession()) {
if (IsPreconnecting())
return OK;
using_spdy_ = true;
next_state_ = STATE_CREATE_STREAM;
existing_spdy_session_ = spdy_session;
return OK;
} else if (request_ && (using_ssl_ || ShouldForceSpdyWithoutSSL())) {
request_->SetSpdySessionKey(spdy_session_key);
} else if (IsRequestEligibleForPipelining()) {
existing_available_pipeline_ = stream_factory_->http_pipelined_host_pool_.
IsExistingPipelineAvailableForKey(*http_pipelining_key_.get());
if (existing_available_pipeline_) {
return OK;
} else {
bool was_new_key = request_->SetHttpPipeliningKey(
*http_pipelining_key_.get());
if (!was_new_key && session_->force_http_pipelining()) {
return ERR_IO_PENDING;
}
}
}
if (waiting_job_) {
waiting_job_->Resume(this);
waiting_job_ = NULL;
}
if (proxy_info_.is_http() || proxy_info_.is_https())
establishing_tunnel_ = using_ssl_;
bool want_spdy_over_npn = original_url_ != NULL;
if (proxy_info_.is_https()) {
InitSSLConfig(proxy_info_.proxy_server().host_port_pair(),
&proxy_ssl_config_,
true );
proxy_ssl_config_.rev_checking_enabled = false;
}
if (using_ssl_) {
InitSSLConfig(origin_, &server_ssl_config_,
false );
}
if (IsPreconnecting()) {
DCHECK(!stream_factory_->for_websockets_);
return PreconnectSocketsForHttpRequest(
origin_url_,
request_info_.extra_headers,
request_info_.load_flags,
priority_,
session_,
proxy_info_,
ShouldForceSpdySSL(),
want_spdy_over_npn,
server_ssl_config_,
proxy_ssl_config_,
request_info_.privacy_mode,
net_log_,
num_streams_);
} else {
OnHostResolutionCallback resolution_callback = CanUseExistingSpdySession() ?
base::Bind(&Job::OnHostResolution, session_->spdy_session_pool(),
GetSpdySessionKey()) :
OnHostResolutionCallback();
if (stream_factory_->for_websockets_) {
SSLConfig websocket_server_ssl_config = server_ssl_config_;
websocket_server_ssl_config.next_protos.clear();
return InitSocketHandleForWebSocketRequest(
origin_url_, request_info_.extra_headers, request_info_.load_flags,
priority_, session_, proxy_info_, ShouldForceSpdySSL(),
want_spdy_over_npn, websocket_server_ssl_config, proxy_ssl_config_,
request_info_.privacy_mode, net_log_,
connection_.get(), resolution_callback, io_callback_);
}
return InitSocketHandleForHttpRequest(
origin_url_, request_info_.extra_headers, request_info_.load_flags,
priority_, session_, proxy_info_, ShouldForceSpdySSL(),
want_spdy_over_npn, server_ssl_config_, proxy_ssl_config_,
request_info_.privacy_mode, net_log_,
connection_.get(), resolution_callback, io_callback_);
}
}
int HttpStreamFactoryImpl::Job::DoInitConnectionComplete(int result) {
if (IsPreconnecting()) {
if (using_quic_)
return result;
DCHECK_EQ(OK, result);
return OK;
}
if (result == ERR_SPDY_SESSION_ALREADY_EXISTS) {
SpdySessionKey spdy_session_key = GetSpdySessionKey();
existing_spdy_session_ =
session_->spdy_session_pool()->FindAvailableSession(
spdy_session_key, net_log_);
if (existing_spdy_session_) {
using_spdy_ = true;
next_state_ = STATE_CREATE_STREAM;
} else {
ReturnToStateInitConnection(true );
}
return OK;
}
if (result < 0 && waiting_job_) {
waiting_job_->Resume(this);
waiting_job_ = NULL;
}
if (result < 0 && session_->force_http_pipelining()) {
stream_factory_->AbortPipelinedRequestsWithKey(
this, *http_pipelining_key_.get(), result, server_ssl_config_);
}
bool ssl_started = using_ssl_ && (result == OK || connection_->socket() ||
connection_->is_ssl_error());
if (ssl_started && (result == OK || IsCertificateError(result))) {
if (using_quic_ && result == OK) {
was_npn_negotiated_ = true;
NextProto protocol_negotiated =
SSLClientSocket::NextProtoFromString("quic/1+spdy/3");
protocol_negotiated_ = protocol_negotiated;
} else {
SSLClientSocket* ssl_socket =
static_cast<SSLClientSocket*>(connection_->socket());
if (ssl_socket->WasNpnNegotiated()) {
was_npn_negotiated_ = true;
std::string proto;
std::string server_protos;
SSLClientSocket::NextProtoStatus status =
ssl_socket->GetNextProto(&proto, &server_protos);
NextProto protocol_negotiated =
SSLClientSocket::NextProtoFromString(proto);
protocol_negotiated_ = protocol_negotiated;
net_log_.AddEvent(
NetLog::TYPE_HTTP_STREAM_REQUEST_PROTO,
base::Bind(&NetLogHttpStreamProtoCallback,
status, &proto, &server_protos));
if (ssl_socket->was_spdy_negotiated())
SwitchToSpdyMode();
}
if (ShouldForceSpdySSL())
SwitchToSpdyMode();
}
} else if (proxy_info_.is_https() && connection_->socket() &&
result == OK) {
ProxyClientSocket* proxy_socket =
static_cast<ProxyClientSocket*>(connection_->socket());
if (proxy_socket->IsUsingSpdy()) {
was_npn_negotiated_ = true;
protocol_negotiated_ = proxy_socket->GetProtocolNegotiated();
SwitchToSpdyMode();
}
}
if (ShouldForceSpdyWithoutSSL())
SwitchToSpdyMode();
if (result == ERR_PROXY_AUTH_REQUESTED ||
result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE) {
DCHECK(!ssl_started);
connection_.reset(connection_->release_pending_http_proxy_connection());
return result;
}
if (!ssl_started && result < 0 && original_url_.get()) {
session_->http_server_properties()->SetBrokenAlternateProtocol(
HostPortPair::FromURL(*original_url_));
return result;
}
if (using_quic_) {
if (result < 0)
return result;
stream_ = quic_request_.ReleaseStream();
next_state_ = STATE_NONE;
return OK;
}
if (result < 0 && !ssl_started)
return ReconsiderProxyAfterError(result);
establishing_tunnel_ = false;
if (connection_->socket()) {
LogHttpConnectedMetrics(*connection_);
if (!connection_->is_reused()) {
ConnectionType type = using_spdy_ ? CONNECTION_SPDY : CONNECTION_HTTP;
UpdateConnectionTypeHistograms(type);
}
}
if (using_ssl_) {
DCHECK(ssl_started);
if (IsCertificateError(result)) {
if (using_spdy_ && original_url_.get() &&
original_url_->SchemeIs("http")) {
spdy_certificate_error_ = result;
result = OK;
} else {
result = HandleCertificateError(result);
if (result == OK && !connection_->socket()->IsConnectedAndIdle()) {
ReturnToStateInitConnection(true );
return result;
}
}
}
if (result < 0)
return result;
}
next_state_ = STATE_CREATE_STREAM;
return OK;
}
int HttpStreamFactoryImpl::Job::DoWaitingUserAction(int result) {
return ERR_IO_PENDING;
}
int HttpStreamFactoryImpl::Job::DoCreateStream() {
DCHECK(connection_->socket() || existing_spdy_session_.get() ||
existing_available_pipeline_ || using_quic_);
next_state_ = STATE_CREATE_STREAM_COMPLETE;
if (connection_->socket() && !connection_->is_reused())
SetSocketMotivation();
if (!using_spdy_) {
bool using_proxy = (proxy_info_.is_http() || proxy_info_.is_https()) &&
(request_info_.url.SchemeIs("http") ||
request_info_.url.SchemeIs("ftp"));
if (stream_factory_->http_pipelined_host_pool_.
IsExistingPipelineAvailableForKey(*http_pipelining_key_.get())) {
DCHECK(!stream_factory_->for_websockets_);
stream_.reset(stream_factory_->http_pipelined_host_pool_.
CreateStreamOnExistingPipeline(
*http_pipelining_key_.get()));
CHECK(stream_.get());
} else if (stream_factory_->for_websockets_) {
DCHECK(request_);
DCHECK(request_->websocket_handshake_stream_create_helper());
websocket_stream_.reset(
request_->websocket_handshake_stream_create_helper()
->CreateBasicStream(connection_.Pass(), using_proxy));
} else if (!using_proxy && IsRequestEligibleForPipelining()) {
stream_.reset(
stream_factory_->http_pipelined_host_pool_.CreateStreamOnNewPipeline(
*http_pipelining_key_.get(),
connection_.release(),
server_ssl_config_,
proxy_info_,
net_log_,
was_npn_negotiated_,
protocol_negotiated_));
CHECK(stream_.get());
} else {
stream_.reset(new HttpBasicStream(connection_.release(), using_proxy));
}
return OK;
}
CHECK(!stream_.get());
bool direct = true;
const ProxyServer& proxy_server = proxy_info_.proxy_server();
PrivacyMode privacy_mode = request_info_.privacy_mode;
SpdySessionKey spdy_session_key(origin_, proxy_server, privacy_mode);
if (IsHttpsProxyAndHttpUrl()) {
spdy_session_key = SpdySessionKey(proxy_server.host_port_pair(),
ProxyServer::Direct(),
PRIVACY_MODE_DISABLED);
direct = false;
}
base::WeakPtr<SpdySession> spdy_session;
if (existing_spdy_session_.get()) {
if (connection_->socket())
connection_->socket()->Disconnect();
connection_->Reset();
std::swap(spdy_session, existing_spdy_session_);
} else {
SpdySessionPool* spdy_pool = session_->spdy_session_pool();
spdy_session = spdy_pool->FindAvailableSession(spdy_session_key, net_log_);
if (!spdy_session) {
new_spdy_session_ =
spdy_pool->CreateAvailableSessionFromSocket(spdy_session_key,
connection_.Pass(),
net_log_,
spdy_certificate_error_,
using_ssl_);
const HostPortPair& host_port_pair = spdy_session_key.host_port_pair();
base::WeakPtr<HttpServerProperties> http_server_properties =
session_->http_server_properties();
if (http_server_properties)
http_server_properties->SetSupportsSpdy(host_port_pair, true);
spdy_session_direct_ = direct;
bool use_relative_url = direct || request_info_.url.SchemeIs("https");
stream_.reset(new SpdyHttpStream(new_spdy_session_, use_relative_url));
return OK;
}
}
if (!spdy_session)
return ERR_CONNECTION_CLOSED;
if (stream_factory_->for_websockets_) {
NOTREACHED();
} else {
bool use_relative_url = direct || request_info_.url.SchemeIs("https");
stream_.reset(new SpdyHttpStream(spdy_session, use_relative_url));
}
return OK;
}
int HttpStreamFactoryImpl::Job::DoCreateStreamComplete(int result) {
if (result < 0)
return result;
session_->proxy_service()->ReportSuccess(proxy_info_);
next_state_ = STATE_NONE;
return OK;
}
int HttpStreamFactoryImpl::Job::DoRestartTunnelAuth() {
next_state_ = STATE_RESTART_TUNNEL_AUTH_COMPLETE;
ProxyClientSocket* proxy_socket =
static_cast<ProxyClientSocket*>(connection_->socket());
return proxy_socket->RestartWithAuth(io_callback_);
}
int HttpStreamFactoryImpl::Job::DoRestartTunnelAuthComplete(int result) {
if (result == ERR_PROXY_AUTH_REQUESTED)
return result;
if (result == OK) {
establishing_tunnel_ = false;
ReturnToStateInitConnection(false );
return OK;
}
return ReconsiderProxyAfterError(result);
}
void HttpStreamFactoryImpl::Job::ReturnToStateInitConnection(
bool close_connection) {
if (close_connection && connection_->socket())
connection_->socket()->Disconnect();
connection_->Reset();
if (request_) {
request_->RemoveRequestFromSpdySessionRequestMap();
request_->RemoveRequestFromHttpPipeliningRequestMap();
}
next_state_ = STATE_INIT_CONNECTION;
}
void HttpStreamFactoryImpl::Job::SetSocketMotivation() {
if (request_info_.motivation == HttpRequestInfo::PRECONNECT_MOTIVATED)
connection_->socket()->SetSubresourceSpeculation();
else if (request_info_.motivation == HttpRequestInfo::OMNIBOX_MOTIVATED)
connection_->socket()->SetOmniboxSpeculation();
}
bool HttpStreamFactoryImpl::Job::IsHttpsProxyAndHttpUrl() const {
if (!proxy_info_.is_https())
return false;
if (original_url_.get()) {
DCHECK(original_url_->SchemeIs("http"));
return original_url_->SchemeIs("http");
}
return request_info_.url.SchemeIs("http");
}
void HttpStreamFactoryImpl::Job::InitSSLConfig(
const HostPortPair& origin_server,
SSLConfig* ssl_config,
bool is_proxy) const {
if (proxy_info_.is_https() && ssl_config->send_client_cert) {
ssl_config->false_start_enabled = false;
}
enum {
FALLBACK_NONE = 0,
FALLBACK_SSL3 = 1,
FALLBACK_TLS1 = 2,
FALLBACK_TLS1_1 = 3,
FALLBACK_MAX
};
int fallback = FALLBACK_NONE;
if (ssl_config->version_fallback) {
switch (ssl_config->version_max) {
case SSL_PROTOCOL_VERSION_SSL3:
fallback = FALLBACK_SSL3;
break;
case SSL_PROTOCOL_VERSION_TLS1:
fallback = FALLBACK_TLS1;
break;
case SSL_PROTOCOL_VERSION_TLS1_1:
fallback = FALLBACK_TLS1_1;
break;
}
}
UMA_HISTOGRAM_ENUMERATION("Net.ConnectionUsedSSLVersionFallback",
fallback, FALLBACK_MAX);
const std::string& host = origin_server.host();
if (!is_proxy &&
host.size() >= 10 &&
host.compare(host.size() - 10, 10, "google.com") == 0 &&
(host.size() == 10 || host[host.size()-11] == '.')) {
UMA_HISTOGRAM_ENUMERATION("Net.GoogleConnectionUsedSSLVersionFallback",
fallback, FALLBACK_MAX);
}
if (request_info_.load_flags & LOAD_VERIFY_EV_CERT)
ssl_config->verify_ev_cert = true;
if (request_info_.privacy_mode == PRIVACY_MODE_ENABLED)
ssl_config->channel_id_enabled = false;
}
int HttpStreamFactoryImpl::Job::ReconsiderProxyAfterError(int error) {
DCHECK(!pac_request_);
switch (error) {
case ERR_PROXY_CONNECTION_FAILED:
case ERR_NAME_NOT_RESOLVED:
case ERR_INTERNET_DISCONNECTED:
case ERR_ADDRESS_UNREACHABLE:
case ERR_CONNECTION_CLOSED:
case ERR_CONNECTION_TIMED_OUT:
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:
case ERR_PROXY_CERTIFICATE_INVALID:
case ERR_SSL_PROTOCOL_ERROR:
break;
case ERR_SOCKS_CONNECTION_HOST_UNREACHABLE:
return ERR_ADDRESS_UNREACHABLE;
default:
return error;
}
if (request_info_.load_flags & LOAD_BYPASS_PROXY) {
return error;
}
if (proxy_info_.is_https() && proxy_ssl_config_.send_client_cert) {
session_->ssl_client_auth_cache()->Remove(
proxy_info_.proxy_server().host_port_pair());
}
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 (connection_->socket())
connection_->socket()->Disconnect();
connection_->Reset();
if (request_) {
request_->RemoveRequestFromSpdySessionRequestMap();
request_->RemoveRequestFromHttpPipeliningRequestMap();
}
next_state_ = STATE_RESOLVE_PROXY_COMPLETE;
} else {
rv = error;
}
return rv;
}
int HttpStreamFactoryImpl::Job::HandleCertificateError(int error) {
DCHECK(using_ssl_);
DCHECK(IsCertificateError(error));
SSLClientSocket* ssl_socket =
static_cast<SSLClientSocket*>(connection_->socket());
ssl_socket->GetSSLInfo(&ssl_info_);
SSLConfig::CertAndStatus bad_cert;
if (ssl_info_.cert.get() == NULL ||
!X509Certificate::GetDEREncoded(ssl_info_.cert->os_cert_handle(),
&bad_cert.der_cert)) {
return error;
}
bad_cert.cert_status = ssl_info_.cert_status;
server_ssl_config_.allowed_bad_certs.push_back(bad_cert);
int load_flags = request_info_.load_flags;
if (session_->params().ignore_certificate_errors)
load_flags |= LOAD_IGNORE_ALL_CERT_ERRORS;
if (ssl_socket->IgnoreCertError(error, load_flags))
return OK;
return error;
}
void HttpStreamFactoryImpl::Job::SwitchToSpdyMode() {
if (HttpStreamFactory::spdy_enabled())
using_spdy_ = true;
}
void HttpStreamFactoryImpl::Job::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;
}
}
bool HttpStreamFactoryImpl::Job::IsPreconnecting() const {
DCHECK_GE(num_streams_, 0);
return num_streams_ > 0;
}
bool HttpStreamFactoryImpl::Job::IsOrphaned() const {
return !IsPreconnecting() && !request_;
}
bool HttpStreamFactoryImpl::Job::IsRequestEligibleForPipelining() {
if (IsPreconnecting() || !request_) {
return false;
}
if (stream_factory_->for_websockets_) {
return false;
}
if (session_->force_http_pipelining()) {
return true;
}
if (!session_->params().http_pipelining_enabled) {
return false;
}
if (using_ssl_) {
return false;
}
if (request_info_.method != "GET" && request_info_.method != "HEAD") {
return false;
}
if (request_info_.load_flags &
(net::LOAD_MAIN_FRAME | net::LOAD_SUB_FRAME | net::LOAD_PREFETCH |
net::LOAD_IS_DOWNLOAD)) {
return false;
}
return stream_factory_->http_pipelined_host_pool_.IsKeyEligibleForPipelining(
*http_pipelining_key_.get());
}
}