普通文本  |  1354行  |  43.26 KB

// 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.
//
// TODO(ukai): code is similar with http_network_transaction.cc.  We should
//   think about ways to share code, if possible.

#include "net/socket_stream/socket_stream.h"

#include <set>
#include <string>
#include <vector>

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "net/base/auth.h"
#include "net/base/io_buffer.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/base/net_util.h"
#include "net/dns/host_resolver.h"
#include "net/http/http_auth_controller.h"
#include "net/http/http_network_session.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_request_info.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_stream_factory.h"
#include "net/http/http_transaction_factory.h"
#include "net/http/http_util.h"
#include "net/socket/client_socket_factory.h"
#include "net/socket/client_socket_handle.h"
#include "net/socket/socks5_client_socket.h"
#include "net/socket/socks_client_socket.h"
#include "net/socket/ssl_client_socket.h"
#include "net/socket/tcp_client_socket.h"
#include "net/socket_stream/socket_stream_metrics.h"
#include "net/ssl/ssl_cert_request_info.h"
#include "net/ssl/ssl_info.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"

static const int kMaxPendingSendAllowed = 32768;  // 32 kilobytes.
static const int kReadBufferSize = 4096;

namespace net {

int SocketStream::Delegate::OnStartOpenConnection(
    SocketStream* socket, const CompletionCallback& callback) {
  return OK;
}

void SocketStream::Delegate::OnAuthRequired(SocketStream* socket,
                                            AuthChallengeInfo* auth_info) {
  // By default, no credential is available and close the connection.
  socket->Close();
}

void SocketStream::Delegate::OnSSLCertificateError(
    SocketStream* socket,
    const SSLInfo& ssl_info,
    bool fatal) {
  socket->CancelWithSSLError(ssl_info);
}

bool SocketStream::Delegate::CanGetCookies(SocketStream* socket,
                                           const GURL& url) {
  return true;
}

bool SocketStream::Delegate::CanSetCookie(SocketStream* request,
                                          const GURL& url,
                                          const std::string& cookie_line,
                                          CookieOptions* options) {
  return true;
}

SocketStream::ResponseHeaders::ResponseHeaders() : IOBuffer() {}

void SocketStream::ResponseHeaders::Realloc(size_t new_size) {
  headers_.reset(static_cast<char*>(realloc(headers_.release(), new_size)));
}

SocketStream::ResponseHeaders::~ResponseHeaders() { data_ = NULL; }

SocketStream::SocketStream(const GURL& url, Delegate* delegate,
                           URLRequestContext* context,
                           CookieStore* cookie_store)
    : delegate_(delegate),
      url_(url),
      max_pending_send_allowed_(kMaxPendingSendAllowed),
      context_(context),
      next_state_(STATE_NONE),
      factory_(ClientSocketFactory::GetDefaultFactory()),
      proxy_mode_(kDirectConnection),
      proxy_url_(url),
      pac_request_(NULL),
      connection_(new ClientSocketHandle),
      privacy_mode_(PRIVACY_MODE_DISABLED),
      // Unretained() is required; without it, Bind() creates a circular
      // dependency and the SocketStream object will not be freed.
      io_callback_(base::Bind(&SocketStream::OnIOCompleted,
                              base::Unretained(this))),
      read_buf_(NULL),
      current_write_buf_(NULL),
      waiting_for_write_completion_(false),
      closing_(false),
      server_closed_(false),
      metrics_(new SocketStreamMetrics(url)),
      cookie_store_(cookie_store) {
  DCHECK(base::MessageLoop::current())
      << "The current base::MessageLoop must exist";
  DCHECK(base::MessageLoopForIO::IsCurrent())
      << "The current base::MessageLoop must be TYPE_IO";
  DCHECK(delegate_);

  if (context_) {
    if (!cookie_store_.get())
      cookie_store_ = context_->cookie_store();

    net_log_ = BoundNetLog::Make(
        context->net_log(),
        NetLog::SOURCE_SOCKET_STREAM);

    net_log_.BeginEvent(NetLog::TYPE_REQUEST_ALIVE);
  }
}

SocketStream::UserData* SocketStream::GetUserData(
    const void* key) const {
  UserDataMap::const_iterator found = user_data_.find(key);
  if (found != user_data_.end())
    return found->second.get();
  return NULL;
}

void SocketStream::SetUserData(const void* key, UserData* data) {
  user_data_[key] = linked_ptr<UserData>(data);
}

bool SocketStream::is_secure() const {
  return url_.SchemeIs("wss");
}

void SocketStream::DetachContext() {
  if (!context_)
    return;

  if (pac_request_) {
    context_->proxy_service()->CancelPacRequest(pac_request_);
    pac_request_ = NULL;
  }

  net_log_.EndEvent(NetLog::TYPE_REQUEST_ALIVE);
  net_log_ = BoundNetLog();

  context_ = NULL;
  cookie_store_ = NULL;
}

void SocketStream::CheckPrivacyMode() {
  if (context_ && context_->network_delegate()) {
    bool enable = context_->network_delegate()->CanEnablePrivacyMode(url_,
                                                                     url_);
    privacy_mode_ = enable ? PRIVACY_MODE_ENABLED : PRIVACY_MODE_DISABLED;
    // Disable Channel ID if privacy mode is enabled.
    if (enable)
      server_ssl_config_.channel_id_enabled = false;
  }
}

void SocketStream::Connect() {
  DCHECK(base::MessageLoop::current())
      << "The current base::MessageLoop must exist";
  DCHECK(base::MessageLoopForIO::IsCurrent())
      << "The current base::MessageLoop must be TYPE_IO";
  if (context_) {
    context_->ssl_config_service()->GetSSLConfig(&server_ssl_config_);
    proxy_ssl_config_ = server_ssl_config_;
  }
  CheckPrivacyMode();

  DCHECK_EQ(next_state_, STATE_NONE);

  AddRef();  // Released in Finish()
  // Open a connection asynchronously, so that delegate won't be called
  // back before returning Connect().
  next_state_ = STATE_BEFORE_CONNECT;
  net_log_.BeginEvent(
      NetLog::TYPE_SOCKET_STREAM_CONNECT,
      NetLog::StringCallback("url", &url_.possibly_invalid_spec()));
  base::MessageLoop::current()->PostTask(
      FROM_HERE, base::Bind(&SocketStream::DoLoop, this, OK));
}

size_t SocketStream::GetTotalSizeOfPendingWriteBufs() const {
  size_t total_size = 0;
  for (PendingDataQueue::const_iterator iter = pending_write_bufs_.begin();
       iter != pending_write_bufs_.end();
       ++iter)
    total_size += (*iter)->size();
  return total_size;
}

bool SocketStream::SendData(const char* data, int len) {
  DCHECK(base::MessageLoop::current())
      << "The current base::MessageLoop must exist";
  DCHECK(base::MessageLoopForIO::IsCurrent())
      << "The current base::MessageLoop must be TYPE_IO";
  DCHECK_GT(len, 0);

  if (!connection_->socket() ||
      !connection_->socket()->IsConnected() || next_state_ == STATE_NONE) {
    return false;
  }

  int total_buffered_bytes = len;
  if (current_write_buf_.get()) {
    // Since
    // - the purpose of this check is to limit the amount of buffer used by
    //   this instance.
    // - the DrainableIOBuffer doesn't release consumed memory.
    // we need to use not BytesRemaining() but size() here.
    total_buffered_bytes += current_write_buf_->size();
  }
  total_buffered_bytes += GetTotalSizeOfPendingWriteBufs();
  if (total_buffered_bytes > max_pending_send_allowed_)
    return false;

  // TODO(tyoshino): Split data into smaller chunks e.g. 8KiB to free consumed
  // buffer progressively
  pending_write_bufs_.push_back(make_scoped_refptr(
      new IOBufferWithSize(len)));
  memcpy(pending_write_bufs_.back()->data(), data, len);

  // If current_write_buf_ is not NULL, it means that a) there's ongoing write
  // operation or b) the connection is being closed. If a), the buffer we just
  // pushed will be automatically handled when the completion callback runs
  // the loop, and therefore we don't need to enqueue DoLoop(). If b), it's ok
  // to do nothing. If current_write_buf_ is NULL, to make sure DoLoop() is
  // ran soon, enequeue it.
  if (!current_write_buf_.get()) {
    // Send pending data asynchronously, so that delegate won't be called
    // back before returning from SendData().
    base::MessageLoop::current()->PostTask(
        FROM_HERE, base::Bind(&SocketStream::DoLoop, this, OK));
  }

  return true;
}

void SocketStream::Close() {
  DCHECK(base::MessageLoop::current())
      << "The current base::MessageLoop must exist";
  DCHECK(base::MessageLoopForIO::IsCurrent())
      << "The current base::MessageLoop must be TYPE_IO";
  // If next_state_ is STATE_NONE, the socket was not opened, or already
  // closed.  So, return immediately.
  // Otherwise, it might call Finish() more than once, so breaks balance
  // of AddRef() and Release() in Connect() and Finish(), respectively.
  if (next_state_ == STATE_NONE)
    return;
  base::MessageLoop::current()->PostTask(
      FROM_HERE, base::Bind(&SocketStream::DoClose, this));
}

void SocketStream::RestartWithAuth(const AuthCredentials& credentials) {
  DCHECK(base::MessageLoop::current())
      << "The current base::MessageLoop must exist";
  DCHECK(base::MessageLoopForIO::IsCurrent())
      << "The current base::MessageLoop must be TYPE_IO";
  DCHECK(proxy_auth_controller_.get());
  if (!connection_->socket()) {
    DVLOG(1) << "Socket is closed before restarting with auth.";
    return;
  }

  proxy_auth_controller_->ResetAuth(credentials);

  base::MessageLoop::current()->PostTask(
      FROM_HERE, base::Bind(&SocketStream::DoRestartWithAuth, this));
}

void SocketStream::DetachDelegate() {
  if (!delegate_)
    return;
  delegate_ = NULL;
  // Prevent the rest of the function from executing if we are being called from
  // within Finish().
  if (next_state_ == STATE_NONE)
    return;
  net_log_.AddEvent(NetLog::TYPE_CANCELLED);
  // We don't need to send pending data when client detach the delegate.
  pending_write_bufs_.clear();
  Close();
}

const ProxyServer& SocketStream::proxy_server() const {
  return proxy_info_.proxy_server();
}

void SocketStream::SetClientSocketFactory(
    ClientSocketFactory* factory) {
  DCHECK(factory);
  factory_ = factory;
}

void SocketStream::CancelWithError(int error) {
  base::MessageLoop::current()->PostTask(
      FROM_HERE, base::Bind(&SocketStream::DoLoop, this, error));
}

void SocketStream::CancelWithSSLError(const SSLInfo& ssl_info) {
  CancelWithError(MapCertStatusToNetError(ssl_info.cert_status));
}

void SocketStream::ContinueDespiteError() {
  base::MessageLoop::current()->PostTask(
      FROM_HERE, base::Bind(&SocketStream::DoLoop, this, OK));
}

SocketStream::~SocketStream() {
  DetachContext();
  DCHECK(!delegate_);
  DCHECK(!pac_request_);
}

SocketStream::RequestHeaders::~RequestHeaders() { data_ = NULL; }

void SocketStream::set_addresses(const AddressList& addresses) {
  addresses_ = addresses;
}

void SocketStream::DoClose() {
  closing_ = true;
  // If next_state_ is:
  // - STATE_TCP_CONNECT_COMPLETE, it's waiting other socket establishing
  //   connection.
  // - STATE_AUTH_REQUIRED, it's waiting for restarting.
  // - STATE_RESOLVE_PROTOCOL_COMPLETE, it's waiting for delegate_ to finish
  //   OnStartOpenConnection method call
  // In these states, we'll close the SocketStream now.
  if (next_state_ == STATE_TCP_CONNECT_COMPLETE ||
      next_state_ == STATE_AUTH_REQUIRED ||
      next_state_ == STATE_RESOLVE_PROTOCOL_COMPLETE) {
    DoLoop(ERR_ABORTED);
    return;
  }
  // If next_state_ is STATE_READ_WRITE, we'll run DoLoop and close
  // the SocketStream.
  // If it's writing now, we should defer the closing after the current
  // writing is completed.
  if (next_state_ == STATE_READ_WRITE && !current_write_buf_.get())
    DoLoop(ERR_ABORTED);

  // In other next_state_, we'll wait for callback of other APIs, such as
  // ResolveProxy().
}

void SocketStream::Finish(int result) {
  DCHECK(base::MessageLoop::current())
      << "The current base::MessageLoop must exist";
  DCHECK(base::MessageLoopForIO::IsCurrent())
      << "The current base::MessageLoop must be TYPE_IO";
  DCHECK_LE(result, OK);
  if (result == OK)
    result = ERR_CONNECTION_CLOSED;
  DCHECK_EQ(next_state_, STATE_NONE);
  DVLOG(1) << "Finish result=" << ErrorToString(result);

  metrics_->OnClose();

  if (result != ERR_CONNECTION_CLOSED && delegate_)
    delegate_->OnError(this, result);
  if (result != ERR_PROTOCOL_SWITCHED && delegate_)
    delegate_->OnClose(this);
  delegate_ = NULL;

  Release();
}

int SocketStream::DidEstablishConnection() {
  if (!connection_->socket() || !connection_->socket()->IsConnected()) {
    next_state_ = STATE_CLOSE;
    return ERR_CONNECTION_FAILED;
  }
  next_state_ = STATE_READ_WRITE;
  metrics_->OnConnected();

  net_log_.EndEvent(NetLog::TYPE_SOCKET_STREAM_CONNECT);
  if (delegate_)
    delegate_->OnConnected(this, max_pending_send_allowed_);

  return OK;
}

int SocketStream::DidReceiveData(int result) {
  DCHECK(read_buf_.get());
  DCHECK_GT(result, 0);
  net_log_.AddEvent(NetLog::TYPE_SOCKET_STREAM_RECEIVED);
  int len = result;
  metrics_->OnRead(len);
  if (delegate_) {
    // Notify recevied data to delegate.
    delegate_->OnReceivedData(this, read_buf_->data(), len);
  }
  read_buf_ = NULL;
  return OK;
}

void SocketStream::DidSendData(int result) {
  DCHECK_GT(result, 0);
  DCHECK(current_write_buf_.get());
  net_log_.AddEvent(NetLog::TYPE_SOCKET_STREAM_SENT);

  int bytes_sent = result;

  metrics_->OnWrite(bytes_sent);

  current_write_buf_->DidConsume(result);

  if (current_write_buf_->BytesRemaining())
    return;

  size_t bytes_freed = current_write_buf_->size();

  current_write_buf_ = NULL;

  // We freed current_write_buf_ and this instance is now able to accept more
  // data via SendData() (note that DidConsume() doesn't free consumed memory).
  // We can tell that to delegate_ by calling OnSentData().
  if (delegate_)
    delegate_->OnSentData(this, bytes_freed);
}

void SocketStream::OnIOCompleted(int result) {
  DoLoop(result);
}

void SocketStream::OnReadCompleted(int result) {
  if (result == 0) {
    // 0 indicates end-of-file, so socket was closed.
    // Don't close the socket if it's still writing.
    server_closed_ = true;
  } else if (result > 0 && read_buf_.get()) {
    result = DidReceiveData(result);
  }
  DoLoop(result);
}

void SocketStream::OnWriteCompleted(int result) {
  waiting_for_write_completion_ = false;
  if (result > 0) {
    DidSendData(result);
    result = OK;
  }
  DoLoop(result);
}

void SocketStream::DoLoop(int result) {
  if (next_state_ == STATE_NONE)
    return;

  // If context was not set, close immediately.
  if (!context_)
    next_state_ = STATE_CLOSE;

  do {
    State state = next_state_;
    next_state_ = STATE_NONE;
    switch (state) {
      case STATE_BEFORE_CONNECT:
        DCHECK_EQ(OK, result);
        result = DoBeforeConnect();
        break;
      case STATE_BEFORE_CONNECT_COMPLETE:
        result = DoBeforeConnectComplete(result);
        break;
      case STATE_RESOLVE_PROXY:
        DCHECK_EQ(OK, result);
        result = DoResolveProxy();
        break;
      case STATE_RESOLVE_PROXY_COMPLETE:
        result = DoResolveProxyComplete(result);
        break;
      case STATE_RESOLVE_HOST:
        DCHECK_EQ(OK, result);
        result = DoResolveHost();
        break;
      case STATE_RESOLVE_HOST_COMPLETE:
        result = DoResolveHostComplete(result);
        break;
      case STATE_RESOLVE_PROTOCOL:
        result = DoResolveProtocol(result);
        break;
      case STATE_RESOLVE_PROTOCOL_COMPLETE:
        result = DoResolveProtocolComplete(result);
        break;
      case STATE_TCP_CONNECT:
        result = DoTcpConnect(result);
        break;
      case STATE_TCP_CONNECT_COMPLETE:
        result = DoTcpConnectComplete(result);
        break;
      case STATE_GENERATE_PROXY_AUTH_TOKEN:
        result = DoGenerateProxyAuthToken();
        break;
      case STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE:
        result = DoGenerateProxyAuthTokenComplete(result);
        break;
      case STATE_WRITE_TUNNEL_HEADERS:
        DCHECK_EQ(OK, result);
        result = DoWriteTunnelHeaders();
        break;
      case STATE_WRITE_TUNNEL_HEADERS_COMPLETE:
        result = DoWriteTunnelHeadersComplete(result);
        break;
      case STATE_READ_TUNNEL_HEADERS:
        DCHECK_EQ(OK, result);
        result = DoReadTunnelHeaders();
        break;
      case STATE_READ_TUNNEL_HEADERS_COMPLETE:
        result = DoReadTunnelHeadersComplete(result);
        break;
      case STATE_SOCKS_CONNECT:
        DCHECK_EQ(OK, result);
        result = DoSOCKSConnect();
        break;
      case STATE_SOCKS_CONNECT_COMPLETE:
        result = DoSOCKSConnectComplete(result);
        break;
      case STATE_SECURE_PROXY_CONNECT:
        DCHECK_EQ(OK, result);
        result = DoSecureProxyConnect();
        break;
      case STATE_SECURE_PROXY_CONNECT_COMPLETE:
        result = DoSecureProxyConnectComplete(result);
        break;
      case STATE_SECURE_PROXY_HANDLE_CERT_ERROR:
        result = DoSecureProxyHandleCertError(result);
        break;
      case STATE_SECURE_PROXY_HANDLE_CERT_ERROR_COMPLETE:
        result = DoSecureProxyHandleCertErrorComplete(result);
        break;
      case STATE_SSL_CONNECT:
        DCHECK_EQ(OK, result);
        result = DoSSLConnect();
        break;
      case STATE_SSL_CONNECT_COMPLETE:
        result = DoSSLConnectComplete(result);
        break;
      case STATE_SSL_HANDLE_CERT_ERROR:
        result = DoSSLHandleCertError(result);
        break;
      case STATE_SSL_HANDLE_CERT_ERROR_COMPLETE:
        result = DoSSLHandleCertErrorComplete(result);
        break;
      case STATE_READ_WRITE:
        result = DoReadWrite(result);
        break;
      case STATE_AUTH_REQUIRED:
        // It might be called when DoClose is called while waiting in
        // STATE_AUTH_REQUIRED.
        Finish(result);
        return;
      case STATE_CLOSE:
        DCHECK_LE(result, OK);
        Finish(result);
        return;
      default:
        NOTREACHED() << "bad state " << state;
        Finish(result);
        return;
    }
    if (state == STATE_RESOLVE_PROTOCOL && result == ERR_PROTOCOL_SWITCHED)
      continue;
    // If the connection is not established yet and had actual errors,
    // record the error.  In next iteration, it will close the connection.
    if (state != STATE_READ_WRITE && result < ERR_IO_PENDING) {
      net_log_.EndEventWithNetErrorCode(
          NetLog::TYPE_SOCKET_STREAM_CONNECT, result);
    }
  } while (result != ERR_IO_PENDING);
}

int SocketStream::DoBeforeConnect() {
  next_state_ = STATE_BEFORE_CONNECT_COMPLETE;
  if (!context_ || !context_->network_delegate())
    return OK;

  int result = context_->network_delegate()->NotifyBeforeSocketStreamConnect(
      this, io_callback_);
  if (result != OK && result != ERR_IO_PENDING)
    next_state_ = STATE_CLOSE;

  return result;
}

int SocketStream::DoBeforeConnectComplete(int result) {
  DCHECK_NE(ERR_IO_PENDING, result);

  if (result == OK)
    next_state_ = STATE_RESOLVE_PROXY;
  else
    next_state_ = STATE_CLOSE;

  return result;
}

int SocketStream::DoResolveProxy() {
  DCHECK(context_);
  DCHECK(!pac_request_);
  next_state_ = STATE_RESOLVE_PROXY_COMPLETE;

  if (!proxy_url_.is_valid()) {
    next_state_ = STATE_CLOSE;
    return ERR_INVALID_ARGUMENT;
  }

  // TODO(toyoshim): Check server advertisement of SPDY through the HTTP
  // Alternate-Protocol header, then switch to SPDY if SPDY is available.
  // Usually we already have a session to the SPDY server because JavaScript
  // running WebSocket itself would be served by SPDY. But, in some situation
  // (E.g. Used by Chrome Extensions or used for cross origin connection), this
  // connection might be the first one. At that time, we should check
  // Alternate-Protocol header here for ws:// or TLS NPN extension for wss:// .

  return context_->proxy_service()->ResolveProxy(
      proxy_url_, net::LOAD_NORMAL, &proxy_info_, io_callback_, &pac_request_,
      NULL, net_log_);
}

int SocketStream::DoResolveProxyComplete(int result) {
  pac_request_ = NULL;
  if (result != OK) {
    DVLOG(1) << "Failed to resolve proxy: " << result;
    if (delegate_)
      delegate_->OnError(this, result);
    proxy_info_.UseDirect();
  }
  if (proxy_info_.is_direct()) {
    // If proxy was not found for original URL (i.e. websocket URL),
    // try again with https URL, like Safari implementation.
    // Note that we don't want to use http proxy, because we'll use tunnel
    // proxy using CONNECT method, which is used by https proxy.
    if (!proxy_url_.SchemeIs("https")) {
      const std::string scheme = "https";
      GURL::Replacements repl;
      repl.SetSchemeStr(scheme);
      proxy_url_ = url_.ReplaceComponents(repl);
      DVLOG(1) << "Try https proxy: " << proxy_url_;
      next_state_ = STATE_RESOLVE_PROXY;
      return OK;
    }
  }

  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 ERR_NO_SUPPORTED_PROXIES;
  }

