4
4
# pylint: disable=too-many-lines,duplicate-code
5
5
6
6
import os
7
+ import select
7
8
import time
8
9
import errno
9
10
from enum import Enum
51
52
RECONNECT_DELAY ,
52
53
DEFAULT_TRANSPORT ,
53
54
SSL_TRANSPORT ,
55
+ DEFAULT_HOST ,
56
+ DEFAULT_PORT ,
57
+ DEFAULT_SOCKET_FD ,
54
58
DEFAULT_SSL_KEY_FILE ,
55
59
DEFAULT_SSL_CERT_FILE ,
56
60
DEFAULT_SSL_CA_FILE ,
@@ -594,7 +598,10 @@ class Connection(ConnectionInterface):
594
598
:value: :exc:`~tarantool.error.CrudModuleError`
595
599
"""
596
600
597
- def __init__ (self , host , port ,
601
+ def __init__ (self ,
602
+ host = DEFAULT_HOST ,
603
+ port = DEFAULT_PORT ,
604
+ socket_fd = DEFAULT_SOCKET_FD ,
598
605
user = None ,
599
606
password = None ,
600
607
socket_timeout = SOCKET_TIMEOUT ,
@@ -623,8 +630,11 @@ def __init__(self, host, port,
623
630
Unix sockets.
624
631
:type host: :obj:`str` or :obj:`None`
625
632
626
- :param port: Server port or Unix socket path.
627
- :type port: :obj:`int` or :obj:`str`
633
+ :param port: Server port, or Unix socket path.
634
+ :type port: :obj:`int` or :obj:`str` or :obj:`None`
635
+
636
+ :param socket_fd: socket fd number.
637
+ :type socket_fd: :obj:`int` or :obj:`None`
628
638
629
639
:param user: User name for authentication on the Tarantool
630
640
server.
@@ -804,6 +814,18 @@ def __init__(self, host, port,
804
814
"""
805
815
# pylint: disable=too-many-arguments,too-many-locals,too-many-statements
806
816
817
+ if host is None and port is None and socket_fd is None :
818
+ raise ConfigurationError ("need to specify host/port, "
819
+ "port (in case of Unix sockets) "
820
+ "or socket_fd" )
821
+
822
+ if socket_fd is not None and (host is not None or port is not None ):
823
+ raise ConfigurationError ("specifying both socket_fd and host/port is not allowed" )
824
+
825
+ if host is not None and port is None :
826
+ raise ConfigurationError ("when specifying host, "
827
+ "it is also necessary to specify port" )
828
+
807
829
if msgpack .version >= (1 , 0 , 0 ) and encoding not in (None , 'utf-8' ):
808
830
raise ConfigurationError ("msgpack>=1.0.0 only supports None and "
809
831
+ "'utf-8' encoding option values" )
@@ -820,6 +842,7 @@ def __init__(self, host, port,
820
842
recv .restype = ctypes .c_int
821
843
self .host = host
822
844
self .port = port
845
+ self .socket_fd = socket_fd
823
846
self .user = user
824
847
self .password = password
825
848
self .socket_timeout = socket_timeout
@@ -897,10 +920,37 @@ def connect_basic(self):
897
920
:meta private:
898
921
"""
899
922
900
- if self .host is None :
901
- self .connect_unix ()
902
- else :
923
+ if self .socket_fd is not None :
924
+ self .connect_socket_fd ()
925
+ elif self . host is not None :
903
926
self .connect_tcp ()
927
+ else :
928
+ self .connect_unix ()
929
+
930
+ def connect_socket_fd (self ):
931
+ """
932
+ Establish a connection using an existing socket fd.
933
+
934
+ +---------------------+--------------------------+-------------------------+
935
+ | socket_fd / timeout | >= 0 | `None` |
936
+ +=====================+==========================+=========================+
937
+ | blocking | Set non-blocking socket | Don't change, `select` |
938
+ | | lib call `select` | isn't needed |
939
+ +---------------------+--------------------------+-------------------------+
940
+ | non-blocking | Don't change, socket lib | Don't change, call |
941
+ | | call `select` | `select` ourselves |
942
+ +---------------------+--------------------------+-------------------------+
943
+
944
+ :meta private:
945
+ """
946
+
947
+ self .connected = True
948
+ if self ._socket :
949
+ self ._socket .close ()
950
+
951
+ self ._socket = socket .socket (fileno = self .socket_fd )
952
+ if self .socket_timeout is not None :
953
+ self ._socket .settimeout (self .socket_timeout )
904
954
905
955
def connect_tcp (self ):
906
956
"""
@@ -1124,6 +1174,11 @@ def _recv(self, to_read):
1124
1174
while to_read > 0 :
1125
1175
try :
1126
1176
tmp = self ._socket .recv (to_read )
1177
+ except BlockingIOError :
1178
+ ready , _ , _ = select .select ([self ._socket .fileno ()], [], [], self .socket_timeout )
1179
+ if not ready :
1180
+ raise NetworkError (TimeoutError ()) # pylint: disable=raise-missing-from
1181
+ continue
1127
1182
except OverflowError as exc :
1128
1183
self ._socket .close ()
1129
1184
err = socket .error (
@@ -1163,6 +1218,41 @@ def _read_response(self):
1163
1218
# Read the packet
1164
1219
return self ._recv (length )
1165
1220
1221
+ def _sendall (self , bytes_to_send ):
1222
+ """
1223
+ Sends bytes to the transport (socket).
1224
+
1225
+ :param bytes_to_send: Message to send.
1226
+ :type bytes_to_send: :obj:`bytes`
1227
+
1228
+ :raise: :exc:`~tarantool.error.NetworkError`
1229
+
1230
+ :meta private:
1231
+ """
1232
+
1233
+ total_sent = 0
1234
+ while total_sent < len (bytes_to_send ):
1235
+ try :
1236
+ sent = self ._socket .send (bytes_to_send [total_sent :])
1237
+ if sent == 0 :
1238
+ err = socket .error (
1239
+ errno .ECONNRESET ,
1240
+ "Lost connection to server during query"
1241
+ )
1242
+ raise NetworkError (err )
1243
+ total_sent += sent
1244
+ except BlockingIOError as exc :
1245
+ total_sent += exc .characters_written
1246
+ _ , ready , _ = select .select ([], [self ._socket .fileno ()], [], self .socket_timeout )
1247
+ if not ready :
1248
+ raise NetworkError (TimeoutError ()) # pylint: disable=raise-missing-from
1249
+ except socket .error as exc :
1250
+ err = socket .error (
1251
+ errno .ECONNRESET ,
1252
+ "Lost connection to server during query"
1253
+ )
1254
+ raise NetworkError (err ) from exc
1255
+
1166
1256
def _send_request_wo_reconnect (self , request , on_push = None , on_push_ctx = None ):
1167
1257
"""
1168
1258
Send request without trying to reconnect.
@@ -1191,7 +1281,7 @@ def _send_request_wo_reconnect(self, request, on_push=None, on_push_ctx=None):
1191
1281
response = None
1192
1282
while True :
1193
1283
try :
1194
- self ._socket . sendall (bytes (request ))
1284
+ self ._sendall (bytes (request ))
1195
1285
response = request .response_class (self , self ._read_response ())
1196
1286
break
1197
1287
except SchemaReloadException as exc :
0 commit comments