A few more tests, still incomplete
diff --git a/OpenSSL/SSL.py b/OpenSSL/SSL.py
index 35ecf7c..e4116e2 100644
--- a/OpenSSL/SSL.py
+++ b/OpenSSL/SSL.py
@@ -143,6 +143,16 @@
+class WantWriteError(Error):
+ pass
+
+
+
+class WantX509LookupError(Error):
+ pass
+
+
+
class ZeroReturnError(Error):
pass
@@ -777,21 +787,27 @@
error = _api.SSL_get_error(ssl, result)
if error == _api.SSL_ERROR_WANT_READ:
raise WantReadError()
+ elif error == _api.SSL_ERROR_WANT_WRITE:
+ raise WantWriteError()
elif error == _api.SSL_ERROR_ZERO_RETURN:
raise ZeroReturnError()
- elif error == _api.SSL_ERROR_NONE:
- pass
+ elif error == _api.SSL_ERROR_WANT_X509_LOOKUP:
+ # TODO Untested
+ 1/0
+ raise WantX509LookupError()
elif error == _api.SSL_ERROR_SYSCALL:
if _api.ERR_peek_error() == 0:
if result < 0:
raise SysCallError(
_api.ffi.errno, errorcode[_api.ffi.errno])
else:
- # TODO
- raise Exception("unknown syscall error")
+ raise SysCallError(-1, "Unexpected EOF")
else:
- # TODO
+ # TODO Untested
+ 1/0
_raise_current_error(Error)
+ elif error == _api.SSL_ERROR_NONE:
+ pass
else:
_raise_current_error(Error)
@@ -930,7 +946,23 @@
if _api.BIO_should_retry(bio):
if _api.BIO_should_read(bio):
raise WantReadError()
- 1/0
+ elif _api.BIO_should_write(bio):
+ # TODO Untested
+ 1/0
+ raise WantWriteError()
+ elif _api.BIO_should_io_special(bio):
+ 1/0
+ # TODO Untested. I think io_special means the socket BIO has a
+ # not-yet connected socket.
+ raise ValueError("BIO_should_io_special")
+ else:
+ 1/0
+ # TODO Untested
+ raise ValueError("unknown bio failure")
+ else:
+ 1/0
+ # TODO Untested
+ _raise_current_error(Error)
def bio_read(self, bufsiz):
diff --git a/OpenSSL/crypto.py b/OpenSSL/crypto.py
index 23c533c..2fed4b8 100644
--- a/OpenSSL/crypto.py
+++ b/OpenSSL/crypto.py
@@ -286,8 +286,11 @@
if data_length < 0:
1/0
- result = _api.buffer(result_buffer[0], data_length)[:].decode('utf-8')
- _api.OPENSSL_free(result_buffer[0])
+ try:
+ result = _api.buffer(result_buffer[0], data_length)[:].decode('utf-8')
+ finally:
+ # XXX untested
+ _api.OPENSSL_free(result_buffer[0])
return result
diff --git a/OpenSSL/test/test_ssl.py b/OpenSSL/test/test_ssl.py
index 8fc17a2..1b2f213 100644
--- a/OpenSSL/test/test_ssl.py
+++ b/OpenSSL/test/test_ssl.py
@@ -8,7 +8,7 @@
from gc import collect
from errno import ECONNREFUSED, EINPROGRESS, EWOULDBLOCK, EPIPE
from sys import platform, version_info
-from socket import error, socket
+from socket import SHUT_RDWR, error, socket
from os import makedirs
from os.path import join, dirname
from unittest import main
@@ -33,9 +33,9 @@
SESS_CACHE_NO_INTERNAL_STORE, SESS_CACHE_NO_INTERNAL)
from OpenSSL.SSL import (
- Error, SysCallError, WantReadError, ZeroReturnError, SSLeay_version)
+ Error, SysCallError, WantReadError, WantWriteError, ZeroReturnError)
from OpenSSL.SSL import (
- Context, ContextType, Session, Connection, ConnectionType)
+ Context, ContextType, Session, Connection, ConnectionType, SSLeay_version)
from OpenSSL.test.util import TestCase, bytes, b
from OpenSSL.test.test_crypto import (
@@ -1239,8 +1239,6 @@
"""
Unit tests for :py:obj:`OpenSSL.SSL.Connection`.
"""
- # XXX want_write
- # XXX want_read
# XXX get_peer_certificate -> None
# XXX sock_shutdown
# XXX master_key -> TypeError
@@ -1729,6 +1727,36 @@
self._loopback, clientFactory=makeClient, serverFactory=makeServer)
+ def test_wantWriteError(self):
+ """
+ :py:obj:`Connection` methods which generate output raise
+ :py:obj:`OpenSSL.SSL.WantWriteError` if writing to the connection's BIO
+ fail indicating a should-write state.
+ """
+ client_socket, server_socket = socket_pair()
+ # Fill up the client's send buffer so Connection won't be able to write
+ # anything.
+ msg = 'x' * 1024
+ for i in range(1024):
+ try:
+ client_socket.send(msg)
+ except error as e:
+ if e.errno == EWOULDBLOCK:
+ break
+ raise
+ else:
+ self.fail(
+ "Failed to fill socket buffer, cannot test BIO want write")
+
+ ctx = Context(TLSv1_METHOD)
+ conn = Connection(ctx, client_socket)
+ # Client's speak first, so make it an SSL client
+ conn.set_connect_state()
+ self.assertRaises(WantWriteError, conn.do_handshake)
+
+ # XXX want_read
+
+
class ConnectionGetCipherListTests(TestCase):
"""
@@ -2224,6 +2252,18 @@
self.assertEquals(e.__class__, Error)
+ def test_unexpectedEndOfFile(self):
+ """
+ If the connection is lost before an orderly SSL shutdown occurs,
+ :py:obj:`OpenSSL.SSL.SysCallError` is raised with a message of
+ "Unexpected EOF".
+ """
+ server_conn, client_conn = self._loopback()
+ client_conn.sock_shutdown(SHUT_RDWR)
+ exc = self.assertRaises(SysCallError, server_conn.recv, 1024)
+ self.assertEqual(exc.args, (-1, "Unexpected EOF"))
+
+
def _check_client_ca_list(self, func):
"""
Verify the return value of the :py:obj:`get_client_ca_list` method for server and client connections.
@@ -2435,6 +2475,22 @@
self._check_client_ca_list(set_replaces_add_ca)
+
+class ConnectionBIOTests(TestCase):
+ """
+ Tests for :py:obj:`Connection.bio_read` and :py:obj:`Connection.bio_write`.
+ """
+ def test_wantReadError(self):
+ """
+ :py:obj:`Connection.bio_read` raises :py:obj:`OpenSSL.SSL.WantReadError`
+ if there are no bytes available to be read from the BIO.
+ """
+ ctx = Context(TLSv1_METHOD)
+ conn = Connection(ctx, None)
+ self.assertRaises(WantReadError, conn.bio_read, 1024)
+
+
+
class InfoConstantTests(TestCase):
"""
Tests for assorted constants exposed for use in info callbacks.