  next_state_ = STATE_RESOLVE_HOST;
  return OK;
}

int SocketStream::DoResolveHost() {
  next_state_ = STATE_RESOLVE_HOST_COMPLETE;

  DCHECK(!proxy_info_.is_empty());
  if (proxy_info_.is_direct())
    proxy_mode_ = kDirectConnection;
  else if (proxy_info_.proxy_server().is_socks())
    proxy_mode_ = kSOCKSProxy;
  else
    proxy_mode_ = kTunnelProxy;

  // Determine the host and port to connect to.
  HostPortPair host_port_pair;
  if (proxy_mode_ != kDirectConnection) {
    host_port_pair = proxy_info_.proxy_server().host_port_pair();
  } else {
    host_port_pair = HostPortPair::FromURL(url_);
  }

  HostResolver::RequestInfo resolve_info(host_port_pair);

  DCHECK(context_->host_resolver());
  resolver_.reset(new SingleRequestHostResolver(context_->host_resolver()));
  return resolver_->Resolve(resolve_info,
                            DEFAULT_PRIORITY,
                            &addresses_,
                            base::Bind(&SocketStream::OnIOCompleted, this),
                            net_log_);
}

int SocketStream::DoResolveHostComplete(int result) {
  if (result == OK)
    next_state_ = STATE_RESOLVE_PROTOCOL;
  else
    next_state_ = STATE_CLOSE;
  // TODO(ukai): if error occured, reconsider proxy after error.
  return result;
}

