Skip to content

Commit

Permalink
Retry on WANT_WRITE & WANT_READ in sendall()
Browse files Browse the repository at this point in the history
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 pyca#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
  • Loading branch information
webknjaz committed Nov 9, 2020
1 parent 124a013 commit c945229
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,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 <https://github.com/pyca/pyopenssl/pull/176>`_
- Added a new optional ``chain`` parameter to ``OpenSSL.crypto.X509StoreContext()``
where additional untrusted certificates can be specified to help chain building.
`#948 <https://github.com/pyca/pyopenssl/pull/948>`_
Expand Down
9 changes: 8 additions & 1 deletion src/OpenSSL/SSL.py
Original file line number Diff line number Diff line change
Expand Up @@ -1670,7 +1670,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 guarrantees 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

Expand Down

0 comments on commit c945229

Please sign in to comment.