From 9836c42b2626edd60a6f92b97bc866e0e619cc36 Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Mon, 9 Nov 2020 00:44:18 +0100 Subject: [PATCH] Retry on `WANT_WRITE` & `WANT_READ` in `sendall()` This change introduces retries in `OpenSSL.SSL.Connection.sendall()` when `WANT_WRITE_ERROR` or `WANT_READ_ERROR` happen. It relies on `SSL_MODE_ENABLE_PARTIAL_WRITE` being set on the context, that changes the mode of `SSL_write()` to return errors only if zero bytes has been sent making it safe to retry in these cases. Ideally, the calling code is supposed to `poll()`/`select()` the socket to know when it's okay to attempt the next retry (hence it is readable or writable) but it's not available in the `sendall()` method and just retrying the operation is good enough. Fixes #176 Refs: * http://openssl.6102.n7.nabble.com/SSL-MODE-ACCEPT-MOVING-WRITE-BUFFER-td6421.html * https://stackoverflow.com/a/28992313/595220 * https://www.openssl.org/docs/manmaster/man3/SSL_write.html * https://stackoverflow.com/a/20817394/595220 --- CHANGELOG.rst | 4 ++++ src/OpenSSL/SSL.py | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6e23770d..6305898c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -225,6 +225,10 @@ Deprecations: Changes: ^^^^^^^^ +- Fixed the inability of ``OpenSSL.SSL.Connection.sendall()`` to + keep with sending data over the wire after ``SSL_ERROR_WANT_READ`` + or ``SSL_ERROR_WANT_WRITE`` is returned by ``SSL_write()``. + `#176 `_ - Added a new optional ``chain`` parameter to ``OpenSSL.crypto.X509StoreContext()`` where additional untrusted certificates can be specified to help chain building. `#948 `_ diff --git a/src/OpenSSL/SSL.py b/src/OpenSSL/SSL.py index 4db5240e..2ac897f7 100644 --- a/src/OpenSSL/SSL.py +++ b/src/OpenSSL/SSL.py @@ -2053,7 +2053,14 @@ def sendall(self, buf, flags=0): result = _lib.SSL_write( self._ssl, data + total_sent, min(left_to_send, 2147483647) ) - self._raise_ssl_error(self._ssl, result) + try: + self._raise_ssl_error(self._ssl, result) + except (WantReadError, WantWriteError): + # NOTE: The use of SSL_MODE_ENABLE_PARTIAL_WRITE + # NOTE: above guarantees that in case of failure + # NOTE: no bytes have been written so we don't need + # NOTE: to update the counters, just need to retry. + continue total_sent += result left_to_send -= result