int SocketStream::DoResolveProtocol(int result) {
  DCHECK_EQ(OK, result);

  if (!delegate_) {
    next_state_ = STATE_CLOSE;
    return result;
  }

  next_state_ = STATE_RESOLVE_PROTOCOL_COMPLETE;
  result = delegate_->OnStartOpenConnection(this, io_callback_);
  if (result == ERR_IO_PENDING)
    metrics_->OnWaitConnection();
  else if (result != OK && result != ERR_PROTOCOL_SWITCHED)
    next_state_ = STATE_CLOSE;
  return result;
}

int SocketStream::DoResolveProtocolComplete(int result) {
  DCHECK_NE(ERR_IO_PENDING, result);

  if (result == ERR_PROTOCOL_SWITCHED) {
    next_state_ = STATE_CLOSE;
    metrics_->OnCountWireProtocolType(
        SocketStreamMetrics::WIRE_PROTOCOL_SPDY);
  } else if (result == OK) {
    next_state_ = STATE_TCP_CONNECT;
    metrics_->OnCountWireProtocolType(
        SocketStreamMetrics::WIRE_PROTOCOL_WEBSOCKET);
  } else {
    next_state_ = STATE_CLOSE;
  }
  return result;
}

int SocketStream::DoTcpConnect(int result) {
  if (result != OK) {
    next_state_ = STATE_CLOSE;
    return result;
  }
  next_state_ = STATE_TCP_CONNECT_COMPLETE;
  DCHECK(factory_);
  connection_->SetSocket(
      factory_->CreateTransportClientSocket(addresses_,
                                            net_log_.net_log(),
                                            net_log_.source()));
  metrics_->OnStartConnection();
  return connection_->socket()->Connect(io_callback_);
}

