54
54
55
55
from .expr import ExprParser as expr
56
56
57
- _SPLIT = re .compile (r',(?![^\(\)]*\))' )
58
- _PRIORITY = re .compile (r'^\(address=(.+),priority=(\d+)\)$' , re .VERBOSE )
57
+ _SPLIT_RE = re .compile (r",(?![^\(\)]*\))" )
58
+ _PRIORITY_RE = re .compile (r"^\(address=(.+),priority=(\d+)\)$" , re .VERBOSE )
59
+ _URI_SCHEME_RE = re .compile (r"^([a-zA-Z][a-zA-Z0-9+\-.]+)://(.*)" )
59
60
_SSL_OPTS = ["ssl-cert" , "ssl-ca" , "ssl-key" , "ssl-crl" ]
60
61
_SESS_OPTS = _SSL_OPTS + ["user" , "password" , "schema" , "host" , "port" ,
61
62
"routers" , "socket" , "ssl-mode" , "auth" , "use-pure" ,
62
- "connect-timeout" , "connection-attributes" ]
63
+ "connect-timeout" , "connection-attributes" ,
64
+ "dns-srv" ]
63
65
64
66
logging .getLogger (__name__ ).addHandler (logging .NullHandler ())
65
67
@@ -80,11 +82,11 @@ def _parse_address_list(path):
80
82
and path .endswith ("]" )
81
83
82
84
routers = []
83
- address_list = _SPLIT .split (path [1 :- 1 ] if array else path )
85
+ address_list = _SPLIT_RE .split (path [1 :- 1 ] if array else path )
84
86
for address in address_list :
85
87
router = {}
86
88
87
- match = _PRIORITY .match (address )
89
+ match = _PRIORITY_RE .match (address )
88
90
if match :
89
91
address = match .group (1 )
90
92
router ["priority" ] = int (match .group (2 ))
@@ -109,13 +111,24 @@ def _parse_connection_uri(uri):
109
111
Returns:
110
112
Returns a dict with parsed values of credentials and address of the
111
113
MySQL server/farm.
114
+
115
+ Raises:
116
+ :class:`mysqlx.InterfaceError`: If contains a duplicate option or
117
+ URI scheme is not valid.
112
118
"""
113
119
settings = {"schema" : "" }
114
- uri = "{0}{1}" .format ("" if uri .startswith ("mysqlx://" )
115
- else "mysqlx://" , uri )
116
- _ , temp = uri .split ("://" , 1 )
117
- userinfo , temp = temp .partition ("@" )[::2 ]
118
- host , query_str = temp .partition ("?" )[::2 ]
120
+
121
+ match = _URI_SCHEME_RE .match (uri )
122
+ scheme , uri = match .groups () if match else ("mysqlx" , uri )
123
+
124
+ if scheme not in ("mysqlx" , "mysqlx+srv" ):
125
+ raise InterfaceError ("Scheme '{0}' is not valid" .format (scheme ))
126
+
127
+ if scheme == "mysqlx+srv" :
128
+ settings ["dns-srv" ] = True
129
+
130
+ userinfo , tmp = uri .partition ("@" )[::2 ]
131
+ host , query_str = tmp .partition ("?" )[::2 ]
119
132
120
133
pos = host .rfind ("/" )
121
134
if host [pos :].find (")" ) == - 1 and pos > 0 :
@@ -130,14 +143,17 @@ def _parse_connection_uri(uri):
130
143
if host .startswith (("/" , ".." , "." )):
131
144
settings ["socket" ] = unquote (host )
132
145
elif host .startswith ("\\ ." ):
133
- raise InterfaceError ("Windows Pipe is not supported. " )
146
+ raise InterfaceError ("Windows Pipe is not supported" )
134
147
else :
135
148
settings .update (_parse_address_list (host ))
136
149
150
+ invalid_options = ("user" , "password" , "dns-srv" )
137
151
for key , val in parse_qsl (query_str , True ):
138
152
opt = key .replace ("_" , "-" ).lower ()
153
+ if opt in invalid_options :
154
+ raise InterfaceError ("Invalid option: '{0}'" .format (key ))
139
155
if opt in settings :
140
- raise InterfaceError ("Duplicate option '{0}'. " .format (key ))
156
+ raise InterfaceError ("Duplicate option: '{0}'" .format (key ))
141
157
if opt in _SSL_OPTS :
142
158
settings [opt ] = unquote (val .strip ("()" ))
143
159
else :
@@ -159,15 +175,18 @@ def _validate_settings(settings):
159
175
160
176
Args:
161
177
settings: dict containing connection settings.
178
+
179
+ Raises:
180
+ :class:`mysqlx.InterfaceError`: On any configuration issue.
162
181
"""
163
182
invalid_opts = set (settings .keys ()).difference (_SESS_OPTS )
164
183
if invalid_opts :
165
- raise ProgrammingError ("Invalid options: {0}. "
166
- "" .format (", " .join (invalid_opts )))
184
+ raise InterfaceError ("Invalid option(s): ' {0}' "
185
+ "" .format ("', ' " .join (invalid_opts )))
167
186
168
187
if "routers" in settings :
169
188
for router in settings ["routers" ]:
170
- _validate_hosts (router )
189
+ _validate_hosts (router , 33060 )
171
190
elif "host" in settings :
172
191
_validate_hosts (settings )
173
192
@@ -176,23 +195,23 @@ def _validate_settings(settings):
176
195
settings ["ssl-mode" ] = settings ["ssl-mode" ].lower ()
177
196
SSLMode .index (settings ["ssl-mode" ])
178
197
except (AttributeError , ValueError ):
179
- raise InterfaceError ("Invalid SSL Mode '{0}'. "
198
+ raise InterfaceError ("Invalid SSL Mode '{0}'"
180
199
"" .format (settings ["ssl-mode" ]))
181
200
if settings ["ssl-mode" ] == SSLMode .DISABLED and \
182
201
any (key in settings for key in _SSL_OPTS ):
183
- raise InterfaceError ("SSL options used with ssl-mode 'disabled'. " )
202
+ raise InterfaceError ("SSL options used with ssl-mode 'disabled'" )
184
203
185
204
if "ssl-crl" in settings and not "ssl-ca" in settings :
186
- raise InterfaceError ("CA Certificate not provided. " )
205
+ raise InterfaceError ("CA Certificate not provided" )
187
206
if "ssl-key" in settings and not "ssl-cert" in settings :
188
- raise InterfaceError ("Client Certificate not provided. " )
207
+ raise InterfaceError ("Client Certificate not provided" )
189
208
190
209
if not "ssl-ca" in settings and settings .get ("ssl-mode" ) \
191
210
in [SSLMode .VERIFY_IDENTITY , SSLMode .VERIFY_CA ]:
192
- raise InterfaceError ("Cannot verify Server without CA. " )
211
+ raise InterfaceError ("Cannot verify Server without CA" )
193
212
if "ssl-ca" in settings and settings .get ("ssl-mode" ) \
194
213
not in [SSLMode .VERIFY_IDENTITY , SSLMode .VERIFY_CA ]:
195
- raise InterfaceError ("Must verify Server if CA is provided. " )
214
+ raise InterfaceError ("Must verify Server if CA is provided" )
196
215
197
216
if "auth" in settings :
198
217
try :
@@ -204,12 +223,39 @@ def _validate_settings(settings):
204
223
if "connection-attributes" in settings :
205
224
validate_connection_attributes (settings )
206
225
226
+ if "connect-timeout" in settings :
227
+ try :
228
+ if isinstance (settings ["connect-timeout" ], STRING_TYPES ):
229
+ settings ["connect-timeout" ] = int (settings ["connect-timeout" ])
230
+ if not isinstance (settings ["connect-timeout" ], INT_TYPES ) \
231
+ or settings ["connect-timeout" ] < 0 :
232
+ raise ValueError
233
+ except ValueError :
234
+ raise TypeError ("The connection timeout value must be a positive "
235
+ "integer (including 0)" )
236
+
237
+ if "dns-srv" in settings :
238
+ if not isinstance (settings ["dns-srv" ], bool ):
239
+ raise InterfaceError ("The value of 'dns-srv' must be a boolean" )
240
+ if settings .get ("socket" ):
241
+ raise InterfaceError ("Using Unix domain sockets with DNS SRV "
242
+ "lookup is not allowed" )
243
+ if settings .get ("port" ):
244
+ raise InterfaceError ("Specifying a port number with DNS SRV "
245
+ "lookup is not allowed" )
246
+ if settings .get ("routers" ):
247
+ raise InterfaceError ("Specifying multiple hostnames with DNS "
248
+ "SRV look up is not allowed" )
249
+ elif "host" in settings and not settings .get ("port" ):
250
+ settings ["port" ] = 33060
207
251
208
- def _validate_hosts (settings ):
252
+
253
+ def _validate_hosts (settings , default_port = None ):
209
254
"""Validate hosts.
210
255
211
256
Args:
212
257
settings (dict): Settings dictionary.
258
+ default_port (int): Default connection port.
213
259
214
260
Raises:
215
261
:class:`mysqlx.InterfaceError`: If priority or port are invalid.
@@ -225,8 +271,8 @@ def _validate_hosts(settings):
225
271
settings ["port" ] = int (settings ["port" ])
226
272
except NameError :
227
273
raise InterfaceError ("Invalid port" )
228
- elif "host" in settings :
229
- settings ["port" ] = 33060
274
+ elif "host" in settings and default_port :
275
+ settings ["port" ] = default_port
230
276
231
277
232
278
def validate_connection_attributes (settings ):
@@ -250,9 +296,9 @@ def validate_connection_attributes(settings):
250
296
return
251
297
if not (conn_attrs .startswith ("[" ) and conn_attrs .endswith ("]" )) and \
252
298
not conn_attrs in ['False' , "false" , "True" , "true" ]:
253
- raise InterfaceError ("connection-attributes must be Boolean or a "
254
- "list of key-value pairs, found: '{}' "
255
- "" .format (conn_attrs ))
299
+ raise InterfaceError ("The value of ' connection-attributes' must "
300
+ "be a boolean or a list of key-value pairs, "
301
+ "found: '{}' " .format (conn_attrs ))
256
302
elif conn_attrs in ['False' , "false" , "True" , "true" ]:
257
303
if conn_attrs in ['False' , "false" ]:
258
304
settings ["connection-attributes" ] = False
@@ -317,7 +363,7 @@ def validate_connection_attributes(settings):
317
363
# Validate attribute name limit 32 characters
318
364
if len (attr_name ) > 32 :
319
365
raise InterfaceError ("Attribute name '{}' exceeds 32 "
320
- "characters limit size. " .format (attr_name ))
366
+ "characters limit size" .format (attr_name ))
321
367
# Validate names in connection-attributes cannot start with "_"
322
368
if attr_name .startswith ("_" ):
323
369
raise InterfaceError ("Key names in connection-attributes "
@@ -327,7 +373,7 @@ def validate_connection_attributes(settings):
327
373
# Validate value type
328
374
if not isinstance (attr_value , STRING_TYPES ):
329
375
raise InterfaceError ("Attribute '{}' value: '{}' must "
330
- "be a string type. "
376
+ "be a string type"
331
377
"" .format (attr_name , attr_value ))
332
378
# Validate attribute value limit 1024 characters
333
379
if len (attr_value ) > 1024 :
@@ -354,6 +400,7 @@ def _get_connection_settings(*args, **kwargs):
354
400
355
401
Raises:
356
402
TypeError: If connection timeout is not a positive integer.
403
+ :class:`mysqlx.InterfaceError`: If settings not provided.
357
404
"""
358
405
settings = {}
359
406
if args :
@@ -369,17 +416,6 @@ def _get_connection_settings(*args, **kwargs):
369
416
if not settings :
370
417
raise InterfaceError ("Settings not provided" )
371
418
372
- if "connect-timeout" in settings :
373
- try :
374
- if isinstance (settings ["connect-timeout" ], STRING_TYPES ):
375
- settings ["connect-timeout" ] = int (settings ["connect-timeout" ])
376
- if not isinstance (settings ["connect-timeout" ], INT_TYPES ) \
377
- or settings ["connect-timeout" ] < 0 :
378
- raise ValueError
379
- except ValueError :
380
- raise TypeError ("The connection timeout value must be a positive "
381
- "integer (including 0)" )
382
-
383
419
_validate_settings (settings )
384
420
return settings
385
421
0 commit comments