Skip to content

Commit bc4d1b5

Browse files
vstinnerjstasiak
authored andcommitted
gh-274: Handle blocking I/O errors in GreenSocket
Fix recv_into(), recvfrom(), recvfrom_into() and sendto() methods of GreenSocket to handle blocking I/O errors (ex: BlockingIOError on Python 3). Even if the trampoline was called, the socket method can still fails with an I/O errors for various reasons (see manual pages of the C functions for examples). Ref: #274
1 parent 0e1030e commit bc4d1b5

File tree

1 file changed

+35
-31
lines changed

1 file changed

+35
-31
lines changed

eventlet/greenio/base.py

Lines changed: 35 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -304,59 +304,61 @@ def makeGreenFile(self, *args, **kw):
304304
"makefile instead", DeprecationWarning, stacklevel=2)
305305
return self.makefile(*args, **kw)
306306

307-
def recv(self, buflen, flags=0):
307+
def _read_trampoline(self):
308+
self._trampoline(
309+
self.fd,
310+
read=True,
311+
timeout=self.gettimeout(),
312+
timeout_exc=socket.timeout("timed out"))
313+
314+
def _recv_loop(self, recv_meth, *args):
308315
fd = self.fd
309316
if self.act_non_blocking:
310-
return fd.recv(buflen, flags)
317+
return recv_meth(*args)
318+
311319
while True:
312320
try:
313-
return fd.recv(buflen, flags)
321+
# recv: bufsize=0?
322+
# recv_into: buffer is empty?
323+
if not args[0]:
324+
self._read_trampoline()
325+
return recv_meth(*args)
314326
except socket.error as e:
315327
if get_errno(e) in SOCKET_BLOCKING:
316328
pass
317329
elif get_errno(e) in SOCKET_CLOSED:
318330
return b''
319331
else:
320332
raise
333+
321334
try:
322-
self._trampoline(
323-
fd,
324-
read=True,
325-
timeout=self.gettimeout(),
326-
timeout_exc=socket.timeout("timed out"))
335+
self._read_trampoline()
327336
except IOClosed as e:
328337
# Perhaps we should return '' instead?
329338
raise EOFError()
330339

331-
def recvfrom(self, *args):
332-
if not self.act_non_blocking:
333-
self._trampoline(self.fd, read=True, timeout=self.gettimeout(),
334-
timeout_exc=socket.timeout("timed out"))
335-
return self.fd.recvfrom(*args)
340+
def recv(self, bufsize, flags=0):
341+
return self._recv_loop(self.fd.recv, bufsize, flags)
336342

337-
def recvfrom_into(self, *args):
338-
if not self.act_non_blocking:
339-
self._trampoline(self.fd, read=True, timeout=self.gettimeout(),
340-
timeout_exc=socket.timeout("timed out"))
341-
return self.fd.recvfrom_into(*args)
343+
def recvfrom(self, bufsize, flags=0):
344+
return self._recv_loop(self.fd.recvfrom, bufsize, flags)
342345

343-
def recv_into(self, *args):
344-
if not self.act_non_blocking:
345-
self._trampoline(self.fd, read=True, timeout=self.gettimeout(),
346-
timeout_exc=socket.timeout("timed out"))
347-
return self.fd.recv_into(*args)
346+
def recv_into(self, buffer, nbytes=0, flags=0):
347+
return self._recv_loop(self.fd.recv_into, buffer, nbytes, flags)
348348

349-
def send(self, data, flags=0):
350-
fd = self.fd
349+
def recvfrom_into(self, buffer, nbytes=0, flags=0):
350+
return self._recv_loop(self.fd.recvfrom_into, buffer, nbytes, flags)
351+
352+
def _send_loop(self, send_method, data, *args):
351353
if self.act_non_blocking:
352-
return fd.send(data, flags)
354+
return send_method(data, *args)
353355

354356
# blocking socket behavior - sends all, blocks if the buffer is full
355357
total_sent = 0
356358
len_data = len(data)
357359
while 1:
358360
try:
359-
total_sent += fd.send(data[total_sent:], flags)
361+
total_sent += send_method(data[total_sent:], *args)
360362
except socket.error as e:
361363
eno = get_errno(e)
362364
if eno == errno.ENOTCONN or eno not in SOCKET_BLOCKING:
@@ -373,16 +375,18 @@ def send(self, data, flags=0):
373375

374376
return total_sent
375377

378+
def send(self, data, flags=0):
379+
return self._send_loop(self.fd.send, data, flags)
380+
381+
def sendto(self, data, address, flags=0):
382+
return self._send_loop(self.fd.sendto, data, address, flags)
383+
376384
def sendall(self, data, flags=0):
377385
tail = self.send(data, flags)
378386
len_data = len(data)
379387
while tail < len_data:
380388
tail += self.send(data[tail:], flags)
381389

382-
def sendto(self, *args):
383-
self._trampoline(self.fd, write=True)
384-
return self.fd.sendto(*args)
385-
386390
def setblocking(self, flag):
387391
if flag:
388392
self.act_non_blocking = False

0 commit comments

Comments
 (0)