int SocketStream::DoTcpConnectComplete(int result) {
  // TODO(ukai): if error occured, reconsider proxy after error.
  if (result != OK) {
    next_state_ = STATE_CLOSE;
    return result;
  }

  if (proxy_mode_ == kTunnelProxy) {
    if (proxy_info_.is_https())
      next_state_ = STATE_SECURE_PROXY_CONNECT;
    else
      next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN;
  } else if (proxy_mode_ == kSOCKSProxy) {
    next_state_ = STATE_SOCKS_CONNECT;
  } else if (is_secure()) {
    next_state_ = STATE_SSL_CONNECT;
  } else {
    result = DidEstablishConnection();
  }
  return result;
}

int SocketStream::DoGenerateProxyAuthToken() {
  next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE;
  if (!proxy_auth_controller_.get()) {
    DCHECK(context_);
    DCHECK(context_->http_transaction_factory());
    DCHECK(context_->http_transaction_factory()->GetSession());
    HttpNetworkSession* session =
        context_->http_transaction_factory()->GetSession();
    const char* scheme = proxy_info_.is_https() ? "https://" : "http://";
    GURL auth_url(scheme +
                  proxy_info_.proxy_server().host_port_pair().ToString());
    proxy_auth_controller_ =
        new HttpAuthController(HttpAuth::AUTH_PROXY,
                               auth_url,
                               session->http_auth_cache(),
                               session->http_auth_handler_factory());
  }
  HttpRequestInfo request_info;
  request_info.url = url_;
  request_info.method = "CONNECT";
  return proxy_auth_controller_->MaybeGenerateAuthToken(
      &request_info, io_callback_, net_log_);
}

