1 /*
2   Copyright (c) 2014-2017 DataStax
3 
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7 
8   http://www.apache.org/licenses/LICENSE-2.0
9 
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15 */
16 
17 #ifndef DATASTAX_INTERNAL_CONNECTOR_HPP
18 #define DATASTAX_INTERNAL_CONNECTOR_HPP
19 
20 #include <uv.h>
21 
22 #include "address.hpp"
23 #include "callback.hpp"
24 #include "ref_counted.hpp"
25 
26 namespace datastax { namespace internal { namespace core {
27 
28 /**
29  * A wrapper for uv_connect that handles connecting a TCP connection.
30  */
31 class TcpConnector : public RefCounted<TcpConnector> {
32 public:
33   typedef SharedRefPtr<TcpConnector> Ptr;
34 
35   typedef internal::Callback<void, TcpConnector*> Callback;
36 
37   enum Status { NEW, CONNECTING, FAILED_BAD_PARAM, FAILED_TO_CONNECT, CANCELED, SUCCESS };
38 
39   /**
40    * Constructor
41    *
42    * @param address The address to connect to.
43    */
TcpConnector(const Address & address)44   TcpConnector(const Address& address)
45       : address_(address)
46       , status_(NEW)
47       , uv_status_(-1) {
48     req_.data = this;
49   }
50 
51   /**
52    * Connect the given TCP handle.
53    *
54    * @param handle The handle to connect.
55    * @param callback A callback that's called when the handle is connected or
56    * an error occurs.
57    */
connect(uv_tcp_t * handle,const Callback & callback)58   void connect(uv_tcp_t* handle, const Callback& callback) {
59     int rc = 0;
60 
61     inc_ref(); // For the event loop
62 
63     callback_ = callback;
64     status_ = CONNECTING;
65 
66     Address::SocketStorage storage;
67     rc = uv_tcp_connect(&req_, handle, address_.to_sockaddr(&storage), on_connect);
68 
69     if (rc != 0) {
70       status_ = FAILED_BAD_PARAM;
71       uv_status_ = rc;
72       callback_(this);
73       dec_ref();
74     }
75   }
76 
77   /**
78    * Cancel the connection process.
79    */
cancel()80   void cancel() {
81     if (status_ == CONNECTING) {
82       uv_cancel(reinterpret_cast<uv_req_t*>(&req_));
83       status_ = CANCELED;
84     }
85   }
86 
87 public:
loop()88   uv_loop_t* loop() { return req_.handle->loop; }
89 
is_success()90   bool is_success() { return status_ == SUCCESS; }
is_canceled()91   bool is_canceled() { return status_ == CANCELED; }
status()92   Status status() { return status_; }
uv_status()93   int uv_status() { return uv_status_; }
94 
address()95   const Address& address() { return address_; }
96 
97 private:
on_connect(uv_connect_t * req,int status)98   static void on_connect(uv_connect_t* req, int status) {
99     TcpConnector* connector = static_cast<TcpConnector*>(req->data);
100     if (connector->status_ == CONNECTING) {
101       if (status == 0) {
102         connector->status_ = SUCCESS;
103       } else {
104         connector->status_ = FAILED_TO_CONNECT;
105       }
106     }
107 
108     connector->uv_status_ = status;
109     connector->callback_(connector);
110     connector->dec_ref();
111   }
112 
113 private:
114   uv_connect_t req_;
115   Address address_;
116   Callback callback_;
117   Status status_;
118   int uv_status_;
119 };
120 
121 }}} // namespace datastax::internal::core
122 
123 #endif
124