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(