int SocketStream::DoGenerateProxyAuthTokenComplete(int result) {
  if (result != OK) {
    next_state_ = STATE_CLOSE;
    return result;
  }

  next_state_ = STATE_WRITE_TUNNEL_HEADERS;
  return result;
}

int SocketStream::DoWriteTunnelHeaders() {
  DCHECK_EQ(kTunnelProxy, proxy_mode_);

  next_state_ = STATE_WRITE_TUNNEL_HEADERS_COMPLETE;

  if (!tunnel_request_headers_.get()) {
    metrics_->OnCountConnectionType(SocketStreamMetrics::TUNNEL_CONNECTION);
    tunnel_request_headers_ = new RequestHeaders();
    tunnel_request_headers_bytes_sent_ = 0;
  }
  if (tunnel_request_headers_->headers_.empty()) {
    HttpRequestHeaders request_headers;
    request_headers.SetHeader("Host", GetHostAndOptionalPort(url_));
    request_headers.SetHeader("Proxy-Connection", "keep-alive");
    if (proxy_auth_controller_.get() && proxy_auth_controller_->HaveAuth())
      proxy_auth_controller_->AddAuthorizationHeader(&request_headers);
    tunnel_request_headers_->headers_ = base::StringPrintf(
        "CONNECT %s HTTP/1.1\r\n"
        "%s",
        GetHostAndPort(url_).c_str(),
        request_headers.ToString().c_str());
  }
  tunnel_request_headers_->SetDataOffset(tunnel_request_headers_bytes_sent_);
  int buf_len = static_cast<int>(tunnel_request_headers_->headers_.size() -
                                 tunnel_request_headers_bytes_sent_);
  DCHECK_GT(buf_len, 0);
  return connection_->socket()->Write(
      tunnel_request_headers_.get(), buf_len, io_callback_);
}

int SocketStream::DoWriteTunnelHeadersComplete(int result) {
  DCHECK_EQ(kTunnelProxy, proxy_mode_);

  if (result < 0) {
    next_state_ = STATE_CLOSE;
    return result;
  }

  tunnel_request_headers_bytes_sent_ += result;
  if (tunnel_request_headers_bytes_sent_ <
      tunnel_request_headers_->headers_.size()) {
    next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN;
  } else {
    // Handling a cert error or a client cert request requires reconnection.
    // DoWriteTunnelHeaders() will be called again.
    // Thus |tunnel_request_headers_bytes_sent_| should be reset to 0 for
    // sending |tunnel_request_headers_| correctly.
    tunnel_request_headers_bytes_sent_ = 0;
    next_state_ = STATE_READ_TUNNEL_HEADERS;
  }
  return OK;
}

