Skip to content

Commit

Permalink
* Bug #974632: _ConnectionDeadError sometimes was escaping the get/set
Browse files Browse the repository at this point in the history
code.  This should return to readline() not raising an exception, except
in the case that it's called from the get/set().  Report from Gary
Poster, proposed patch by Brad Crittenden.

Misc fixes by Brad Crittenden: fixing a docstring, if "port" is set to
any false-like value it will default to 11211.
  • Loading branch information
Sean Reifschneider committed Apr 11, 2012
1 parent 8078ecf commit bacb805
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 14 deletions.
8 changes: 8 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
* Bug #974632: _ConnectionDeadError sometimes was escaping the get/set
code. This should return to readline() not raising an exception, except
in the case that it's called from the get/set(). Report from Gary
Poster, proposed patch by Brad Crittenden.

Misc fixes by Brad Crittenden: fixing a docstring, if "port" is set to
any false-like value it will default to 11211.

Mon, 29 Nov 2011 12:37:32 -0700 Sean Reifschneider <jafo@tummy.com>

* Bug #887765: Interrupted connection to memcache server can cause
Expand Down
38 changes: 24 additions & 14 deletions memcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ def __init__(self, servers, debug=0, pickleProtocol=0,
@param cache_cas: (default False) If true, cas operations will be
cached. WARNING: This cache is not expired internally, if you have
a long-running process you will need to expire it manually via
"client.reset_cas(), or the cache can grow unlimited.
client.reset_cas(), or the cache can grow unlimited.
@param server_max_key_length: (default SERVER_MAX_KEY_LENGTH)
Data that is larger than this will not be sent to the server.
@param server_max_value_length: (default SERVER_MAX_VALUE_LENGTH)
Expand Down Expand Up @@ -800,7 +800,8 @@ def _unsafe_set():

try:
server.send_cmd(fullcmd)
return(server.expect("STORED") == "STORED")
return(server.expect("STORED", raise_exception=True)
== "STORED")
except socket.error, msg:
if isinstance(msg, tuple): msg = msg[1]
server.mark_dead(msg)
Expand Down Expand Up @@ -831,18 +832,20 @@ def _unsafe_get():
rkey = flags = rlen = cas_id = None

if cmd == 'gets':
rkey, flags, rlen, cas_id, = self._expect_cas_value(server)
rkey, flags, rlen, cas_id, = self._expect_cas_value(server,
raise_exception=True)
if rkey and self.cache_cas:
self.cas_ids[rkey] = cas_id
else:
rkey, flags, rlen, = self._expectvalue(server)
rkey, flags, rlen, = self._expectvalue(server,
raise_exception=True)

if not rkey:
return None
try:
value = self._recv_value(server, flags, rlen)
finally:
server.expect("END")
server.expect("END", raise_exception=True)
except (_Error, socket.error), msg:
if isinstance(msg, tuple): msg = msg[1]
server.mark_dead(msg)
Expand Down Expand Up @@ -948,19 +951,19 @@ def get_multi(self, keys, key_prefix=''):
server.mark_dead(msg)
return retvals

def _expect_cas_value(self, server, line=None):
def _expect_cas_value(self, server, line=None, raise_exception=False):
if not line:
line = server.readline()
line = server.readline(raise_exception)

if line and line[:5] == 'VALUE':
resp, rkey, flags, len, cas_id = line.split()
return (rkey, int(flags), int(len), int(cas_id))
else:
return (None, None, None, None)

def _expectvalue(self, server, line=None):
def _expectvalue(self, server, line=None, raise_exception=False):
if not line:
line = server.readline()
line = server.readline(raise_exception)

if line and line[:5] == 'VALUE':
resp, rkey, flags, len = line.split()
Expand Down Expand Up @@ -1064,7 +1067,7 @@ def __init__(self, host, debug=0, dead_retry=_DEAD_RETRY,
else:
self.family = socket.AF_INET
self.ip = hostData['host']
self.port = int(hostData.get('port', 11211))
self.port = int(hostData.get('port') or 11211)
self.address = ( self.ip, self.port )

self.deaduntil = 0
Expand Down Expand Up @@ -1130,7 +1133,11 @@ def send_cmds(self, cmds):
""" cmds already has trailing \r\n's applied """
self.socket.sendall(cmds)

def readline(self):
def readline(self, raise_exception=False):
"""Read a line and return it. If "raise_exception" is set,
raise _ConnectionDeadError if the read fails, otherwise return
an empty string.
"""
buf = self.buffer
recv = self.socket.recv
while True:
Expand All @@ -1141,14 +1148,17 @@ def readline(self):
if not data:
# connection close, let's kill it and raise
self.close_socket()
raise _ConnectionDeadError()
if raise_exception:
raise _ConnectionDeadError()
else:
return ''

buf += data
self.buffer = buf[index+2:]
return buf[:index]

def expect(self, text):
line = self.readline()
def expect(self, text, raise_exception=False):
line = self.readline(raise_exception)
if line != text:
self.debuglog("while expecting '%s', got unexpected response '%s'"
% (text, line))
Expand Down

0 comments on commit bacb805

Please sign in to comment.