1
1
from __future__ import unicode_literals
2
2
from distutils .version import StrictVersion
3
+ from errno import EWOULDBLOCK
3
4
from itertools import chain
4
5
import io
5
6
import os
17
18
from redis ._compat import (xrange , imap , byte_to_chr , unicode , long ,
18
19
nativestr , basestring , iteritems ,
19
20
LifoQueue , Empty , Full , urlparse , parse_qs ,
20
- recv , recv_into , unquote )
21
+ recv , recv_into , unquote , BlockingIOError )
21
22
from redis .exceptions import (
22
23
DataError ,
23
24
RedisError ,
31
32
ExecAbortError ,
32
33
ReadOnlyError
33
34
)
34
- from redis .selector import DefaultSelector
35
35
from redis .utils import HIREDIS_AVAILABLE
36
36
if HIREDIS_AVAILABLE :
37
37
import hiredis
61
61
62
62
SERVER_CLOSED_CONNECTION_ERROR = "Connection closed by server."
63
63
64
+ SENTINEL = object ()
65
+
64
66
65
67
class Encoder (object ):
66
68
"Encode strings to bytes and decode bytes to strings"
@@ -126,9 +128,10 @@ def parse_error(self, response):
126
128
127
129
128
130
class SocketBuffer (object ):
129
- def __init__ (self , socket , socket_read_size ):
131
+ def __init__ (self , socket , socket_read_size , socket_timeout ):
130
132
self ._sock = socket
131
133
self .socket_read_size = socket_read_size
134
+ self .socket_timeout = socket_timeout
132
135
self ._buffer = io .BytesIO ()
133
136
# number of bytes written to the buffer from the socket
134
137
self .bytes_written = 0
@@ -139,25 +142,51 @@ def __init__(self, socket, socket_read_size):
139
142
def length (self ):
140
143
return self .bytes_written - self .bytes_read
141
144
142
- def _read_from_socket (self , length = None ):
145
+ def _read_from_socket (self , length = None , timeout = SENTINEL ,
146
+ raise_on_timeout = True ):
147
+ sock = self ._sock
143
148
socket_read_size = self .socket_read_size
144
149
buf = self ._buffer
145
150
buf .seek (self .bytes_written )
146
151
marker = 0
152
+ custom_timeout = timeout is not SENTINEL
147
153
148
- while True :
149
- data = recv (self ._sock , socket_read_size )
150
- # an empty string indicates the server shutdown the socket
151
- if isinstance (data , bytes ) and len (data ) == 0 :
152
- raise socket .error (SERVER_CLOSED_CONNECTION_ERROR )
153
- buf .write (data )
154
- data_length = len (data )
155
- self .bytes_written += data_length
156
- marker += data_length
157
-
158
- if length is not None and length > marker :
159
- continue
160
- break
154
+ try :
155
+ if custom_timeout :
156
+ sock .settimeout (timeout )
157
+ while True :
158
+ data = recv (self ._sock , socket_read_size )
159
+ # an empty string indicates the server shutdown the socket
160
+ if isinstance (data , bytes ) and len (data ) == 0 :
161
+ raise ConnectionError (SERVER_CLOSED_CONNECTION_ERROR )
162
+ buf .write (data )
163
+ data_length = len (data )
164
+ self .bytes_written += data_length
165
+ marker += data_length
166
+
167
+ if length is not None and length > marker :
168
+ continue
169
+ return True
170
+ except BlockingIOError as ex :
171
+ # if we're in nonblocking mode and the recv raises a
172
+ # blocking error, simply return False indicating that
173
+ # there's no data to be read. otherwise raise the
174
+ # original exception.
175
+ if raise_on_timeout or ex .errno != EWOULDBLOCK :
176
+ raise
177
+ return False
178
+ except socket .timeout :
179
+ if raise_on_timeout :
180
+ raise
181
+ return False
182
+ finally :
183
+ if custom_timeout :
184
+ sock .settimeout (self .socket_timeout )
185
+
186
+ def can_read (self , timeout ):
187
+ return bool (self .length ) or \
188
+ self ._read_from_socket (timeout = timeout ,
189
+ raise_on_timeout = False )
161
190
162
191
def read (self , length ):
163
192
length = length + 2 # make sure to read the \r\n terminator
@@ -233,7 +262,9 @@ def __del__(self):
233
262
def on_connect (self , connection ):
234
263
"Called when the socket connects"
235
264
self ._sock = connection ._sock
236
- self ._buffer = SocketBuffer (self ._sock , self .socket_read_size )
265
+ self ._buffer = SocketBuffer (self ._sock ,
266
+ self .socket_read_size ,
267
+ connection .socket_timeout )
237
268
self .encoder = connection .encoder
238
269
239
270
def on_disconnect (self ):
@@ -244,8 +275,8 @@ def on_disconnect(self):
244
275
self ._buffer = None
245
276
self .encoder = None
246
277
247
- def can_read (self ):
248
- return self ._buffer and bool ( self ._buffer .length )
278
+ def can_read (self , timeout ):
279
+ return self ._buffer and self ._buffer .can_read ( timeout )
249
280
250
281
def read_response (self ):
251
282
response = self ._buffer .readline ()
@@ -312,6 +343,7 @@ def __del__(self):
312
343
313
344
def on_connect (self , connection ):
314
345
self ._sock = connection ._sock
346
+ self ._socket_timeout = connection .socket_timeout
315
347
kwargs = {
316
348
'protocolError' : InvalidResponse ,
317
349
'replyError' : self .parse_error ,
@@ -333,13 +365,52 @@ def on_disconnect(self):
333
365
self ._reader = None
334
366
self ._next_response = False
335
367
336
- def can_read (self ):
368
+ def can_read (self , timeout ):
337
369
if not self ._reader :
338
370
raise ConnectionError (SERVER_CLOSED_CONNECTION_ERROR )
339
371
340
372
if self ._next_response is False :
341
373
self ._next_response = self ._reader .gets ()
342
- return self ._next_response is not False
374
+ if self ._next_response is False :
375
+ return self .read_from_socket (timeout = timeout ,
376
+ raise_on_timeout = False )
377
+ return True
378
+
379
+ def read_from_socket (self , timeout = SENTINEL , raise_on_timeout = True ):
380
+ sock = self ._sock
381
+ custom_timeout = timeout is not SENTINEL
382
+ try :
383
+ if custom_timeout :
384
+ sock .settimeout (timeout )
385
+ if HIREDIS_USE_BYTE_BUFFER :
386
+ bufflen = recv_into (self ._sock , self ._buffer )
387
+ if bufflen == 0 :
388
+ raise ConnectionError (SERVER_CLOSED_CONNECTION_ERROR )
389
+ self ._reader .feed (self ._buffer , 0 , bufflen )
390
+ else :
391
+ buffer = recv (self ._sock , self .socket_read_size )
392
+ # an empty string indicates the server shutdown the socket
393
+ if not isinstance (buffer , bytes ) or len (buffer ) == 0 :
394
+ raise ConnectionError (SERVER_CLOSED_CONNECTION_ERROR )
395
+ self ._reader .feed (buffer )
396
+ # data was read from the socket and added to the buffer.
397
+ # return True to indicate that data was read.
398
+ return True
399
+ except BlockingIOError as ex :
400
+ # if we're in nonblocking mode and the recv raises a
401
+ # blocking error, simply return False indicating that
402
+ # there's no data to be read. otherwise raise the
403
+ # original exception.
404
+ if raise_on_timeout or ex .errno != EWOULDBLOCK :
405
+ raise
406
+ return False
407
+ except socket .timeout :
408
+ if not raise_on_timeout :
409
+ raise
410
+ return False
411
+ finally :
412
+ if custom_timeout :
413
+ sock .settimeout (self ._socket_timeout )
343
414
344
415
def read_response (self ):
345
416
if not self ._reader :
@@ -352,21 +423,8 @@ def read_response(self):
352
423
return response
353
424
354
425
response = self ._reader .gets ()
355
- socket_read_size = self .socket_read_size
356
426
while response is False :
357
- if HIREDIS_USE_BYTE_BUFFER :
358
- bufflen = recv_into (self ._sock , self ._buffer )
359
- if bufflen == 0 :
360
- raise socket .error (SERVER_CLOSED_CONNECTION_ERROR )
361
- else :
362
- buffer = recv (self ._sock , socket_read_size )
363
- # an empty string indicates the server shutdown the socket
364
- if not isinstance (buffer , bytes ) or len (buffer ) == 0 :
365
- raise socket .error (SERVER_CLOSED_CONNECTION_ERROR )
366
- if HIREDIS_USE_BYTE_BUFFER :
367
- self ._reader .feed (self ._buffer , 0 , bufflen )
368
- else :
369
- self ._reader .feed (buffer )
427
+ self .read_from_socket ()
370
428
response = self ._reader .gets ()
371
429
# if an older version of hiredis is installed, we need to attempt
372
430
# to convert ResponseErrors to their appropriate types.
@@ -416,7 +474,6 @@ def __init__(self, host='localhost', port=6379, db=0, password=None,
416
474
self .retry_on_timeout = retry_on_timeout
417
475
self .encoder = Encoder (encoding , encoding_errors , decode_responses )
418
476
self ._sock = None
419
- self ._selector = None
420
477
self ._parser = parser_class (socket_read_size = socket_read_size )
421
478
self ._description_args = {
422
479
'host' : self .host ,
@@ -454,7 +511,6 @@ def connect(self):
454
511
raise ConnectionError (self ._error_message (e ))
455
512
456
513
self ._sock = sock
457
- self ._selector = DefaultSelector (sock )
458
514
try :
459
515
self .on_connect ()
460
516
except RedisError :
@@ -538,9 +594,6 @@ def disconnect(self):
538
594
self ._parser .on_disconnect ()
539
595
if self ._sock is None :
540
596
return
541
- if self ._selector is not None :
542
- self ._selector .close ()
543
- self ._selector = None
544
597
try :
545
598
if os .getpid () == self .pid :
546
599
self ._sock .shutdown (socket .SHUT_RDWR )
@@ -585,11 +638,7 @@ def can_read(self, timeout=0):
585
638
if not sock :
586
639
self .connect ()
587
640
sock = self ._sock
588
- return self ._parser .can_read () or self ._selector .can_read (timeout )
589
-
590
- def is_ready_for_command (self ):
591
- "Check if the connection is ready for a command"
592
- return self ._selector .is_ready_for_command ()
641
+ return self ._parser .can_read (timeout )
593
642
594
643
def read_response (self ):
595
644
"Read the response from a previously sent command"
@@ -963,10 +1012,13 @@ def get_connection(self, command_name, *keys, **options):
963
1012
# a command. if not, the connection was either returned to the
964
1013
# pool before all data has been read or the socket has been
965
1014
# closed. either way, reconnect and verify everything is good.
966
- if not connection .is_ready_for_command ():
1015
+ try :
1016
+ if connection .can_read ():
1017
+ raise ConnectionError ('Connection has data' )
1018
+ except ConnectionError :
967
1019
connection .disconnect ()
968
1020
connection .connect ()
969
- if not connection .is_ready_for_command ():
1021
+ if connection .can_read ():
970
1022
raise ConnectionError ('Connection not ready' )
971
1023
except : # noqa: E722
972
1024
# release the connection back to the pool so that we don't leak it
@@ -1111,10 +1163,13 @@ def get_connection(self, command_name, *keys, **options):
1111
1163
# a command. if not, the connection was either returned to the
1112
1164
# pool before all data has been read or the socket has been
1113
1165
# closed. either way, reconnect and verify everything is good.
1114
- if not connection .is_ready_for_command ():
1166
+ try :
1167
+ if connection .can_read ():
1168
+ raise ConnectionError ('Connection has data' )
1169
+ except ConnectionError :
1115
1170
connection .disconnect ()
1116
1171
connection .connect ()
1117
- if not connection .is_ready_for_command ():
1172
+ if connection .can_read ():
1118
1173
raise ConnectionError ('Connection not ready' )
1119
1174
except : # noqa: E722
1120
1175
# release the connection back to the pool so that we don't leak it
0 commit comments