int SocketStream::DoReadTunnelHeaders() {
  DCHECK_EQ(kTunnelProxy, proxy_mode_);

  next_state_ = STATE_READ_TUNNEL_HEADERS_COMPLETE;

  if (!tunnel_response_headers_.get()) {
    tunnel_response_headers_ = new ResponseHeaders();
    tunnel_response_headers_capacity_ = kMaxTunnelResponseHeadersSize;
    tunnel_response_headers_->Realloc(tunnel_response_headers_capacity_);
    tunnel_response_headers_len_ = 0;
  }

  int buf_len = tunnel_response_headers_capacity_ -
      tunnel_response_headers_len_;
  tunnel_response_headers_->SetDataOffset(tunnel_response_headers_len_);
  CHECK(tunnel_response_headers_->data());

  return connection_->socket()->Read(
      tunnel_response_headers_.get(), buf_len, io_callback_);
}

int SocketStream::DoReadTunnelHeadersComplete(int result) {
  DCHECK_EQ(kTunnelProxy, proxy_mode_);

  if (result < 0) {
    next_state_ = STATE_CLOSE;
    return result;
  }

  if (result == 0) {
    // 0 indicates end-of-file, so socket was closed.
    next_state_ = STATE_CLOSE;
    return ERR_CONNECTION_CLOSED;
  }

  tunnel_response_headers_len_ += result;
  DCHECK(tunnel_response_headers_len_ <= tunnel_response_headers_capacity_);

  int eoh = HttpUtil::LocateEndOfHeaders(
      tunnel_response_headers_->headers(), tunnel_response_headers_len_, 0);
  if (eoh == -1) {
    if (tunnel_response_headers_len_ >= kMaxTunnelResponseHeadersSize) {
      next_state_ = STATE_CLOSE;
      return ERR_RESPONSE_HEADERS_TOO_BIG;
    }

    next_state_ = STATE_READ_TUNNEL_HEADERS;
    return OK;
  }
  // DidReadResponseHeaders
  scoped_refptr<HttpResponseHeaders> headers;
  headers = new HttpResponseHeaders(
      HttpUtil::AssembleRawHeaders(tunnel_response_headers_->headers(), eoh));
  if (headers->GetParsedHttpVersion() < HttpVersion(1, 0)) {
    // Require the "HTTP/1.x" status line.
    next_state_ = STATE_CLOSE;
    return ERR_TUNNEL_CONNECTION_FAILED;
  }
  switch (headers->response_code()) {
    case 200:  // OK
      if (is_secure()) {
        DCHECK_EQ(eoh, tunnel_response_headers_len_);
        next_state_ = STATE_SSL_CONNECT;
      } else {
        result = DidEstablishConnection();
        if (result < 0) {
          next_state_ = STATE_CLOSE;
          return result;
        }
        if ((eoh < tunnel_response_headers_len_) && delegate_)
          delegate_->OnReceivedData(
              this, tunnel_response_headers_->headers() + eoh,
              tunnel_response_headers_len_ - eoh);
      }
      return OK;
    case 407:  // Proxy Authentication Required.
      if (proxy_mode_ != kTunnelProxy)
        return ERR_UNEXPECTED_PROXY_AUTH;

      result = proxy_auth_controller_->HandleAuthChallenge(
          headers, false, true, net_log_);
      if (result != OK)
        return result;
      DCHECK(!proxy_info_.is_empty());
      next_state_ = STATE_AUTH_REQUIRED;
      if (proxy_auth_controller_->HaveAuth()) {
        base::MessageLoop::current()->PostTask(
            FROM_HERE, base::Bind(&SocketStream::DoRestartWithAuth, this));
        return ERR_IO_PENDING;
      }
      if (delegate_) {
        // Wait until RestartWithAuth or Close is called.
        base::MessageLoop::current()->PostTask(
            FROM_HERE, base::Bind(&SocketStream::DoAuthRequired, this));
        return ERR_IO_PENDING;
      }
      break;
    default:
      break;
  }
  next_state_ = STATE_CLOSE;
  return ERR_TUNNEL_CONNECTION_FAILED;
}

int SocketStream::DoSOCKSConnect() {
  DCHECK_EQ(kSOCKSProxy, proxy_mode_);

  next_state_ = STATE_SOCKS_CONNECT_COMPLETE;

  HostResolver::RequestInfo req_info(HostPortPair::FromURL(url_));

  DCHECK(!proxy_info_.is_empty());
  scoped_ptr<StreamSocket> s;
  if (proxy_info_.proxy_server().scheme() == ProxyServer::SCHEME_SOCKS5) {
    s.reset(new SOCKS5ClientSocket(connection_.Pass(), req_info));
  } else {
    s.reset(new SOCKSClientSocket(connection_.Pass(),
                                  req_info,
                                  DEFAULT_PRIORITY,
                                  context_->host_resolver()));
  }
  connection_.reset(new ClientSocketHandle);
  connection_->SetSocket(s.Pass());
  metrics_->OnCountConnectionType(SocketStreamMetrics::SOCKS_CONNECTION);
  return connection_->socket()->Connect(io_callback_);
}

int SocketStream::DoSOCKSConnectComplete(int result) {
  DCHECK_EQ(kSOCKSProxy, proxy_mode_);

  if (result == OK) {
    if (is_secure())
      next_state_ = STATE_SSL_CONNECT;
    else
      result = DidEstablishConnection();
  } else {
    next_state_ = STATE_CLOSE;
  }
  return result;
}

int SocketStream::DoSecureProxyConnect() {
  DCHECK(factory_);
  SSLClientSocketContext ssl_context;
  ssl_context.cert_verifier = context_->cert_verifier();
  ssl_context.transport_security_state = context_->transport_security_state();
  ssl_context.channel_id_service = context_->channel_id_service();
  scoped_ptr<StreamSocket> socket(factory_->CreateSSLClientSocket(
      connection_.Pass(),
      proxy_info_.proxy_server().host_port_pair(),
      proxy_ssl_config_,
      ssl_context));
  connection_.reset(new ClientSocketHandle);
  connection_->SetSocket(socket.Pass());
  next_state_ = STATE_SECURE_PROXY_CONNECT_COMPLETE;
  metrics_->OnCountConnectionType(SocketStreamMetrics::SECURE_PROXY_CONNECTION);
  return connection_->socket()->Connect(io_callback_);
}

