From 92a2ef90b5ede4763632b8b2309ac7eac88ddee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dvo=C5=99=C3=A1k=20V=C3=A1clav?= Date: Mon, 2 Nov 2020 13:51:56 +0100 Subject: [PATCH 1/2] fixes #245 --- cheroot/makefile.py | 11 ++++++++++- cheroot/ssl/pyopenssl.py | 18 ++++++++++++++---- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/cheroot/makefile.py b/cheroot/makefile.py index 77878c13b7..a6a6c90b2d 100644 --- a/cheroot/makefile.py +++ b/cheroot/makefile.py @@ -24,13 +24,22 @@ def write(self, b): self._flush_unlocked() return len(b) + def _safe_call(self, is_reader, call, *args, **kwargs): + """Call the supplied callable with retries, as needed. + + Method to be overridden in subclasses/mix-ins. + """ + return call(*args, **kwargs) + def _flush_unlocked(self): self._checkClosed('flush of closed file') while self._write_buf: try: # ssl sockets only except 'bytes', not bytearrays # so perhaps we should conditionally wrap this for perf? - n = self.raw.write(bytes(self._write_buf)) + n = self._safe_call( + False, self.raw.write, bytes(self._write_buf), + ) except io.BlockingIOError as e: n = e.characters_written del self._write_buf[:n] diff --git a/cheroot/ssl/pyopenssl.py b/cheroot/ssl/pyopenssl.py index 548200f7d8..a18d0150da 100644 --- a/cheroot/ssl/pyopenssl.py +++ b/cheroot/ssl/pyopenssl.py @@ -145,14 +145,24 @@ def readline(self, size=-1): size, ) - def sendall(self, *args, **kwargs): - """Send whole message to the socket.""" + def read(self, *args, **kwargs): + """Read from the wrapped socket, with retry.""" return self._safe_call( - False, - super(SSLFileobjectMixin, self).sendall, + True, + super(SSLFileobjectMixin, self).read, *args, **kwargs ) + def sendall(self, *args, **kwargs): + """Send whole message to the socket. Unsupported, do not use.""" + # Not supported due to https://github.com/pyca/pyopenssl/issues/176. + # Until that bug is fixed, sendall() may throw SSL.WantWriteError, but + # there is no correct way to retry the call because we don't know how + # many bytes were already transmitted. We could work around this by + # reimplementing sendall() using send(), but we don't actually use + # sendall() anywhere. + raise NotImplementedError('sendall() is unsupported by pyOpenSSL') + def send(self, *args, **kwargs): """Send some part of message to the socket.""" return self._safe_call( From 6fe20d2fd4a979036d6e56943bbba0c035943f62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dvo=C5=99=C3=A1k=20V=C3=A1clav?= Date: Wed, 4 Nov 2020 16:28:27 +0100 Subject: [PATCH 2/2] undo sendall() change unrelated to the bug fix --- cheroot/ssl/pyopenssl.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/cheroot/ssl/pyopenssl.py b/cheroot/ssl/pyopenssl.py index a18d0150da..a4e3920e55 100644 --- a/cheroot/ssl/pyopenssl.py +++ b/cheroot/ssl/pyopenssl.py @@ -146,7 +146,7 @@ def readline(self, size=-1): ) def read(self, *args, **kwargs): - """Read from the wrapped socket, with retry.""" + """Read from the wrapped socket.""" return self._safe_call( True, super(SSLFileobjectMixin, self).read, @@ -154,14 +154,12 @@ def read(self, *args, **kwargs): ) def sendall(self, *args, **kwargs): - """Send whole message to the socket. Unsupported, do not use.""" - # Not supported due to https://github.com/pyca/pyopenssl/issues/176. - # Until that bug is fixed, sendall() may throw SSL.WantWriteError, but - # there is no correct way to retry the call because we don't know how - # many bytes were already transmitted. We could work around this by - # reimplementing sendall() using send(), but we don't actually use - # sendall() anywhere. - raise NotImplementedError('sendall() is unsupported by pyOpenSSL') + """Send whole message to the socket.""" + return self._safe_call( + False, + super(SSLFileobjectMixin, self).sendall, + *args, **kwargs + ) def send(self, *args, **kwargs): """Send some part of message to the socket."""