blob: 754cc65e1fc027a3adc64544d7a0c0b52ecb0b25 [file] [log] [blame]
// Copyright (c) 2012 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 "services/network/proxy_resolving_client_socket.h"
#include <stdint.h>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "net/base/io_buffer.h"
#include "net/base/ip_address.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/http/http_auth_controller.h"
#include "net/http/http_network_session.h"
#include "net/http/proxy_client_socket.h"
#include "net/http/proxy_fallback.h"
#include "net/log/net_log_source_type.h"
#include "net/socket/client_socket_pool_manager.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
namespace network {
ProxyResolvingClientSocket::ProxyResolvingClientSocket(
net::HttpNetworkSession* network_session,
const net::CommonConnectJobParams* common_connect_job_params,
const net::SSLConfig& ssl_config,
const GURL& url,
bool use_tls)
: network_session_(network_session),
common_connect_job_params_(common_connect_job_params),
ssl_config_(ssl_config),
url_(url),
use_tls_(use_tls),
net_log_(net::NetLogWithSource::Make(network_session_->net_log(),
net::NetLogSourceType::SOCKET)),
next_state_(STATE_NONE),
weak_factory_(this) {
// TODO(xunjieli): Handle invalid URLs more gracefully (at mojo API layer
// or when the request is created).
DCHECK(url_.is_valid());
}
ProxyResolvingClientSocket::~ProxyResolvingClientSocket() {}
int ProxyResolvingClientSocket::Read(net::IOBuffer* buf,
int buf_len,
net::CompletionOnceCallback callback) {
if (socket_)
return socket_->Read(buf, buf_len, std::move(callback));
return net::ERR_SOCKET_NOT_CONNECTED;
}
int ProxyResolvingClientSocket::ReadIfReady(
net::IOBuffer* buf,
int buf_len,
net::CompletionOnceCallback callback) {
if (socket_)
return socket_->ReadIfReady(buf, buf_len, std::move(callback));
return net::ERR_SOCKET_NOT_CONNECTED;
}
int ProxyResolvingClientSocket::CancelReadIfReady() {
if (socket_)
return socket_->CancelReadIfReady();
// Return net::OK as ReadIfReady() is canceled when socket is disconnected.
return net::OK;
}
int ProxyResolvingClientSocket::Write(
net::IOBuffer* buf,
int buf_len,
net::CompletionOnceCallback callback,
const net::NetworkTrafficAnnotationTag& traffic_annotation) {
if (socket_) {
return socket_->Write(buf, buf_len, std::move(callback),
traffic_annotation);
}
return net::ERR_SOCKET_NOT_CONNECTED;
}
int ProxyResolvingClientSocket::SetReceiveBufferSize(int32_t size) {
if (socket_)
return socket_->SetReceiveBufferSize(size);
return net::ERR_SOCKET_NOT_CONNECTED;
}
int ProxyResolvingClientSocket::SetSendBufferSize(int32_t size) {
if (socket_)
return socket_->SetSendBufferSize(size);
return net::ERR_SOCKET_NOT_CONNECTED;
}
int ProxyResolvingClientSocket::Connect(net::CompletionOnceCallback callback) {
DCHECK(user_connect_callback_.is_null());
DCHECK(!socket_);
next_state_ = STATE_PROXY_RESOLVE;
int result = DoLoop(net::OK);
if (result == net::ERR_IO_PENDING) {
user_connect_callback_ = std::move(callback);
}
return result;
}
void ProxyResolvingClientSocket::Disconnect() {
connect_job_.reset();
socket_.reset();
if (proxy_resolve_request_)
proxy_resolve_request_.reset();
user_connect_callback_.Reset();
}
bool ProxyResolvingClientSocket::IsConnected() const {
if (!socket_)
return false;
return socket_->IsConnected();
}
bool ProxyResolvingClientSocket::IsConnectedAndIdle() const {
if (!socket_)
return false;
return socket_->IsConnectedAndIdle();
}
int ProxyResolvingClientSocket::GetPeerAddress(net::IPEndPoint* address) const {
if (!socket_)
return net::ERR_SOCKET_NOT_CONNECTED;
if (proxy_info_.is_direct())
return socket_->GetPeerAddress(address);
net::IPAddress ip_address;
if (!ip_address.AssignFromIPLiteral(url_.HostNoBrackets())) {
// Do not expose the proxy IP address to the caller.
return net::ERR_NAME_NOT_RESOLVED;
}
*address = net::IPEndPoint(ip_address, url_.EffectiveIntPort());
return net::OK;
}
int ProxyResolvingClientSocket::GetLocalAddress(
net::IPEndPoint* address) const {
if (socket_)
return socket_->GetLocalAddress(address);
return net::ERR_SOCKET_NOT_CONNECTED;
}
const net::NetLogWithSource& ProxyResolvingClientSocket::NetLog() const {
if (socket_)
return socket_->NetLog();
return net_log_;
}
bool ProxyResolvingClientSocket::WasEverUsed() const {
if (socket_)
return socket_->WasEverUsed();
return false;
}
bool ProxyResolvingClientSocket::WasAlpnNegotiated() const {
if (socket_)
return socket_->WasAlpnNegotiated();
return false;
}
net::NextProto ProxyResolvingClientSocket::GetNegotiatedProtocol() const {
if (socket_)
return socket_->GetNegotiatedProtocol();
return net::kProtoUnknown;
}
bool ProxyResolvingClientSocket::GetSSLInfo(net::SSLInfo* ssl_info) {
if (socket_)
return socket_->GetSSLInfo(ssl_info);
return false;
}
void ProxyResolvingClientSocket::GetConnectionAttempts(
net::ConnectionAttempts* out) const {
out->clear();
}
int64_t ProxyResolvingClientSocket::GetTotalReceivedBytes() const {
NOTIMPLEMENTED();
return 0;
}
void ProxyResolvingClientSocket::ApplySocketTag(const net::SocketTag& tag) {
NOTIMPLEMENTED();
}
void ProxyResolvingClientSocket::OnIOComplete(int result) {
DCHECK_NE(net::ERR_IO_PENDING, result);
int net_error = DoLoop(result);
if (net_error != net::ERR_IO_PENDING)
std::move(user_connect_callback_).Run(net_error);
}
int ProxyResolvingClientSocket::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_PROXY_RESOLVE:
DCHECK_EQ(net::OK, rv);
rv = DoProxyResolve();
break;
case STATE_PROXY_RESOLVE_COMPLETE:
rv = DoProxyResolveComplete(rv);
break;
case STATE_INIT_CONNECTION:
DCHECK_EQ(net::OK, rv);
rv = DoInitConnection();
break;
case STATE_INIT_CONNECTION_COMPLETE:
rv = DoInitConnectionComplete(rv);
break;
default:
NOTREACHED() << "bad state";
rv = net::ERR_FAILED;
break;
}
} while (rv != net::ERR_IO_PENDING && next_state_ != STATE_NONE);
return rv;
}
int ProxyResolvingClientSocket::DoProxyResolve() {
next_state_ = STATE_PROXY_RESOLVE_COMPLETE;
// base::Unretained(this) is safe because resolution request is canceled when
// |proxy_resolve_request_| is destroyed.
return network_session_->proxy_resolution_service()->ResolveProxy(
url_, "POST", &proxy_info_,
base::BindRepeating(&ProxyResolvingClientSocket::OnIOComplete,
base::Unretained(this)),
&proxy_resolve_request_, net_log_);
}
int ProxyResolvingClientSocket::DoProxyResolveComplete(int result) {
proxy_resolve_request_ = nullptr;
if (result == net::OK) {
// Removes unsupported proxies from the list. Currently, this removes
// just the SCHEME_QUIC proxy.
// TODO(crbug.com/876885): Allow QUIC proxy once net::QuicProxyClientSocket
// supports ReadIfReady() and CancelReadIfReady().
proxy_info_.RemoveProxiesWithoutScheme(
net::ProxyServer::SCHEME_DIRECT | net::ProxyServer::SCHEME_HTTP |
net::ProxyServer::SCHEME_HTTPS | net::ProxyServer::SCHEME_SOCKS4 |
net::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 net::ERR_NO_SUPPORTED_PROXIES;
}
next_state_ = STATE_INIT_CONNECTION;
return net::OK;
}
return result;
}
int ProxyResolvingClientSocket::DoInitConnection() {
DCHECK(!socket_);
next_state_ = STATE_INIT_CONNECTION_COMPLETE;
// Now that the proxy is resolved, create and start a ConnectJob.
connect_job_ = net::CreateConnectJobForRawConnect(
net::HostPortPair::FromURL(url_), use_tls_, common_connect_job_params_,
net::MAXIMUM_PRIORITY, proxy_info_, ssl_config_, ssl_config_, net_log_,
this);
return connect_job_->Connect();
}
int ProxyResolvingClientSocket::DoInitConnectionComplete(int result) {
if (result != net::OK) {
connect_job_.reset();
// ReconsiderProxyAfterError either returns an error (in which case it is
// not reconsidering a proxy) or returns ERR_IO_PENDING if it is considering
// another proxy.
return ReconsiderProxyAfterError(result);
}
socket_ = connect_job_->PassSocket();
connect_job_.reset();
network_session_->proxy_resolution_service()->ReportSuccess(proxy_info_);
return net::OK;
}
void ProxyResolvingClientSocket::OnConnectJobComplete(int result,
net::ConnectJob* job) {
DCHECK_EQ(next_state_, STATE_INIT_CONNECTION_COMPLETE);
DCHECK_EQ(connect_job_.get(), job);
OnIOComplete(result);
}
void ProxyResolvingClientSocket::OnNeedsProxyAuth(
const net::HttpResponseInfo& response,
net::HttpAuthController* auth_controller,
base::OnceClosure restart_with_auth_callback,
net::ConnectJob* job) {
DCHECK_EQ(next_state_, STATE_INIT_CONNECTION_COMPLETE);
DCHECK_EQ(connect_job_.get(), job);
// If there are credentials available to try and use, use them.
if (auth_controller->HaveAuth()) {
std::move(restart_with_auth_callback).Run();
return;
}
// Otherwise, cancel the ConnectJob and continue.
connect_job_.reset();
OnIOComplete(net::ERR_PROXY_AUTH_REQUESTED);
}
int ProxyResolvingClientSocket::ReconsiderProxyAfterError(int error) {
DCHECK(!socket_);
DCHECK(!proxy_resolve_request_);
DCHECK_NE(error, net::OK);
DCHECK_NE(error, net::ERR_IO_PENDING);
// Check if the error was a proxy failure.
if (!net::CanFalloverToNextProxy(proxy_info_.proxy_server(), error, &error))
return error;
if (proxy_info_.is_https() && ssl_config_.send_client_cert) {
network_session_->ssl_client_auth_cache()->Remove(
proxy_info_.proxy_server().host_port_pair());
}
// There was nothing left to fall-back to, so fail the transaction
// with the last connection error we got.
if (!proxy_info_.Fallback(error, net_log_))
return error;
next_state_ = STATE_INIT_CONNECTION;
return net::OK;
}
} // namespace network