int SocketStream::DoSecureProxyConnectComplete(int result) {
  DCHECK_EQ(STATE_NONE, next_state_);
  // Reconnect with client authentication.
  if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED)
    return HandleCertificateRequest(result, &proxy_ssl_config_);

  if (IsCertificateError(result))
    next_state_ = STATE_SECURE_PROXY_HANDLE_CERT_ERROR;
  else if (result == OK)
    next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN;
  else
    next_state_ = STATE_CLOSE;
  return result;
}

int SocketStream::DoSecureProxyHandleCertError(int result) {
  DCHECK_EQ(STATE_NONE, next_state_);
  DCHECK(IsCertificateError(result));
  result = HandleCertificateError(result);
  if (result == ERR_IO_PENDING)
    next_state_ = STATE_SECURE_PROXY_HANDLE_CERT_ERROR_COMPLETE;
  else
    next_state_ = STATE_CLOSE;
  return result;
}

int SocketStream::DoSecureProxyHandleCertErrorComplete(int result) {
  DCHECK_EQ(STATE_NONE, next_state_);
  if (result == OK) {
    if (!connection_->socket()->IsConnectedAndIdle())
      return AllowCertErrorForReconnection(&proxy_ssl_config_);
    next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN;
  } else {
    next_state_ = STATE_CLOSE;
  }
  return result;
}

int SocketStream::DoSSLConnect() {
  DCHECK(factory_);
  SSLClientSocketContext ssl_context;
  ssl_context.cert_verifier = context_->cert_verifier();
  ssl_context.transport_security_state = context_->transport_security_state();
  ssl_context.channel_id_service = context_->channel_id_service();
  scoped_ptr<StreamSocket> socket(
      factory_->CreateSSLClientSocket(connection_.Pass(),
                                      HostPortPair::FromURL(url_),
                                      server_ssl_config_,
                                      ssl_context));
  connection_.reset(new ClientSocketHandle);
  connection_->SetSocket(socket.Pass());
  next_state_ = STATE_SSL_CONNECT_COMPLETE;
  metrics_->OnCountConnectionType(SocketStreamMetrics::SSL_CONNECTION);
  return connection_->socket()->Connect(io_callback_);
}

int SocketStream::DoSSLConnectComplete(int result) {
  DCHECK_EQ(STATE_NONE, next_state_);
  // Reconnect with client authentication.
  if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED)
    return HandleCertificateRequest(result, &server_ssl_config_);

  if (IsCertificateError(result))
    next_state_ = STATE_SSL_HANDLE_CERT_ERROR;
  else if (result == OK)
    result = DidEstablishConnection();
  else
    next_state_ = STATE_CLOSE;
  return result;
}

int SocketStream::DoSSLHandleCertError(int result) {
  DCHECK_EQ(STATE_NONE, next_state_);
  DCHECK(IsCertificateError(result));
  result = HandleCertificateError(result);
  if (result == OK || result == ERR_IO_PENDING)
    next_state_ = STATE_SSL_HANDLE_CERT_ERROR_COMPLETE;
  else
    next_state_ = STATE_CLOSE;
  return result;
}

int SocketStream::DoSSLHandleCertErrorComplete(int result) {
  DCHECK_EQ(STATE_NONE, next_state_);
  // TODO(toyoshim): Upgrade to SPDY through TLS NPN extension if possible.
  // If we use HTTPS and this is the first connection to the SPDY server,
  // we should take care of TLS NPN extension here.

  if (result == OK) {
    if (!connection_->socket()->IsConnectedAndIdle())
      return AllowCertErrorForReconnection(&server_ssl_config_);
    result = DidEstablishConnection();
  } else {
    next_state_ = STATE_CLOSE;
  }
  return result;
}

int SocketStream::DoReadWrite(int result) {
  if (result < OK) {
    next_state_ = STATE_CLOSE;
    return result;
  }
  if (!connection_->socket() || !connection_->socket()->IsConnected()) {
    next_state_ = STATE_CLOSE;
    return ERR_CONNECTION_CLOSED;
  }

  // If client has requested close(), and there's nothing to write, then
  // let's close the socket.
  // We don't care about receiving data after the socket is closed.
  if (closing_ && !current_write_buf_.get() && pending_write_bufs_.empty()) {
    connection_->socket()->Disconnect();
    next_state_ = STATE_CLOSE;
    return OK;
  }

  next_state_ = STATE_READ_WRITE;

  // If server already closed the socket, we don't try to read.
  if (!server_closed_) {
    if (!read_buf_.get()) {
      // No read pending and server didn't close the socket.
      read_buf_ = new IOBuffer(kReadBufferSize);
      result = connection_->socket()->Read(
          read_buf_.get(),
          kReadBufferSize,
          base::Bind(&SocketStream::OnReadCompleted, base::Unretained(this)));
      if (result > 0) {
        return DidReceiveData(result);
      } else if (result == 0) {
        // 0 indicates end-of-file, so socket was closed.
        next_state_ = STATE_CLOSE;
        server_closed_ = true;
        return ERR_CONNECTION_CLOSED;
      }
      // If read is pending, try write as well.
      // Otherwise, return the result and do next loop (to close the
      // connection).
      if (result != ERR_IO_PENDING) {
        next_state_ = STATE_CLOSE;
        server_closed_ = true;
        return result;
      }
    }
    // Read is pending.
    DCHECK(read_buf_.get());
  }

  if (waiting_for_write_completion_)
    return ERR_IO_PENDING;

  if (!current_write_buf_.get()) {
    if (pending_write_bufs_.empty()) {
      // Nothing buffered for send.
      return ERR_IO_PENDING;
    }

    current_write_buf_ = new DrainableIOBuffer(
        pending_write_bufs_.front().get(), pending_write_bufs_.front()->size());
    pending_write_bufs_.pop_front();
  }

  result = connection_->socket()->Write(
      current_write_buf_.get(),
      current_write_buf_->BytesRemaining(),
      base::Bind(&SocketStream::OnWriteCompleted, base::Unretained(this)));

  if (result == ERR_IO_PENDING) {
    waiting_for_write_completion_ = true;
  } else if (result < 0) {
    // Shortcut. Enter STATE_CLOSE now by changing next_state_ here than by
    // calling DoReadWrite() again with the error code.
    next_state_ = STATE_CLOSE;
  } else if (result > 0) {
    // Write is not pending. Return OK and do next loop.
    DidSendData(result);
    result = OK;
  }

  return result;
}

