Home | History | Annotate | Download | only in socket
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "net/socket/websocket_transport_connect_sub_job.h"
      6 
      7 #include "base/logging.h"
      8 #include "net/base/ip_endpoint.h"
      9 #include "net/base/net_errors.h"
     10 #include "net/base/net_log.h"
     11 #include "net/socket/client_socket_factory.h"
     12 #include "net/socket/websocket_endpoint_lock_manager.h"
     13 
     14 namespace net {
     15 
     16 WebSocketTransportConnectSubJob::WebSocketTransportConnectSubJob(
     17     const AddressList& addresses,
     18     WebSocketTransportConnectJob* parent_job,
     19     SubJobType type)
     20     : parent_job_(parent_job),
     21       addresses_(addresses),
     22       current_address_index_(0),
     23       next_state_(STATE_NONE),
     24       type_(type) {}
     25 
     26 WebSocketTransportConnectSubJob::~WebSocketTransportConnectSubJob() {
     27   // We don't worry about cancelling the TCP connect, since ~StreamSocket will
     28   // take care of it.
     29   if (next()) {
     30     DCHECK_EQ(STATE_OBTAIN_LOCK_COMPLETE, next_state_);
     31     // The ~Waiter destructor will remove this object from the waiting list.
     32   } else if (next_state_ == STATE_TRANSPORT_CONNECT_COMPLETE) {
     33     WebSocketEndpointLockManager::GetInstance()->UnlockEndpoint(
     34         CurrentAddress());
     35   }
     36 }
     37 
     38 // Start connecting.
     39 int WebSocketTransportConnectSubJob::Start() {
     40   DCHECK_EQ(STATE_NONE, next_state_);
     41   next_state_ = STATE_OBTAIN_LOCK;
     42   return DoLoop(OK);
     43 }
     44 
     45 // Called by WebSocketEndpointLockManager when the lock becomes available.
     46 void WebSocketTransportConnectSubJob::GotEndpointLock() {
     47   DCHECK_EQ(STATE_OBTAIN_LOCK_COMPLETE, next_state_);
     48   OnIOComplete(OK);
     49 }
     50 
     51 LoadState WebSocketTransportConnectSubJob::GetLoadState() const {
     52   switch (next_state_) {
     53     case STATE_OBTAIN_LOCK:
     54     case STATE_OBTAIN_LOCK_COMPLETE:
     55       // TODO(ricea): Add a WebSocket-specific LOAD_STATE ?
     56       return LOAD_STATE_WAITING_FOR_AVAILABLE_SOCKET;
     57     case STATE_TRANSPORT_CONNECT:
     58     case STATE_TRANSPORT_CONNECT_COMPLETE:
     59     case STATE_DONE:
     60       return LOAD_STATE_CONNECTING;
     61     case STATE_NONE:
     62       return LOAD_STATE_IDLE;
     63   }
     64   NOTREACHED();
     65   return LOAD_STATE_IDLE;
     66 }
     67 
     68 ClientSocketFactory* WebSocketTransportConnectSubJob::client_socket_factory()
     69     const {
     70   return parent_job_->helper_.client_socket_factory();
     71 }
     72 
     73 const BoundNetLog& WebSocketTransportConnectSubJob::net_log() const {
     74   return parent_job_->net_log();
     75 }
     76 
     77 const IPEndPoint& WebSocketTransportConnectSubJob::CurrentAddress() const {
     78   DCHECK_LT(current_address_index_, addresses_.size());
     79   return addresses_[current_address_index_];
     80 }
     81 
     82 void WebSocketTransportConnectSubJob::OnIOComplete(int result) {
     83   int rv = DoLoop(result);
     84   if (rv != ERR_IO_PENDING)
     85     parent_job_->OnSubJobComplete(rv, this);  // |this| deleted
     86 }
     87 
     88 int WebSocketTransportConnectSubJob::DoLoop(int result) {
     89   DCHECK_NE(next_state_, STATE_NONE);
     90 
     91   int rv = result;
     92   do {
     93     State state = next_state_;
     94     next_state_ = STATE_NONE;
     95     switch (state) {
     96       case STATE_OBTAIN_LOCK:
     97         DCHECK_EQ(OK, rv);
     98         rv = DoEndpointLock();
     99         break;
    100       case STATE_OBTAIN_LOCK_COMPLETE:
    101         DCHECK_EQ(OK, rv);
    102         rv = DoEndpointLockComplete();
    103         break;
    104       case STATE_TRANSPORT_CONNECT:
    105         DCHECK_EQ(OK, rv);
    106         rv = DoTransportConnect();
    107         break;
    108       case STATE_TRANSPORT_CONNECT_COMPLETE:
    109         rv = DoTransportConnectComplete(rv);
    110         break;
    111       default:
    112         NOTREACHED();
    113         rv = ERR_FAILED;
    114         break;
    115     }
    116   } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE &&
    117            next_state_ != STATE_DONE);
    118 
    119   return rv;
    120 }
    121 
    122 int WebSocketTransportConnectSubJob::DoEndpointLock() {
    123   int rv = WebSocketEndpointLockManager::GetInstance()->LockEndpoint(
    124       CurrentAddress(), this);
    125   next_state_ = STATE_OBTAIN_LOCK_COMPLETE;
    126   return rv;
    127 }
    128 
    129 int WebSocketTransportConnectSubJob::DoEndpointLockComplete() {
    130   next_state_ = STATE_TRANSPORT_CONNECT;
    131   return OK;
    132 }
    133 
    134 int WebSocketTransportConnectSubJob::DoTransportConnect() {
    135   // TODO(ricea): Update global g_last_connect_time and report
    136   // ConnectInterval.
    137   next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE;
    138   AddressList one_address(CurrentAddress());
    139   transport_socket_ = client_socket_factory()->CreateTransportClientSocket(
    140       one_address, net_log().net_log(), net_log().source());
    141   // This use of base::Unretained() is safe because transport_socket_ is
    142   // destroyed in the destructor.
    143   return transport_socket_->Connect(base::Bind(
    144       &WebSocketTransportConnectSubJob::OnIOComplete, base::Unretained(this)));
    145 }
    146 
    147 int WebSocketTransportConnectSubJob::DoTransportConnectComplete(int result) {
    148   next_state_ = STATE_DONE;
    149   WebSocketEndpointLockManager* endpoint_lock_manager =
    150       WebSocketEndpointLockManager::GetInstance();
    151   if (result != OK) {
    152     endpoint_lock_manager->UnlockEndpoint(CurrentAddress());
    153 
    154     if (current_address_index_ + 1 < addresses_.size()) {
    155       // Try falling back to the next address in the list.
    156       next_state_ = STATE_OBTAIN_LOCK;
    157       ++current_address_index_;
    158       result = OK;
    159     }
    160 
    161     return result;
    162   }
    163 
    164   endpoint_lock_manager->RememberSocket(transport_socket_.get(),
    165                                         CurrentAddress());
    166 
    167   return result;
    168 }
    169 
    170 }  // namespace net
    171