text: // 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 "net/http/http_proxy_client_socket.h"
  
  #include "base/bind.h"
  #include "base/bind_helpers.h"
  #include "base/strings/string_util.h"
  #include "base/strings/stringprintf.h"
  #include "googleurl/src/gurl.h"
  #include "net/base/auth.h"
  #include "net/base/host_port_pair.h"
  #include "net/base/io_buffer.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_request_info.h"
  #include "net/http/http_response_headers.h"
  #include "net/http/http_stream_parser.h"
  #include "net/socket/client_socket_handle.h"
  
  namespace net {
  
  HttpProxyClientSocket::HttpProxyClientSocket(
      ClientSocketHandle* transport_socket,
      const GURL& request_url,
      const std::string& user_agent,
      const HostPortPair& endpoint,
      const HostPortPair& proxy_server,
      HttpAuthCache* http_auth_cache,
      HttpAuthHandlerFactory* http_auth_handler_factory,
      bool tunnel,
      bool using_spdy,
      NextProto protocol_negotiated,
      bool is_https_proxy)
      : io_callback_(base::Bind(&HttpProxyClientSocket::OnIOComplete,
                                base::Unretained(this))),
        next_state_(STATE_NONE),
        transport_(transport_socket),
        endpoint_(endpoint),
        auth_(tunnel ?
            new HttpAuthController(HttpAuth::AUTH_PROXY,
                                   GURL((is_https_proxy ? "https://" : "http://")
                                        + proxy_server.ToString()),
                                   http_auth_cache,
                                   http_auth_handler_factory)
            : NULL),
        tunnel_(tunnel),
        using_spdy_(using_spdy),
        protocol_negotiated_(protocol_negotiated),
        is_https_proxy_(is_https_proxy),
        net_log_(transport_socket->socket()->NetLog()) {
    // Synthesize the bits of a request that we actually use.
    request_.url = request_url;
    request_.method = "GET";
    if (!user_agent.empty())
      request_.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent,
                                       user_agent);
  }
  
  HttpProxyClientSocket::~HttpProxyClientSocket() {
    Disconnect();
  }
  
  int HttpProxyClientSocket::RestartWithAuth(const CompletionCallback& callback) {
    DCHECK_EQ(STATE_NONE, next_state_);
    DCHECK(user_callback_.is_null());
  
    int rv = PrepareForAuthRestart();
    if (rv != OK)
      return rv;
  
    rv = DoLoop(OK);
    if (rv == ERR_IO_PENDING) {
      if (!callback.is_null())
        user_callback_ =  callback;
    }
  
    return rv;
  }
  
  const scoped_refptr<HttpAuthController>&
  HttpProxyClientSocket::GetAuthController() const {
    return auth_;
  }
  
  bool HttpProxyClientSocket::IsUsingSpdy() const {
    return using_spdy_;
  }
  
  NextProto HttpProxyClientSocket::GetProtocolNegotiated() const {
    return protocol_negotiated_;
  }
  
  const HttpResponseInfo* HttpProxyClientSocket::GetConnectResponseInfo() const {
    return response_.headers.get() ? &response_ : NULL;
  }
  
  HttpStream* HttpProxyClientSocket::CreateConnectResponseStream() {
    return new HttpBasicStream(transport_.release(),
                               http_stream_parser_.release(), false);
  }
  
  
  int HttpProxyClientSocket::Connect(const CompletionCallback& callback) {
    DCHECK(transport_.get());
    DCHECK(transport_->socket());
    DCHECK(user_callback_.is_null());
  
    // TODO(rch): figure out the right way to set up a tunnel with SPDY.
    // This approach sends the complete HTTPS request to the proxy
    // which allows the proxy to see "private" data.  Instead, we should
    // create an SSL tunnel to the origin server using the CONNECT method
    // inside a single SPDY stream.
    if (using_spdy_ || !tunnel_)
      next_state_ = STATE_DONE;
    if (next_state_ == STATE_DONE)
      return OK;
  
    DCHECK_EQ(STATE_NONE, next_state_);
    next_state_ = STATE_GENERATE_AUTH_TOKEN;
  
    int rv = DoLoop(OK);
    if (rv == ERR_IO_PENDING)
      user_callback_ = callback;
    return rv;
  }
  
  void HttpProxyClientSocket::Disconnect() {
    if (transport_.get())
      transport_->socket()->Disconnect();
  
    // Reset other states to make sure they aren't mistakenly used later.
    // These are the states initialized by Connect().
    next_state_ = STATE_NONE;
    user_callback_.Reset();
  }
  
  bool HttpProxyClientSocket::IsConnected() const {
    return next_state_ == STATE_DONE && transport_->socket()->IsConnected();
  }
  
  bool HttpProxyClientSocket::IsConnectedAndIdle() const {
    return next_state_ == STATE_DONE &&
      transport_->socket()->IsConnectedAndIdle();
  }
  
  const BoundNetLog& HttpProxyClientSocket::NetLog() const {
    return net_log_;
  }
  
  void HttpProxyClientSocket::SetSubresourceSpeculation() {
    if (transport_.get() && transport_->socket()) {
      transport_->socket()->SetSubresourceSpeculation();
    } else {
      NOTREACHED();
    }
  }
  
  void HttpProxyClientSocket::SetOmniboxSpeculation() {
    if (transport_.get() && transport_->socket()) {
      transport_->socket()->SetOmniboxSpeculation();
    } else {
      NOTREACHED();
    }
  }
  
  bool HttpProxyClientSocket::WasEverUsed() const {
    if (transport_.get() && transport_->socket()) {
      return transport_->socket()->WasEverUsed();
    }
    NOTREACHED();
    return false;
  }
  
  bool HttpProxyClientSocket::UsingTCPFastOpen() const {
    if (transport_.get() && transport_->socket()) {
      return transport_->socket()->UsingTCPFastOpen();
    }
    NOTREACHED();
    return false;
  }
  
  bool HttpProxyClientSocket::WasNpnNegotiated() const {
    if (transport_.get() && transport_->socket()) {
      return transport_->socket()->WasNpnNegotiated();
    }
    NOTREACHED();
    return false;
  }
  
  NextProto HttpProxyClientSocket::GetNegotiatedProtocol() const {
    if (transport_.get() && transport_->socket()) {
      return transport_->socket()->GetNegotiatedProtocol();
    }
    NOTREACHED();
    return kProtoUnknown;
  }
  
  bool HttpProxyClientSocket::GetSSLInfo(SSLInfo* ssl_info) {
    if (transport_.get() && transport_->socket()) {
      return transport_->socket()->GetSSLInfo(ssl_info);
    }
    NOTREACHED();
    return false;
  }
  
  int HttpProxyClientSocket::Read(IOBuffer* buf, int buf_len,
                                  const CompletionCallback& callback) {
    DCHECK(user_callback_.is_null());
    if (next_state_ != STATE_DONE) {
      // 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
      // an active network attacker.  We don't worry about this for HTTP
      // because an active network attacker can already control HTTP sessions.
      // We reach this case when the user cancels a 407 proxy auth prompt.
      // See http://crbug.com/8473.
      DCHECK_EQ(407, response_.headers->response_code());
      LogBlockedTunnelResponse();
  
      return ERR_TUNNEL_CONNECTION_FAILED;
    }
  
    return transport_->socket()->Read(buf, buf_len, callback);
  }
  
  int HttpProxyClientSocket::Write(IOBuffer* buf, int buf_len,
                                   const CompletionCallback& callback) {
    DCHECK_EQ(STATE_DONE, next_state_);
    DCHECK(user_callback_.is_null());
  
    return transport_->socket()->Write(buf, buf_len, callback);
  }
  
  bool HttpProxyClientSocket::SetReceiveBufferSize(int32 size) {
    return transport_->socket()->SetReceiveBufferSize(size);
  }
  
  bool HttpProxyClientSocket::SetSendBufferSize(int32 size) {
    return transport_->socket()->SetSendBufferSize(size);
  }
  
  int HttpProxyClientSocket::GetPeerAddress(IPEndPoint* address) const {
    return transport_->socket()->GetPeerAddress(address);
  }
  
  int HttpProxyClientSocket::GetLocalAddress(IPEndPoint* address) const {
    return transport_->socket()->GetLocalAddress(address);
  }
  
  int HttpProxyClientSocket::PrepareForAuthRestart() {
    if (!response_.headers.get())
      return ERR_CONNECTION_RESET;
  
    bool keep_alive = false;
    if (response_.headers->IsKeepAlive() &&
        http_stream_parser_->CanFindEndOfResponse()) {
      if (!http_stream_parser_->IsResponseBodyComplete()) {
        next_state_ = STATE_DRAIN_BODY;
        drain_buf_ = new IOBuffer(kDrainBodyBufferSize);
        return OK;
      }
      keep_alive = true;
    }
  
    // We don't need to drain the response body, so we act as if we had drained
    // the response body.
    return DidDrainBodyForAuthRestart(keep_alive);
  }
  
  int HttpProxyClientSocket::DidDrainBodyForAuthRestart(bool keep_alive) {
    if (keep_alive && transport_->socket()->IsConnectedAndIdle()) {
      next_state_ = STATE_GENERATE_AUTH_TOKEN;
      transport_->set_is_reused(true);
    } else {
      // This assumes that the underlying transport socket is a TCP socket,
      // since only TCP sockets are restartable.
      next_state_ = STATE_TCP_RESTART;
      transport_->socket()->Disconnect();
    }
  
    // Reset the other member variables.
    drain_buf_ = NULL;
    parser_buf_ = NULL;
    http_stream_parser_.reset();
    request_line_.clear();
    request_headers_.Clear();
    response_ = HttpResponseInfo();
    return OK;
  }
  
  void HttpProxyClientSocket::LogBlockedTunnelResponse() const {
    ProxyClientSocket::LogBlockedTunnelResponse(
        response_.headers->response_code(),
        request_.url,
        is_https_proxy_);
  }
  
  void HttpProxyClientSocket::DoCallback(int result) {
    DCHECK_NE(ERR_IO_PENDING, result);
    DCHECK(!user_callback_.is_null());
  
    // Since Run() may result in Read being called,
    // clear user_callback_ up front.
    CompletionCallback c = user_callback_;
    user_callback_.Reset();
    c.Run(result);
  }
  
  void HttpProxyClientSocket::OnIOComplete(int result) {
    DCHECK_NE(STATE_NONE, next_state_);
    DCHECK_NE(STATE_DONE, next_state_);
    int rv = DoLoop(result);
    if (rv != ERR_IO_PENDING)
      DoCallback(rv);
  }
  
  int HttpProxyClientSocket::DoLoop(int last_io_result) {
    DCHECK_NE(next_state_, STATE_NONE);
    DCHECK_NE(next_state_, STATE_DONE);
    int rv = last_io_result;
    do {
      State state = next_state_;
      next_state_ = STATE_NONE;
      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(
              NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST);
          rv = DoSendRequest();
          break;
        case STATE_SEND_REQUEST_COMPLETE:
          rv = DoSendRequestComplete(rv);
          net_log_.EndEventWithNetErrorCode(
              NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, rv);
          break;
        case STATE_READ_HEADERS:
          DCHECK_EQ(OK, rv);
          net_log_.BeginEvent(
              NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS);
          rv = DoReadHeaders();
          break;
        case STATE_READ_HEADERS_COMPLETE:
          rv = DoReadHeadersComplete(rv);
          net_log_.EndEventWithNetErrorCode(
              NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS, rv);
          break;
        case STATE_DRAIN_BODY:
          DCHECK_EQ(OK, rv);
          rv = DoDrainBody();
          break;
        case STATE_DRAIN_BODY_COMPLETE:
          rv = DoDrainBodyComplete(rv);
          break;
        case STATE_TCP_RESTART:
          DCHECK_EQ(OK, rv);
          rv = DoTCPRestart();
          break;
        case STATE_TCP_RESTART_COMPLETE:
          rv = DoTCPRestartComplete(rv);
          break;
        case STATE_DONE:
          break;
        default:
          NOTREACHED() << "bad state";
          rv = ERR_UNEXPECTED;
          break;
      }
    } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE &&
             next_state_ != STATE_DONE);
    return rv;
  }
  
  int HttpProxyClientSocket::DoGenerateAuthToken() {
    next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE;
    return auth_->MaybeGenerateAuthToken(&request_, io_callback_, net_log_);
  }
  
  int HttpProxyClientSocket::DoGenerateAuthTokenComplete(int result) {
    DCHECK_NE(ERR_IO_PENDING, result);
    if (result == OK)
      next_state_ = STATE_SEND_REQUEST;
    return result;
  }
  
  int HttpProxyClientSocket::DoSendRequest() {
    next_state_ = STATE_SEND_REQUEST_COMPLETE;
  
    // This is constructed lazily (instead of within our Start method), so that
    // we have proxy info available.
    if (request_line_.empty()) {
      DCHECK(request_headers_.IsEmpty());
      HttpRequestHeaders authorization_headers;
      if (auth_->HaveAuth())
        auth_->AddAuthorizationHeader(&authorization_headers);
      BuildTunnelRequest(request_, authorization_headers, endpoint_,
                         &request_line_, &request_headers_);
  
      net_log_.AddEvent(
          NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS,
          base::Bind(&HttpRequestHeaders::NetLogCallback,
                     base::Unretained(&request_headers_),
                     &request_line_));
    }
  
    parser_buf_ = new GrowableIOBuffer();
    http_stream_parser_.reset(new HttpStreamParser(
        transport_.get(), &request_, parser_buf_.get(), net_log_));
    return http_stream_parser_->SendRequest(
        request_line_, request_headers_, &response_, io_callback_);
  }
  
  int HttpProxyClientSocket::DoSendRequestComplete(int result) {
    if (result < 0)
      return result;
  
    next_state_ = STATE_READ_HEADERS;
    return OK;
  }
  
  int HttpProxyClientSocket::DoReadHeaders() {
    next_state_ = STATE_READ_HEADERS_COMPLETE;
    return http_stream_parser_->ReadResponseHeaders(io_callback_);
  }
  
  int HttpProxyClientSocket::DoReadHeadersComplete(int result) {
    if (result < 0)
      return result;
  
    // Require the "HTTP/1.x" status line for SSL CONNECT.
    if (response_.headers->GetParsedHttpVersion() < HttpVersion(1, 0))
      return ERR_TUNNEL_CONNECTION_FAILED;
  
    net_log_.AddEvent(
        NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
        base::Bind(&HttpResponseHeaders::NetLogCallback, response_.headers));
  
    switch (response_.headers->response_code()) {
      case 200:  // OK
        if (http_stream_parser_->IsMoreDataBuffered())
          // The proxy sent extraneous data after the headers.
          return ERR_TUNNEL_CONNECTION_FAILED;
  
        next_state_ = STATE_DONE;
        return OK;
  
        // We aren't able to CONNECT to the remote host through the proxy.  We
        // need to be very suspicious about the response because an active network
        // attacker can force us into this state by masquerading as the proxy.
        // The only safe thing to do here is to fail the connection because our
        // client is expecting an SSL protected response.
        // See http://crbug.com/7338.
  
      case 302:  // Found / Moved Temporarily
        // Attempt to follow redirects from HTTPS proxies, but only if we can
        // sanitize the response.  This still allows a rogue HTTPS proxy to
        // redirect an HTTPS site load to a similar-looking site, but no longer
        // allows it to impersonate the site the user requested.
        if (is_https_proxy_ && SanitizeProxyRedirect(&response_, request_.url))
          return ERR_HTTPS_PROXY_TUNNEL_RESPONSE;
  
        // We're not using an HTTPS proxy, or we couldn't sanitize the redirect.
        LogBlockedTunnelResponse();
        return ERR_TUNNEL_CONNECTION_FAILED;
  
      case 407:  // Proxy Authentication Required
        // We need this status code to allow proxy authentication.  Our
        // authentication code is smart enough to avoid being tricked by an
        // active network attacker.
        // The next state is intentionally not set as it should be STATE_NONE;
        return HandleProxyAuthChallenge(auth_.get(), &response_, net_log_);
  
      default:
        // Ignore response to avoid letting the proxy impersonate the target
        // server.  (See http://crbug.com/137891.)
        // We lose something by doing this.  We have seen proxy 403, 404, and
        // 501 response bodies that contain a useful error message.  For
        // example, Squid uses a 404 response to report the DNS error: "The
        // domain name does not exist."
        LogBlockedTunnelResponse();
        return ERR_TUNNEL_CONNECTION_FAILED;
    }
  }
  
  int HttpProxyClientSocket::DoDrainBody() {
    DCHECK(drain_buf_.get());
    DCHECK(transport_->is_initialized());
    next_state_ = STATE_DRAIN_BODY_COMPLETE;
    return http_stream_parser_->ReadResponseBody(
        drain_buf_.get(), kDrainBodyBufferSize, io_callback_);
  }
  
  int HttpProxyClientSocket::DoDrainBodyComplete(int result) {
    if (result < 0)
      return result;
  
    if (http_stream_parser_->IsResponseBodyComplete())
      return DidDrainBodyForAuthRestart(true);
  
    // Keep draining.
    next_state_ = STATE_DRAIN_BODY;
    return OK;
  }
  
  int HttpProxyClientSocket::DoTCPRestart() {
    next_state_ = STATE_TCP_RESTART_COMPLETE;
    return transport_->socket()->Connect(
        base::Bind(&HttpProxyClientSocket::OnIOComplete, base::Unretained(this)));
  }
  
  int HttpProxyClientSocket::DoTCPRestartComplete(int result) {
    if (result != OK)
      return result;
  
    next_state_ = STATE_GENERATE_AUTH_TOKEN;
    return result;
  }
  
  }  // namespace net
  