123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452 |
- // Copyright (c) 2017 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/quic/quic_proxy_client_socket.h"
- #include <cstdio>
- #include <utility>
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/values.h"
- #include "net/base/proxy_delegate.h"
- #include "net/http/http_auth_controller.h"
- #include "net/http/http_log_util.h"
- #include "net/http/http_response_headers.h"
- #include "net/log/net_log_source.h"
- #include "net/log/net_log_source_type.h"
- #include "net/quic/quic_http_utils.h"
- #include "net/spdy/spdy_http_utils.h"
- #include "net/traffic_annotation/network_traffic_annotation.h"
- namespace net {
- QuicProxyClientSocket::QuicProxyClientSocket(
- std::unique_ptr<QuicChromiumClientStream::Handle> stream,
- std::unique_ptr<QuicChromiumClientSession::Handle> session,
- const ProxyServer& proxy_server,
- const std::string& user_agent,
- const HostPortPair& endpoint,
- const NetLogWithSource& net_log,
- scoped_refptr<HttpAuthController> auth_controller,
- ProxyDelegate* proxy_delegate)
- : stream_(std::move(stream)),
- session_(std::move(session)),
- endpoint_(endpoint),
- auth_(std::move(auth_controller)),
- proxy_server_(proxy_server),
- proxy_delegate_(proxy_delegate),
- user_agent_(user_agent),
- net_log_(net_log) {
- DCHECK(stream_->IsOpen());
- request_.method = "CONNECT";
- request_.url = GURL("https://" + endpoint.ToString());
- net_log_.BeginEventReferencingSource(NetLogEventType::SOCKET_ALIVE,
- net_log_.source());
- net_log_.AddEventReferencingSource(
- NetLogEventType::HTTP2_PROXY_CLIENT_SESSION, stream_->net_log().source());
- }
- QuicProxyClientSocket::~QuicProxyClientSocket() {
- Disconnect();
- net_log_.EndEvent(NetLogEventType::SOCKET_ALIVE);
- }
- const HttpResponseInfo* QuicProxyClientSocket::GetConnectResponseInfo() const {
- return response_.headers.get() ? &response_ : nullptr;
- }
- const scoped_refptr<HttpAuthController>&
- QuicProxyClientSocket::GetAuthController() const {
- return auth_;
- }
- int QuicProxyClientSocket::RestartWithAuth(CompletionOnceCallback callback) {
- // A QUIC Stream can only handle a single request, so the underlying
- // stream may not be reused and a new QuicProxyClientSocket must be
- // created (possibly on top of the same QUIC Session).
- next_state_ = STATE_DISCONNECTED;
- return ERR_UNABLE_TO_REUSE_CONNECTION_FOR_PROXY_AUTH;
- }
- // Ignore priority changes, just use priority of initial request. Since multiple
- // requests are pooled on the QuicProxyClientSocket, reprioritization doesn't
- // really work.
- //
- // TODO(mmenke): Use a single priority value for all QuicProxyClientSockets,
- // regardless of what priority they're created with.
- void QuicProxyClientSocket::SetStreamPriority(RequestPriority priority) {}
- // Sends a HEADERS frame to the proxy with a CONNECT request
- // for the specified endpoint. Waits for the server to send back
- // a HEADERS frame. OK will be returned if the status is 200.
- // ERR_TUNNEL_CONNECTION_FAILED will be returned for any other status.
- // In any of these cases, Read() may be called to retrieve the HTTP
- // response body. Any other return values should be considered fatal.
- int QuicProxyClientSocket::Connect(CompletionOnceCallback callback) {
- DCHECK(connect_callback_.is_null());
- if (!stream_->IsOpen())
- return ERR_CONNECTION_CLOSED;
- DCHECK_EQ(STATE_DISCONNECTED, next_state_);
- next_state_ = STATE_GENERATE_AUTH_TOKEN;
- int rv = DoLoop(OK);
- if (rv == ERR_IO_PENDING)
- connect_callback_ = std::move(callback);
- return rv;
- }
- void QuicProxyClientSocket::Disconnect() {
- connect_callback_.Reset();
- read_callback_.Reset();
- read_buf_ = nullptr;
- write_callback_.Reset();
- write_buf_len_ = 0;
- next_state_ = STATE_DISCONNECTED;
- stream_->Reset(quic::QUIC_STREAM_CANCELLED);
- }
- bool QuicProxyClientSocket::IsConnected() const {
- return next_state_ == STATE_CONNECT_COMPLETE && stream_->IsOpen();
- }
- bool QuicProxyClientSocket::IsConnectedAndIdle() const {
- return IsConnected() && !stream_->HasBytesToRead();
- }
- const NetLogWithSource& QuicProxyClientSocket::NetLog() const {
- return net_log_;
- }
- bool QuicProxyClientSocket::WasEverUsed() const {
- return session_->WasEverUsed();
- }
- bool QuicProxyClientSocket::WasAlpnNegotiated() const {
- // Do not delegate to `session_`. While `session_` negotiates ALPN with the
- // proxy, this object represents the tunneled TCP connection to the origin.
- return false;
- }
- NextProto QuicProxyClientSocket::GetNegotiatedProtocol() const {
- // Do not delegate to `session_`. While `session_` negotiates ALPN with the
- // proxy, this object represents the tunneled TCP connection to the origin.
- return kProtoUnknown;
- }
- bool QuicProxyClientSocket::GetSSLInfo(SSLInfo* ssl_info) {
- // Do not delegate to `session_`. While `session_` has a secure channel to the
- // proxy, this object represents the tunneled TCP connection to the origin.
- return false;
- }
- int64_t QuicProxyClientSocket::GetTotalReceivedBytes() const {
- return stream_->NumBytesConsumed();
- }
- void QuicProxyClientSocket::ApplySocketTag(const SocketTag& tag) {
- // In the case of a connection to the proxy using HTTP/2 or HTTP/3 where the
- // underlying socket may multiplex multiple streams, applying this request's
- // socket tag to the multiplexed session would incorrectly apply the socket
- // tag to all mutliplexed streams. Fortunately socket tagging is only
- // supported on Android without the data reduction proxy, so only simple HTTP
- // proxies are supported, so proxies won't be using HTTP/2 or HTTP/3. Enforce
- // that a specific (non-default) tag isn't being applied.
- CHECK(tag == SocketTag());
- }
- int QuicProxyClientSocket::Read(IOBuffer* buf,
- int buf_len,
- CompletionOnceCallback callback) {
- DCHECK(connect_callback_.is_null());
- DCHECK(read_callback_.is_null());
- DCHECK(!read_buf_);
- if (next_state_ == STATE_DISCONNECTED)
- return ERR_SOCKET_NOT_CONNECTED;
- if (!stream_->IsOpen()) {
- return 0;
- }
- int rv =
- stream_->ReadBody(buf, buf_len,
- base::BindOnce(&QuicProxyClientSocket::OnReadComplete,
- weak_factory_.GetWeakPtr()));
- if (rv == ERR_IO_PENDING) {
- read_callback_ = std::move(callback);
- read_buf_ = buf;
- } else if (rv == 0) {
- net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_RECEIVED, 0,
- nullptr);
- } else if (rv > 0) {
- net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_RECEIVED, rv,
- buf->data());
- }
- return rv;
- }
- void QuicProxyClientSocket::OnReadComplete(int rv) {
- if (!stream_->IsOpen())
- rv = 0;
- if (!read_callback_.is_null()) {
- DCHECK(read_buf_);
- if (rv >= 0) {
- net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_RECEIVED, rv,
- read_buf_->data());
- }
- read_buf_ = nullptr;
- std::move(read_callback_).Run(rv);
- }
- }
- int QuicProxyClientSocket::Write(
- IOBuffer* buf,
- int buf_len,
- CompletionOnceCallback callback,
- const NetworkTrafficAnnotationTag& traffic_annotation) {
- DCHECK(connect_callback_.is_null());
- DCHECK(write_callback_.is_null());
- if (next_state_ != STATE_CONNECT_COMPLETE)
- return ERR_SOCKET_NOT_CONNECTED;
- net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_SENT, buf_len,
- buf->data());
- int rv = stream_->WriteStreamData(
- base::StringPiece(buf->data(), buf_len), false,
- base::BindOnce(&QuicProxyClientSocket::OnWriteComplete,
- weak_factory_.GetWeakPtr()));
- if (rv == OK)
- return buf_len;
- if (rv == ERR_IO_PENDING) {
- write_callback_ = std::move(callback);
- write_buf_len_ = buf_len;
- }
- return rv;
- }
- void QuicProxyClientSocket::OnWriteComplete(int rv) {
- if (!write_callback_.is_null()) {
- if (rv == OK)
- rv = write_buf_len_;
- write_buf_len_ = 0;
- std::move(write_callback_).Run(rv);
- }
- }
- int QuicProxyClientSocket::SetReceiveBufferSize(int32_t size) {
- return ERR_NOT_IMPLEMENTED;
- }
- int QuicProxyClientSocket::SetSendBufferSize(int32_t size) {
- return ERR_NOT_IMPLEMENTED;
- }
- int QuicProxyClientSocket::GetPeerAddress(IPEndPoint* address) const {
- return IsConnected() ? session_->GetPeerAddress(address)
- : ERR_SOCKET_NOT_CONNECTED;
- }
- int QuicProxyClientSocket::GetLocalAddress(IPEndPoint* address) const {
- return IsConnected() ? session_->GetSelfAddress(address)
- : ERR_SOCKET_NOT_CONNECTED;
- }
- void QuicProxyClientSocket::OnIOComplete(int result) {
- DCHECK_NE(STATE_DISCONNECTED, next_state_);
- int rv = DoLoop(result);
- if (rv != ERR_IO_PENDING) {
- // Connect() finished (successfully or unsuccessfully).
- DCHECK(!connect_callback_.is_null());
- std::move(connect_callback_).Run(rv);
- }
- }
- int QuicProxyClientSocket::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_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);
- break;
- case STATE_READ_REPLY:
- rv = DoReadReply();
- break;
- case STATE_READ_REPLY_COMPLETE:
- rv = DoReadReplyComplete(rv);
- net_log_.EndEventWithNetErrorCode(
- NetLogEventType::HTTP_TRANSACTION_TUNNEL_READ_HEADERS, rv);
- break;
- default:
- NOTREACHED() << "bad state";
- rv = ERR_UNEXPECTED;
- break;
- }
- } while (rv != ERR_IO_PENDING && next_state_ != STATE_DISCONNECTED &&
- next_state_ != STATE_CONNECT_COMPLETE);
- return rv;
- }
- int QuicProxyClientSocket::DoGenerateAuthToken() {
- next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE;
- return auth_->MaybeGenerateAuthToken(
- &request_,
- base::BindOnce(&QuicProxyClientSocket::OnIOComplete,
- weak_factory_.GetWeakPtr()),
- net_log_);
- }
- int QuicProxyClientSocket::DoGenerateAuthTokenComplete(int result) {
- DCHECK_NE(ERR_IO_PENDING, result);
- if (result == OK)
- next_state_ = STATE_SEND_REQUEST;
- return result;
- }
- int QuicProxyClientSocket::DoSendRequest() {
- next_state_ = STATE_SEND_REQUEST_COMPLETE;
- // Add Proxy-Authentication header if necessary.
- HttpRequestHeaders authorization_headers;
- if (auth_->HaveAuth()) {
- auth_->AddAuthorizationHeader(&authorization_headers);
- }
- if (proxy_delegate_) {
- HttpRequestHeaders proxy_delegate_headers;
- proxy_delegate_->OnBeforeTunnelRequest(proxy_server_,
- &proxy_delegate_headers);
- request_.extra_headers.MergeFrom(proxy_delegate_headers);
- }
- 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);
- spdy::Http2HeaderBlock headers;
- CreateSpdyHeadersFromHttpRequest(request_, request_.extra_headers, &headers);
- return stream_->WriteHeaders(std::move(headers), false, nullptr);
- }
- int QuicProxyClientSocket::DoSendRequestComplete(int result) {
- if (result >= 0) {
- // Wait for HEADERS frame from the server
- next_state_ = STATE_READ_REPLY; // STATE_READ_REPLY_COMPLETE;
- result = OK;
- }
- if (result >= 0 || result == ERR_IO_PENDING) {
- // Emit extra event so can use the same events as HttpProxyClientSocket.
- net_log_.BeginEvent(NetLogEventType::HTTP_TRANSACTION_TUNNEL_READ_HEADERS);
- }
- return result;
- }
- int QuicProxyClientSocket::DoReadReply() {
- next_state_ = STATE_READ_REPLY_COMPLETE;
- int rv = stream_->ReadInitialHeaders(
- &response_header_block_,
- base::BindOnce(&QuicProxyClientSocket::OnReadResponseHeadersComplete,
- weak_factory_.GetWeakPtr()));
- if (rv == ERR_IO_PENDING)
- return ERR_IO_PENDING;
- if (rv < 0)
- return rv;
- return ProcessResponseHeaders(response_header_block_);
- }
- int QuicProxyClientSocket::DoReadReplyComplete(int result) {
- if (result < 0)
- return result;
- // Require the "HTTP/1.x" status line for SSL CONNECT.
- if (response_.headers->GetHttpVersion() < HttpVersion(1, 0))
- return ERR_TUNNEL_CONNECTION_FAILED;
- NetLogResponseHeaders(
- net_log_, NetLogEventType::HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
- response_.headers.get());
- if (proxy_delegate_) {
- int rv = proxy_delegate_->OnTunnelHeadersReceived(proxy_server_,
- *response_.headers);
- if (rv != OK) {
- DCHECK_NE(ERR_IO_PENDING, rv);
- return rv;
- }
- }
- switch (response_.headers->response_code()) {
- case 200: // OK
- next_state_ = STATE_CONNECT_COMPLETE;
- return OK;
- case 407: // Proxy Authentication Required
- next_state_ = STATE_CONNECT_COMPLETE;
- SanitizeProxyAuth(response_);
- return HandleProxyAuthChallenge(auth_.get(), &response_, net_log_);
- default:
- // Ignore response to avoid letting the proxy impersonate the target
- // server. (See http://crbug.com/137891.)
- return ERR_TUNNEL_CONNECTION_FAILED;
- }
- }
- void QuicProxyClientSocket::OnReadResponseHeadersComplete(int result) {
- // Convert the now-populated spdy::Http2HeaderBlock to HttpResponseInfo
- if (result > 0)
- result = ProcessResponseHeaders(response_header_block_);
- if (result != ERR_IO_PENDING)
- OnIOComplete(result);
- }
- int QuicProxyClientSocket::ProcessResponseHeaders(
- const spdy::Http2HeaderBlock& headers) {
- if (SpdyHeadersToHttpResponse(headers, &response_) != OK) {
- DLOG(WARNING) << "Invalid headers";
- return ERR_QUIC_PROTOCOL_ERROR;
- }
- return OK;
- }
- } // namespace net
|