@@ -75,36 +75,57 @@ struct Session_builder
75
75
3. If a bail-out error was detected, throws that error.
76
76
*/
77
77
78
- template <class Conn >
79
- Conn* connect (Conn*);
80
-
81
78
bool operator () (const ds::TCPIP &ds, const ds::TCPIP::Options &options);
82
79
#ifndef WIN32
83
80
bool operator () (const ds::Unix_socket&ds, const ds::Unix_socket::Options &options);
84
81
#endif
85
82
bool operator () (const ds::TCPIP_old &ds, const ds::TCPIP_old::Options &options);
86
83
84
+ /*
85
+ Make a connection attempt using the given connection object. Returns true if
86
+ connection was established, otherwise returns false or throws error
87
+ depending on the value of m_throw_errors;
88
+ */
89
+
90
+ template <class Conn >
91
+ bool connect (Conn&);
92
+
87
93
#ifdef WITH_SSL
88
- TLS* tls_connect (Socket_base &conn, const TLS::Options &opt);
94
+
95
+ /*
96
+ Given plain connection `conn` (which should be a dynamically allocated
97
+ object) create a TLS connection over it, if possible.
98
+
99
+ If a TLS object is successfully created, it takes the ownership of the
100
+ plain connection object passed to this call. The caller of this method
101
+ should take responsibility for the returned TLS object in that case.
102
+
103
+ If this method throws error, then it deletes the plain connection object
104
+ first, so that the caller does not need to bother about it. But if this
105
+ method returns NULL, then the ownership of the plain connection
106
+ object stays with the caller who is responsible of deleting it.
107
+
108
+ Returns NULL if TLS connections are not supported. Throws errors in case
109
+ of other problems.
110
+ */
111
+
112
+ TLS* tls_connect (Socket_base *conn, const TLS::Options &opt);
89
113
#endif
90
114
};
91
115
92
116
93
117
template <class Conn >
94
- Conn* Session_builder::connect (Conn* connection)
118
+ bool Session_builder::connect (Conn & connection)
95
119
{
96
120
m_attempts++;
97
121
98
- assert (connection);
99
-
100
122
try
101
123
{
102
- connection->connect ();
124
+ connection.connect ();
125
+ return true ;
103
126
}
104
127
catch (...)
105
128
{
106
- delete connection;
107
-
108
129
// Use rethrow_error() to wrap arbitrary exception in cdk::Error.
109
130
110
131
try {
@@ -123,9 +144,8 @@ Conn* Session_builder::connect(Conn* connection)
123
144
m_error.reset (err.clone ());
124
145
}
125
146
126
- return NULL ;
147
+ return false ;
127
148
}
128
- return connection;
129
149
}
130
150
131
151
@@ -138,23 +158,56 @@ Session_builder::operator() (
138
158
using foundation::connection::TCPIP;
139
159
using foundation::connection::Socket_base;
140
160
141
- TCPIP* connection = connect (new TCPIP (ds.host (), ds.port ()));
161
+ unique_ptr< TCPIP> connection (new TCPIP (ds.host (), ds.port ()));
142
162
143
- if (!connection)
163
+ if (!connect (* connection) )
144
164
return false ; // continue to next host if available
145
165
146
166
#ifdef WITH_SSL
147
- TLS *tls_conn = tls_connect (*connection, options.get_tls ());
167
+
168
+ /*
169
+ Note: We must be careful to release the unique_ptr<> if tls_connect()
170
+ throws error or returns a TLS object because in that case the plain
171
+ connection object is either deleted by tls_connect() or its ownership
172
+ is passed to the returned TLS object. However, if tls_connect() returns
173
+ NULL, we are still responsible for the plain connection object.
174
+ */
175
+
176
+ TLS * tls_conn = nullptr ;
177
+ try {
178
+ tls_conn = tls_connect (connection.get (), options.get_tls ());
179
+ }
180
+ catch (...)
181
+ {
182
+ connection.release ();
183
+ throw ;
184
+ }
185
+
148
186
if (tls_conn)
149
187
{
188
+ // Now tls_conn owns the plain connection.
189
+ connection.release ();
190
+
191
+ /*
192
+ Note: Order is important! We need to take ownership of tls_conn before
193
+ calling mysqlx::Session() ctor which might throw errors.
194
+ */
195
+
150
196
m_conn = tls_conn;
151
197
m_sess = new mysqlx::Session (*tls_conn, options);
152
198
}
153
199
else
154
200
#endif
155
201
{
156
- m_conn = connection;
202
+ /*
203
+ Note: Order is important! We must construct mysqlx::Session using
204
+ connection object of type TCPIP and we must do it before
205
+ connection.release() is called. If session ctor fails, the unique_ptr<>
206
+ will still take care of deleting the connection object.
207
+ */
208
+
157
209
m_sess = new mysqlx::Session (*connection, options);
210
+ m_conn = connection.release ();
158
211
}
159
212
160
213
m_database = options.database ();
@@ -171,13 +224,13 @@ Session_builder::operator() (
171
224
using foundation::connection::Unix_socket;
172
225
using foundation::connection::Socket_base;
173
226
174
- Unix_socket* connection = connect (new Unix_socket (ds.path ()));
227
+ unique_ptr< Unix_socket> connection (new Unix_socket (ds.path ()));
175
228
176
- if (!connection)
229
+ if (!connect (* connection) )
177
230
return false ; // continue to next host if available
178
231
179
- m_conn = connection;
180
232
m_sess = new mysqlx::Session (*connection, options);
233
+ m_conn = connection.release ();
181
234
182
235
m_database = options.database ();
183
236
@@ -201,8 +254,17 @@ Session_builder::operator() (
201
254
#ifdef WITH_SSL
202
255
203
256
Session_builder::TLS*
204
- Session_builder::tls_connect (Socket_base & connection, const TLS::Options &options)
257
+ Session_builder::tls_connect (Socket_base * connection, const TLS::Options &options)
205
258
{
259
+ /*
260
+ Note: In case of throwing any errors, the connection object should be
261
+ deleted - this is taken care of by the unique_ptr<>. However, if returning
262
+ NULL, the ownership stays with the caller of this method and hence we must
263
+ release the unique_ptr<>.
264
+ */
265
+
266
+ unique_ptr<Socket_base> conn_ptr (connection);
267
+
206
268
if (!options.get_ca ().empty () &&
207
269
options.ssl_mode () < TLS::Options::SSL_MODE::VERIFY_CA)
208
270
throw Error (cdkerrc::generic_error,
@@ -214,11 +276,16 @@ Session_builder::tls_connect(Socket_base &connection, const TLS::Options &option
214
276
" Missing ssl-ca option to verify CA" );
215
277
216
278
if (options.ssl_mode () == TLS::Options::SSL_MODE::DISABLED)
279
+ {
280
+ conn_ptr.release ();
217
281
return NULL ;
282
+ }
283
+
284
+ assert (connection);
218
285
219
286
// Negotiate TLS capabilities.
220
287
221
- cdk::protocol::mysqlx::Protocol proto (connection);
288
+ cdk::protocol::mysqlx::Protocol proto (* connection);
222
289
223
290
struct : cdk::protocol::mysqlx::api::Any::Document
224
291
{
@@ -261,21 +328,30 @@ Session_builder::tls_connect(Socket_base &connection, const TLS::Options &option
261
328
proto.rcv_Reply (prc).wait ();
262
329
263
330
if (!prc.m_tls )
331
+ {
332
+ conn_ptr.release ();
264
333
return NULL ;
334
+ }
265
335
266
336
/*
267
337
Capabilites OK, create TLS connection now.
268
338
269
- Note: The TLS object is protected by unique_ptr<> for the case that
270
- connection attempt below throws an error - in that case the newly created
271
- object should be deleted.
339
+ Note: The TLS object created here takes ownership of the plain connection
340
+ object passed to this method. The TLS object itself is protected by a
341
+ unique_ptr<> for the case that connection attempt below throws an error
342
+ - in that case it will be deleted before leaving this method.
272
343
*/
273
344
274
- unique_ptr<TLS> tls_conn (new TLS (&connection , options));
345
+ unique_ptr<TLS> tls_conn (new TLS (conn_ptr. release () , options));
275
346
276
- // TODO: attempt failover if TLS-layer reports network error?
347
+ // TODO: attempt fail-over if TLS-layer reports network error?
277
348
tls_conn->connect ();
278
349
350
+ /*
351
+ Finally we pass the ownership of the created TLS object to the caller
352
+ of this method.
353
+ */
354
+
279
355
return tls_conn.release ();
280
356
}
281
357
0 commit comments