summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Merickel <michael@merickel.org>2022-05-24 22:07:10 -0500
committerGitHub <noreply@github.com>2022-05-24 22:07:10 -0500
commit4f6789b035610e0552738cdc4b35ca809a592d48 (patch)
treea39202a1f4622c7b925245d660cbaab3f178c8be
parent1952050446806113920152805db46edaf3dda8ba (diff)
parentc7a3d7ed70e57aa37b1c2efd6b5365ac75cee2ac (diff)
downloadwaitress-4f6789b035610e0552738cdc4b35ca809a592d48.tar.gz
Merge pull request #377 from Pylons/bugfix/select-closed-socket-race
Bugfix: Retry if a thread closes a socket before we select() on it
-rw-r--r--src/waitress/channel.py18
-rw-r--r--src/waitress/wasyncore.py5
-rw-r--r--tests/test_channel.py4
3 files changed, 15 insertions, 12 deletions
diff --git a/src/waitress/channel.py b/src/waitress/channel.py
index 948b498..eb59dd3 100644
--- a/src/waitress/channel.py
+++ b/src/waitress/channel.py
@@ -126,10 +126,10 @@ class HTTPChannel(wasyncore.dispatcher):
if self.will_close:
self.handle_close()
- def _flush_exception(self, flush):
+ def _flush_exception(self, flush, do_close=True):
if flush:
try:
- return (flush(), False)
+ return (flush(do_close=do_close), False)
except OSError:
if self.adj.log_socket_errors:
self.logger.exception("Socket error")
@@ -240,20 +240,20 @@ class HTTPChannel(wasyncore.dispatcher):
return True
- def _flush_some_if_lockable(self):
+ def _flush_some_if_lockable(self, do_close=True):
# Since our task may be appending to the outbuf, we try to acquire
# the lock, but we don't block if we can't.
if self.outbuf_lock.acquire(False):
try:
- self._flush_some()
+ self._flush_some(do_close=do_close)
if self.total_outbufs_len < self.adj.outbuf_high_watermark:
self.outbuf_lock.notify()
finally:
self.outbuf_lock.release()
- def _flush_some(self):
+ def _flush_some(self, do_close=True):
# Send as much data as possible to our client
sent = 0
@@ -267,7 +267,7 @@ class HTTPChannel(wasyncore.dispatcher):
while outbuflen > 0:
chunk = outbuf.get(self.sendbuf_len)
- num_sent = self.send(chunk)
+ num_sent = self.send(chunk, do_close=do_close)
if num_sent:
outbuf.skip(num_sent, True)
@@ -374,7 +374,9 @@ class HTTPChannel(wasyncore.dispatcher):
self.total_outbufs_len += num_bytes
if self.total_outbufs_len >= self.adj.send_bytes:
- (flushed, exception) = self._flush_exception(self._flush_some)
+ (flushed, exception) = self._flush_exception(
+ self._flush_some, do_close=False
+ )
if (
exception
@@ -392,7 +394,7 @@ class HTTPChannel(wasyncore.dispatcher):
if self.total_outbufs_len > self.adj.outbuf_high_watermark:
with self.outbuf_lock:
- (_, exception) = self._flush_exception(self._flush_some)
+ (_, exception) = self._flush_exception(self._flush_some, do_close=False)
if exception:
# An exception happened while flushing, wake up the main
diff --git a/src/waitress/wasyncore.py b/src/waitress/wasyncore.py
index a879608..b3459e0 100644
--- a/src/waitress/wasyncore.py
+++ b/src/waitress/wasyncore.py
@@ -426,7 +426,7 @@ class dispatcher:
else:
return conn, addr
- def send(self, data):
+ def send(self, data, do_close=True):
try:
result = self.socket.send(data)
return result
@@ -434,7 +434,8 @@ class dispatcher:
if why.args[0] == EWOULDBLOCK:
return 0
elif why.args[0] in _DISCONNECTED:
- self.handle_close()
+ if do_close:
+ self.handle_close()
return 0
else:
raise
diff --git a/tests/test_channel.py b/tests/test_channel.py
index b1c317d..8467ae7 100644
--- a/tests/test_channel.py
+++ b/tests/test_channel.py
@@ -376,7 +376,7 @@ class TestHTTPChannel(unittest.TestCase):
inst.total_outbufs_len = len(inst.outbufs[0])
inst.adj.send_bytes = 1
inst.adj.outbuf_high_watermark = 2
- sock.send = lambda x: False
+ sock.send = lambda x, do_close=True: False
inst.will_close = False
inst.last_activity = 0
result = inst.handle_write()
@@ -453,7 +453,7 @@ class TestHTTPChannel(unittest.TestCase):
buf = DummyHugeOutbuffer()
inst.outbufs = [buf]
- inst.send = lambda *arg: 0
+ inst.send = lambda *arg, do_close: 0
result = inst._flush_some()
# we are testing that _flush_some doesn't raise an OverflowError
# when one of its outbufs has a __len__ that returns gt sys.maxint