#include "net/spdy/spdy_proxy_client_socket.h"
#include <algorithm>
#include <utility>
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/notimplemented.h"
#include "base/notreached.h"
#include "base/strings/string_util.h"
#include "base/task/single_thread_task_runner.h"
#include "base/types/expected.h"
#include "base/types/expected_macros.h"
#include "base/values.h"
#include "net/base/auth.h"
#include "net/base/completion_once_callback.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/base/proxy_chain.h"
#include "net/base/proxy_delegate.h"
#include "net/http/http_auth_cache.h"
#include "net/http/http_auth_handler_factory.h"
#include "net/http/http_log_util.h"
#include "net/http/http_request_info.h"
#include "net/http/http_response_headers.h"
#include "net/log/net_log_event_type.h"
#include "net/log/net_log_source_type.h"
#include "net/spdy/spdy_http_utils.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "url/gurl.h"
namespace net {
SpdyProxyClientSocket::SpdyProxyClientSocket(
const base::WeakPtr<SpdyStream>& spdy_stream,
const ProxyChain& proxy_chain,
size_t proxy_chain_index,
const std::string& user_agent,
const HostPortPair& endpoint,
const NetLogWithSource& source_net_log,
scoped_refptr<HttpAuthController> auth_controller,
ProxyDelegate* proxy_delegate)
: spdy_stream_(spdy_stream),
endpoint_(endpoint),
auth_(std::move(auth_controller)),
proxy_chain_(proxy_chain),
proxy_chain_index_(proxy_chain_index),
proxy_delegate_(proxy_delegate),
user_agent_(user_agent),
net_log_(NetLogWithSource::Make(spdy_stream->net_log().net_log(),
NetLogSourceType::PROXY_CLIENT_SOCKET)),
source_dependency_(source_net_log.source()) {
request_.method = "CONNECT";
request_.url = GURL("https://" + endpoint.ToString());
net_log_.BeginEventReferencingSource(NetLogEventType::SOCKET_ALIVE,
source_net_log.source());
net_log_.AddEventReferencingSource(
NetLogEventType::HTTP2_PROXY_CLIENT_SESSION,
spdy_stream->net_log().source());
spdy_stream_->SetDelegate(this);
was_ever_used_ = spdy_stream_->WasEverUsed();
}
SpdyProxyClientSocket::~SpdyProxyClientSocket() {
Disconnect();
net_log_.EndEvent(NetLogEventType::SOCKET_ALIVE);
}
const HttpResponseInfo* SpdyProxyClientSocket::GetConnectResponseInfo() const {
return response_.headers.get() ? &response_ : nullptr;
}
const scoped_refptr<HttpAuthController>&
SpdyProxyClientSocket::GetAuthController() const {
return auth_;
}
int SpdyProxyClientSocket::RestartWithAuth(CompletionOnceCallback callback) {
next_state_ = STATE_DISCONNECTED;
return ERR_UNABLE_TO_REUSE_CONNECTION_FOR_PROXY_AUTH;
}
void SpdyProxyClientSocket::SetStreamPriority(RequestPriority priority) {}
int SpdyProxyClientSocket::Connect(CompletionOnceCallback callback) {
DCHECK(read_callback_.is_null());
if (next_state_ == STATE_OPEN)
return OK;
DCHECK_EQ(STATE_DISCONNECTED, next_state_);
next_state_ = STATE_GENERATE_AUTH_TOKEN;
int rv = DoLoop(OK);
if (rv == ERR_IO_PENDING)
read_callback_ = std::move(callback);
return rv;
}
void SpdyProxyClientSocket::Disconnect() {
read_buffer_queue_.Clear();
user_buffer_ = nullptr;
user_buffer_len_ = 0;
read_callback_.Reset();
write_buffer_len_ = 0;
write_callback_.Reset();
next_state_ = STATE_DISCONNECTED;
if (spdy_stream_.get()) {
spdy_stream_->Cancel(ERR_ABORTED);
DCHECK(!spdy_stream_.get());
}
}
bool SpdyProxyClientSocket::IsConnected() const {
return next_state_ == STATE_OPEN;
}
bool SpdyProxyClientSocket::IsConnectedAndIdle() const {
return IsConnected() && read_buffer_queue_.IsEmpty() &&
spdy_stream_->IsOpen();
}
const NetLogWithSource& SpdyProxyClientSocket::NetLog() const {
return net_log_;
}
bool SpdyProxyClientSocket::WasEverUsed() const {
return was_ever_used_ || (spdy_stream_.get() && spdy_stream_->WasEverUsed());
}
NextProto SpdyProxyClientSocket::GetNegotiatedProtocol() const {
return NextProto::kProtoUnknown;
}
bool SpdyProxyClientSocket::GetSSLInfo(SSLInfo* ssl_info) {
return false;
}
int64_t SpdyProxyClientSocket::GetTotalReceivedBytes() const {
NOTIMPLEMENTED();
return 0;
}
void SpdyProxyClientSocket::ApplySocketTag(const SocketTag& tag) {
CHECK(tag == SocketTag());
}
int SpdyProxyClientSocket::Read(IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback) {
int rv = ReadIfReady(buf, buf_len, std::move(callback));
if (rv == ERR_IO_PENDING) {
user_buffer_ = buf;
user_buffer_len_ = static_cast<size_t>(buf_len);
}
return rv;
}
int SpdyProxyClientSocket::ReadIfReady(IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback) {
DCHECK(!read_callback_);
DCHECK(!user_buffer_);
if (next_state_ == STATE_DISCONNECTED)
return ERR_SOCKET_NOT_CONNECTED;
if (next_state_ == STATE_CLOSED && read_buffer_queue_.IsEmpty()) {
return 0;
}
DCHECK(next_state_ == STATE_OPEN || next_state_ == STATE_CLOSED);
DCHECK(buf);
size_t result = PopulateUserReadBuffer(buf->first(buf_len));
if (result == 0) {
read_callback_ = std::move(callback);
return ERR_IO_PENDING;
}
return result;
}
int SpdyProxyClientSocket::CancelReadIfReady() {
DCHECK(!user_buffer_) << "Pending Read() cannot be canceled";
read_callback_.Reset();
return OK;
}
size_t SpdyProxyClientSocket::PopulateUserReadBuffer(base::span<uint8_t> data) {
return read_buffer_queue_.Dequeue(data);
}
int SpdyProxyClientSocket::Write(
IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback,
const NetworkTrafficAnnotationTag& traffic_annotation) {
DCHECK(write_callback_.is_null());
if (next_state_ != STATE_OPEN)
return ERR_SOCKET_NOT_CONNECTED;
if (end_stream_state_ == EndStreamState::kEndStreamSent)
return ERR_CONNECTION_CLOSED;
DCHECK(spdy_stream_.get());
spdy_stream_->SendData(buf, buf_len, MORE_DATA_TO_SEND);
net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_SENT, buf_len,
buf->data());
write_callback_ = std::move(callback);
write_buffer_len_ = buf_len;
return ERR_IO_PENDING;
}
int SpdyProxyClientSocket::SetReceiveBufferSize(int32_t size) {
return ERR_NOT_IMPLEMENTED;
}
int SpdyProxyClientSocket::SetSendBufferSize(int32_t size) {
return ERR_NOT_IMPLEMENTED;
}
int SpdyProxyClientSocket::GetPeerAddress(IPEndPoint* address) const {
if (!IsConnected())
return ERR_SOCKET_NOT_CONNECTED;
return spdy_stream_->GetPeerAddress(address);
}
int SpdyProxyClientSocket::GetLocalAddress(IPEndPoint* address) const {
if (!IsConnected())
return ERR_SOCKET_NOT_CONNECTED;
return spdy_stream_->GetLocalAddress(address);
}
void SpdyProxyClientSocket::RunWriteCallback(int result) {
base::WeakPtr<SpdyProxyClientSocket> weak_ptr = weak_factory_.GetWeakPtr();
if (write_callback_) {
std::move(write_callback_).Run(result);
}
if (!weak_ptr) {
return;
}
if (end_stream_state_ == EndStreamState::kEndStreamReceived) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&SpdyProxyClientSocket::MaybeSendEndStream,
weak_factory_.GetMutableWeakPtr()));
}
}
void SpdyProxyClientSocket::OnIOComplete(int result) {
DCHECK_NE(STATE_DISCONNECTED, next_state_);
int rv = DoLoop(result);
if (rv != ERR_IO_PENDING) {
std::move(read_callback_).Run(rv);
}
}
void SpdyProxyClientSocket::OnBeforeTunnelRequestComplete(
base::expected<HttpRequestHeaders, Error> result) {
if (result.has_value()) {
proxy_delegate_headers_ = std::move(result.value());
OnIOComplete(OK);
} else {
CHECK_NE(ERR_IO_PENDING, result.error());
CHECK_NE(OK, result.error());
OnIOComplete(result.error());
}
}
int SpdyProxyClientSocket::DoLoop(int last_io_result) {
DCHECK_NE(next_state_, STATE_DISCONNECTED);
int rv = last_io_result;
do {
State state = next_state_;
next_state_ = STATE_DISCONNECTED;
switch (state) {
case STATE_GENERATE_AUTH_TOKEN:
DCHECK_EQ(OK, rv);
rv = DoGenerateAuthToken();
break;
case STATE_GENERATE_AUTH_TOKEN_COMPLETE:
rv = DoGenerateAuthTokenComplete(rv);
break;
case STATE_CALCULATE_HEADERS:
DCHECK_EQ(OK, rv);
rv = DoCalculateHeaders();
break;
case STATE_CALCULATE_HEADERS_COMPLETE:
rv = DoCalculateHeadersComplete(rv);
break;
case STATE_SEND_REQUEST:
DCHECK_EQ(OK, rv);
net_log_.BeginEvent(
NetLogEventType::HTTP_TRANSACTION_TUNNEL_SEND_REQUEST);
rv = DoSendRequest();
break;
case STATE_SEND_REQUEST_COMPLETE:
net_log_.EndEventWithNetErrorCode(
NetLogEventType::HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, rv);
rv = DoSendRequestComplete(rv);
if (rv >= 0 || rv == ERR_IO_PENDING) {
net_log_.BeginEvent(
NetLogEventType::HTTP_TRANSACTION_TUNNEL_READ_HEADERS);
}
break;
case STATE_READ_REPLY_COMPLETE:
rv = DoReadReplyComplete(rv);
net_log_.EndEventWithNetErrorCode(
NetLogEventType::HTTP_TRANSACTION_TUNNEL_READ_HEADERS, rv);
break;
case STATE_PROCESS_RESPONSE_HEADERS:
DCHECK_EQ(OK, rv);
rv = DoProcessResponseHeaders();
break;
case STATE_PROCESS_RESPONSE_HEADERS_COMPLETE:
rv = DoProcessResponseHeadersComplete(rv);
break;
case STATE_PROCESS_RESPONSE_CODE:
DCHECK_EQ(OK, rv);
rv = DoProcessResponseCode();
break;
default:
NOTREACHED() << "bad state";
}
} while (rv != ERR_IO_PENDING && next_state_ != STATE_DISCONNECTED &&
next_state_ != STATE_OPEN);
return rv;
}
int SpdyProxyClientSocket::DoGenerateAuthToken() {
next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE;
return auth_->MaybeGenerateAuthToken(
&request_,
base::BindOnce(&SpdyProxyClientSocket::OnIOComplete,
weak_factory_.GetWeakPtr()),
net_log_);
}
int SpdyProxyClientSocket::DoGenerateAuthTokenComplete(int result) {
DCHECK_NE(ERR_IO_PENDING, result);
if (result == OK) {
next_state_ = STATE_CALCULATE_HEADERS;
}
return result;
}
int SpdyProxyClientSocket::DoCalculateHeaders() {
next_state_ = STATE_CALCULATE_HEADERS_COMPLETE;
authorization_headers_.Clear();
proxy_delegate_headers_.Clear();
if (auth_->HaveAuth()) {
auth_->AddAuthorizationHeader(&authorization_headers_);
}
if (proxy_delegate_) {
ASSIGN_OR_RETURN(
proxy_delegate_headers_,
proxy_delegate_->OnBeforeTunnelRequest(
proxy_chain_, proxy_chain_index_,
base::BindOnce(
&SpdyProxyClientSocket::OnBeforeTunnelRequestComplete,
weak_factory_.GetWeakPtr())),
[](const auto& e) {
CHECK_NE(OK, e);
return e;
});
}
return OK;
}
int SpdyProxyClientSocket::DoCalculateHeadersComplete(int result) {
DCHECK_NE(ERR_IO_PENDING, result);
if (result != OK) {
return result;
}
next_state_ = STATE_SEND_REQUEST;
request_.extra_headers.MergeFrom(proxy_delegate_headers_);
return result;
}
int SpdyProxyClientSocket::DoSendRequest() {
next_state_ = STATE_SEND_REQUEST_COMPLETE;
std::string request_line;
BuildTunnelRequest(endpoint_, authorization_headers_, user_agent_,
&request_line, &request_.extra_headers);
NetLogRequestHeaders(net_log_,
NetLogEventType::HTTP_TRANSACTION_SEND_TUNNEL_HEADERS,
request_line, &request_.extra_headers);
quiche::HttpHeaderBlock headers;
CreateSpdyHeadersFromHttpRequest(request_, std::nullopt,
request_.extra_headers, &headers);
return spdy_stream_->SendRequestHeaders(std::move(headers),
MORE_DATA_TO_SEND);
}
int SpdyProxyClientSocket::DoSendRequestComplete(int result) {
if (result < 0)
return result;
next_state_ = STATE_READ_REPLY_COMPLETE;
return ERR_IO_PENDING;
}
int SpdyProxyClientSocket::DoReadReplyComplete(int result) {
if (result < 0)
return result;
if (response_.headers->GetHttpVersion() < HttpVersion(1, 0))
return ERR_TUNNEL_CONNECTION_FAILED;
next_state_ = STATE_PROCESS_RESPONSE_HEADERS;
NetLogResponseHeaders(
net_log_, NetLogEventType::HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
response_.headers.get());
return OK;
}
int SpdyProxyClientSocket::DoProcessResponseHeaders() {
next_state_ = STATE_PROCESS_RESPONSE_HEADERS_COMPLETE;
if (proxy_delegate_) {
return proxy_delegate_->OnTunnelHeadersReceived(
proxy_chain_, proxy_chain_index_, *response_.headers,
base::BindOnce(&SpdyProxyClientSocket::OnIOComplete,
weak_factory_.GetWeakPtr()));
}
return OK;
}
int SpdyProxyClientSocket::DoProcessResponseHeadersComplete(int result) {
DCHECK_NE(ERR_IO_PENDING, result);
if (result != OK) {
return result;
}
next_state_ = STATE_PROCESS_RESPONSE_CODE;
return OK;
}
int SpdyProxyClientSocket::DoProcessResponseCode() {
switch (response_.headers->response_code()) {
case 200:
next_state_ = STATE_OPEN;
return OK;
case 407:
next_state_ = STATE_OPEN;
SanitizeProxyAuth(response_);
return HandleProxyAuthChallenge(auth_.get(), &response_, net_log_);
default:
return ERR_TUNNEL_CONNECTION_FAILED;
}
}
void SpdyProxyClientSocket::OnHeadersSent() {
DCHECK_EQ(next_state_, STATE_SEND_REQUEST_COMPLETE);
OnIOComplete(OK);
}
void SpdyProxyClientSocket::OnEarlyHintsReceived(
const quiche::HttpHeaderBlock& headers) {}
void SpdyProxyClientSocket::OnHeadersReceived(
const quiche::HttpHeaderBlock& response_headers) {
if (next_state_ != STATE_READ_REPLY_COMPLETE)
return;
const int rv = SpdyHeadersToHttpResponse(response_headers, &response_);
DCHECK_NE(rv, ERR_INCOMPLETE_HTTP2_HEADERS);
OnIOComplete(OK);
}
void SpdyProxyClientSocket::OnDataReceived(std::unique_ptr<SpdyBuffer> buffer) {
if (buffer) {
net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_RECEIVED,
buffer->GetRemaining());
read_buffer_queue_.Enqueue(std::move(buffer));
} else {
net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_RECEIVED, 0,
nullptr);
if (end_stream_state_ == EndStreamState::kNone) {
end_stream_state_ = EndStreamState::kEndStreamReceived;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&SpdyProxyClientSocket::MaybeSendEndStream,
weak_factory_.GetWeakPtr()));
}
}
if (read_callback_) {
if (user_buffer_) {
int rv = PopulateUserReadBuffer(user_buffer_->first(user_buffer_len_));
user_buffer_ = nullptr;
user_buffer_len_ = 0;
std::move(read_callback_).Run(rv);
} else {
std::move(read_callback_).Run(OK);
}
}
}
void SpdyProxyClientSocket::OnDataSent() {
if (end_stream_state_ == EndStreamState::kEndStreamSent) {
CHECK(write_callback_.is_null());
return;
}
DCHECK(!write_callback_.is_null());
int rv = write_buffer_len_;
write_buffer_len_ = 0;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&SpdyProxyClientSocket::RunWriteCallback,
weak_factory_.GetWeakPtr(), rv));
}
void SpdyProxyClientSocket::OnTrailers(
const quiche::HttpHeaderBlock& trailers) {
DUMP_WILL_BE_NOTREACHED();
}
void SpdyProxyClientSocket::OnClose(int status) {
was_ever_used_ = spdy_stream_->WasEverUsed();
spdy_stream_.reset();
bool connecting = next_state_ != STATE_DISCONNECTED &&
next_state_ < STATE_OPEN;
if (next_state_ == STATE_OPEN)
next_state_ = STATE_CLOSED;
else
next_state_ = STATE_DISCONNECTED;
base::WeakPtr<SpdyProxyClientSocket> weak_ptr = weak_factory_.GetWeakPtr();
CompletionOnceCallback write_callback = std::move(write_callback_);
write_buffer_len_ = 0;
if (connecting) {
DCHECK(!read_callback_.is_null());
std::move(read_callback_).Run(status);
} else if (!read_callback_.is_null()) {
OnDataReceived(std::unique_ptr<SpdyBuffer>());
}
if (weak_ptr.get() && !write_callback.is_null())
std::move(write_callback).Run(ERR_CONNECTION_CLOSED);
}
bool SpdyProxyClientSocket::CanGreaseFrameType() const {
return false;
}
NetLogSource SpdyProxyClientSocket::source_dependency() const {
return source_dependency_;
}
void SpdyProxyClientSocket::MaybeSendEndStream() {
DCHECK_NE(end_stream_state_, EndStreamState::kNone);
if (end_stream_state_ == EndStreamState::kEndStreamSent)
return;
if (!spdy_stream_)
return;
if (write_callback_)
return;
auto buffer = base::MakeRefCounted<IOBufferWithSize>(0);
spdy_stream_->SendData(buffer.get(), 0, NO_MORE_DATA_TO_SEND);
end_stream_state_ = EndStreamState::kEndStreamSent;
}
}