1 /*
2   Copyright (c) DataStax, Inc.
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_CONTROL_CONNECTOR_HPP
18 #define DATASTAX_INTERNAL_CONTROL_CONNECTOR_HPP
19 
20 #include "callback.hpp"
21 #include "control_connection.hpp"
22 #include "event_response.hpp"
23 #include "ref_counted.hpp"
24 
25 namespace datastax { namespace internal { namespace core {
26 
27 class HostsConnectorRequestCallback;
28 class Metrics;
29 class SchemaConnectorRequestCallback;
30 
31 /**
32  * The initial schema metadata retrieved from the cluster when the control
33  * connection is established.
34  */
35 struct ControlConnectionSchema {
36   ResultResponse::Ptr keyspaces;
37   ResultResponse::Ptr tables;
38   ResultResponse::Ptr views;
39   ResultResponse::Ptr columns;
40   ResultResponse::Ptr indexes;
41   ResultResponse::Ptr user_types;
42   ResultResponse::Ptr functions;
43   ResultResponse::Ptr aggregates;
44   ResultResponse::Ptr virtual_keyspaces;
45   ResultResponse::Ptr virtual_tables;
46   ResultResponse::Ptr virtual_columns;
47 };
48 
49 /**
50  * A connector that establishes a control connection, negotiates the protocol
51  * version, and registers for cluster events (topology and schema changes). It
52  * also retrieves the initial host and schema metadata for the cluster.
53  */
54 class ControlConnector
55     : public RefCounted<ControlConnector>
56     , public RecordingConnectionListener {
57 public:
58   typedef SharedRefPtr<ControlConnector> Ptr;
59   typedef Vector<Ptr> Vec;
60   typedef internal::Callback<void, ControlConnector*> Callback;
61 
62   enum ControlConnectionError {
63     CONTROL_CONNECTION_OK,
64     CONTROL_CONNECTION_CANCELED,
65     CONTROL_CONNECTION_ERROR_CLOSE,
66     CONTROL_CONNECTION_ERROR_CONNECTION,
67     CONTROL_CONNECTION_ERROR_HOSTS,
68     CONTROL_CONNECTION_ERROR_SCHEMA
69   };
70 
71   /**
72    * Constructor
73    *
74    * @param host The host to connect to.
75    * @param protocol_version The initial protocol version to use for the
76    * connection.
77    * @param callback
78    */
79   ControlConnector(const Host::Ptr& host, ProtocolVersion protocol_version,
80                    const Callback& callback);
81 
82   /**
83    * Sets the listener to use after the control connection has been
84    * established.
85    *
86    * @param listener The control connection listener.
87    * @return The connector to chain calls.
88    */
89   ControlConnector* with_listener(ControlConnectionListener* listener);
90 
91   /**
92    * Set the metrics object to use to record metrics for the connection.
93    *
94    * @param metrics A metrics object.
95    * @return The connector to chain calls.
96    */
97   ControlConnector* with_metrics(Metrics* metrics);
98 
99   /**
100    * Sets the control connection settings as well as the underlying settings
101    * for the connection and socket.
102    *
103    * @param settings The settings to be used for the connection process.
104    * @return The connector to chain calls.
105    */
106   ControlConnector* with_settings(const ControlConnectionSettings& settings);
107 
108   /**
109    * Start the connection process.
110    *
111    * @param loop An event loop to use for connecting the connection.
112    */
113   void connect(uv_loop_t* loop);
114 
115   /**
116    * Cancel the connection process.
117    */
118   void cancel();
119 
120   /**
121    * Release the connection from the connector. If not released in the callback
122    * the connection will automatically be closed.
123    *
124    * @return The connection object for this connector. This returns a null object
125    * if the connection is not connected or an error occurred.
126    */
127   ControlConnection::Ptr release_connection();
128 
129 public:
130   /**
131    * Gets the server version of the connection.
132    *
133    * @return The server version number.
134    */
server_version() const135   const VersionNumber server_version() const { return server_version_; }
136 
137   /**
138    * The initial list of hosts available in the cluster.
139    *
140    * @return
141    */
hosts() const142   const HostMap& hosts() const { return hosts_; }
143 
144   /**
145    * The initial schema metadata.
146    *
147    * @return
148    */
schema() const149   const ControlConnectionSchema& schema() const { return schema_; }
150 
151 public:
address() const152   const Address& address() const { return connector_->address(); }
153 
protocol_version() const154   const ProtocolVersion protocol_version() const { return connector_->protocol_version(); }
155 
is_ok() const156   bool is_ok() const { return error_code_ == CONTROL_CONNECTION_OK; }
is_canceled() const157   bool is_canceled() const { return error_code_ == CONTROL_CONNECTION_CANCELED; }
is_invalid_protocol() const158   bool is_invalid_protocol() const {
159     return error_code_ == CONTROL_CONNECTION_ERROR_CONNECTION && connector_->is_invalid_protocol();
160   }
is_ssl_error() const161   bool is_ssl_error() const {
162     return error_code_ == CONTROL_CONNECTION_ERROR_CONNECTION && connector_->is_ssl_error();
163   }
is_auth_error() const164   bool is_auth_error() const {
165     return error_code_ == CONTROL_CONNECTION_ERROR_CONNECTION && connector_->is_auth_error();
166   }
167 
supported_options() const168   const StringMultimap& supported_options() const { return connector_->supported_options(); }
169 
error_code() const170   ControlConnectionError error_code() const { return error_code_; }
error_message() const171   const String& error_message() const { return error_message_; }
ssl_error_code()172   CassError ssl_error_code() { return connector_->ssl_error_code(); }
173 
connection_error_code()174   Connector::ConnectionError connection_error_code() { return connector_->error_code(); }
connection_error_message()175   const String& connection_error_message() { return connector_->error_message(); }
176 
177 private:
178   friend class HostsConnectorRequestCallback;
179   friend class SchemaConnectorRequestCallback;
180 
181 private:
182   void finish();
183 
184   void on_success();
185   void on_error(ControlConnectionError code, const String& message);
186 
187   void on_connect(Connector* connector);
188   void handle_connect(Connector* connector);
189 
190   void query_hosts();
191   void handle_query_hosts(HostsConnectorRequestCallback* callback);
192 
193   void query_schema();
194   void handle_query_schema(SchemaConnectorRequestCallback* callback);
195 
196 private:
197   // Connection listener methods
198   virtual void on_close(Connection* connection);
199 
200 private:
201   Connector::Ptr connector_;
202   Connection::Ptr connection_;
203   ControlConnection::Ptr control_connection_;
204   VersionNumber server_version_;
205   VersionNumber dse_server_version_;
206   HostMap hosts_;
207 
208   /**
209    * This is used to keep track of the "listen address" which is used to look
210    * up a host in the "system.peers" table by its primary key "peer". No other
211    * component cares about this information so it's not need in the `Host` type.
212    */
213   ListenAddressMap listen_addresses_;
214   ControlConnectionSchema schema_;
215 
216   Callback callback_;
217 
218   ControlConnectionError error_code_;
219   String error_message_;
220 
221   ControlConnectionListener* listener_;
222   Metrics* metrics_;
223   Host::Ptr host_;
224   ControlConnectionSettings settings_;
225 };
226 
227 }}} // namespace datastax::internal::core
228 
229 #endif
230