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