GURL SocketStream::ProxyAuthOrigin() const {
  DCHECK(!proxy_info_.is_empty());
  return GURL("http://" +
              proxy_info_.proxy_server().host_port_pair().ToString());
}

int SocketStream::HandleCertificateRequest(int result, SSLConfig* ssl_config) {
  if (ssl_config->send_client_cert) {
    // We already have performed SSL client authentication once and failed.
    return result;
  }

  DCHECK(connection_->socket());
  scoped_refptr<SSLCertRequestInfo> cert_request_info = new SSLCertRequestInfo;
  SSLClientSocket* ssl_socket =
      static_cast<SSLClientSocket*>(connection_->socket());
  ssl_socket->GetSSLCertRequestInfo(cert_request_info.get());

  HttpTransactionFactory* factory = context_->http_transaction_factory();
  if (!factory)
    return result;
  scoped_refptr<HttpNetworkSession> session = factory->GetSession();
  if (!session.get())
    return result;

  // If the user selected one of the certificates in client_certs or declined
  // to provide one for this server before, use the past decision
  // automatically.
  scoped_refptr<X509Certificate> client_cert;
  if (!session->ssl_client_auth_cache()->Lookup(
          cert_request_info->host_and_port, &client_cert)) {
    return result;
  }

  // Note: |client_cert| may be NULL, indicating that the caller
  // wishes to proceed anonymously (eg: continue the handshake
  // without sending a client cert)
  //
  // Check that the certificate selected is still a certificate the server
  // is likely to accept, based on the criteria supplied in the
  // CertificateRequest message.
  const std::vector<std::string>& cert_authorities =
      cert_request_info->cert_authorities;
  if (client_cert.get() && !cert_authorities.empty() &&
      !client_cert->IsIssuedByEncoded(cert_authorities)) {
    return result;
  }

  ssl_config->send_client_cert = true;
  ssl_config->client_cert = client_cert;
  next_state_ = STATE_TCP_CONNECT;
  return OK;
}

int SocketStream::AllowCertErrorForReconnection(SSLConfig* ssl_config) {
  DCHECK(ssl_config);
  // The SSL handshake didn't finish, or the server closed the SSL connection.
  // So, we should restart establishing connection with the certificate in
  // allowed bad certificates in |ssl_config|.
  // See also net/http/http_network_transaction.cc HandleCertificateError() and
  // RestartIgnoringLastError().
  SSLClientSocket* ssl_socket =
      static_cast<SSLClientSocket*>(connection_->socket());
  SSLInfo ssl_info;
  ssl_socket->GetSSLInfo(&ssl_info);
  if (ssl_info.cert.get() == NULL ||
      ssl_config->IsAllowedBadCert(ssl_info.cert.get(), NULL)) {
    // If we already have the certificate in the set of allowed bad
    // certificates, we did try it and failed again, so we should not
    // retry again: the connection should fail at last.
    next_state_ = STATE_CLOSE;
    return ERR_UNEXPECTED;
  }
  // Add the bad certificate to the set of allowed certificates in the
  // SSL config object.
  SSLConfig::CertAndStatus bad_cert;
  if (!X509Certificate::GetDEREncoded(ssl_info.cert->os_cert_handle(),
                                      &bad_cert.der_cert)) {
    next_state_ = STATE_CLOSE;
    return ERR_UNEXPECTED;
  }
  bad_cert.cert_status = ssl_info.cert_status;
  ssl_config->allowed_bad_certs.push_back(bad_cert);
  // Restart connection ignoring the bad certificate.
  connection_->socket()->Disconnect();
  connection_->SetSocket(scoped_ptr<StreamSocket>());
  next_state_ = STATE_TCP_CONNECT;
  return OK;
}

void SocketStream::DoAuthRequired() {
  if (delegate_ && proxy_auth_controller_.get())
    delegate_->OnAuthRequired(this, proxy_auth_controller_->auth_info().get());
  else
    DoLoop(ERR_UNEXPECTED);
}

void SocketStream::DoRestartWithAuth() {
  DCHECK_EQ(next_state_, STATE_AUTH_REQUIRED);
  tunnel_request_headers_ = NULL;
  tunnel_request_headers_bytes_sent_ = 0;
  tunnel_response_headers_ = NULL;
  tunnel_response_headers_capacity_ = 0;
  tunnel_response_headers_len_ = 0;

  next_state_ = STATE_TCP_CONNECT;
  DoLoop(OK);
}

int SocketStream::HandleCertificateError(int result) {
  DCHECK(IsCertificateError(result));
  SSLClientSocket* ssl_socket =
      static_cast<SSLClientSocket*>(connection_->socket());
  DCHECK(ssl_socket);

  if (!context_)
    return result;

  if (SSLClientSocket::IgnoreCertError(result, LOAD_IGNORE_ALL_CERT_ERRORS)) {
    const HttpNetworkSession::Params* session_params =
        context_->GetNetworkSessionParams();
    if (session_params && session_params->ignore_certificate_errors)
      return OK;
  }

  if (!delegate_)
    return result;

  SSLInfo ssl_info;
  ssl_socket->GetSSLInfo(&ssl_info);

  TransportSecurityState* state = context_->transport_security_state();
  const bool fatal = state && state->ShouldSSLErrorsBeFatal(url_.host());

  delegate_->OnSSLCertificateError(this, ssl_info, fatal);
  return ERR_IO_PENDING;
}

CookieStore* SocketStream::cookie_store() const {
  return cookie_store_.get();
}

}  // namespace net