Skip to content

Commit

Permalink
Apply requested changes and add SSLSocket.eager_recv
Browse files Browse the repository at this point in the history
  • Loading branch information
Safihre committed Mar 3, 2022
1 parent a2895e4 commit f2b5c05
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 18 deletions.
20 changes: 20 additions & 0 deletions Lib/ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,15 @@ def session_reused(self):
"""Was the client session reused during handshake"""
return self._sslobj.session_reused

@property
def eager_recv(self):
"""If data is read from the socket eagerly, ignoring possible TLS EOF packets."""
return self._sslobj.eager_recv

@eager_recv.setter
def eager_recv(self, eager_recv):
self._sslobj.eager_recv = eager_recv

@property
def server_side(self):
"""Whether this is a server-side socket."""
Expand Down Expand Up @@ -1105,6 +1114,17 @@ def session_reused(self):
if self._sslobj is not None:
return self._sslobj.session_reused

@property
@_sslcopydoc
def eager_recv(self):
if self._sslobj is not None:
return self._sslobj.eager_recv

@eager_recv.setter
def eager_recv(self, eager_recv):
if self._sslobj is not None:
self._sslobj.eager_recv = eager_recv

def dup(self):
raise NotImplementedError("Can't dup() %s instances" %
self.__class__.__name__)
Expand Down
5 changes: 2 additions & 3 deletions Lib/test/test_ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -2354,7 +2354,6 @@ def test_bulk_nonblocking_read(self):
# In nonblocking mode, we should be able to read all four in a single
# drop of the GIL.
size = 65536
trips = []

client_context, server_context, hostname = testing_context()
server = ThreadedEchoServer(context=server_context, chatty=False,
Expand All @@ -2364,7 +2363,7 @@ def test_bulk_nonblocking_read(self):
sock.settimeout(0.0)
s = client_context.wrap_socket(sock, server_hostname=hostname,
do_handshake_on_connect=False)

s.eager_recv = True
with s:
while True:
try:
Expand All @@ -2391,7 +2390,7 @@ def test_bulk_nonblocking_read(self):
return
size -= count

raise AssertionError("All TLS reads were smaller than 16KB")
self.fail("All TLS reads were smaller than 16KB")


@support.requires_resource('network')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
When reading from a nonblocking TLS socket, drop the GIL once to read up to
the entire buffer. Previously we would read at most one TLS record (16 KB).
Patch by Josh Snyder.
Added :attr:`ssl.SSLSocket.eager_recv`, if enabled a :ref:`non-blocking <ssl-nonblocking>`
TLS socket will drop the GIL once to read up to the entire buffer instead of reading at
most TLS record (16 KB). Patch by Josh Snyder and Safihre.
43 changes: 31 additions & 12 deletions Modules/_ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ typedef struct {
PyObject *Socket; /* weakref to socket on which we're layered */
SSL *ssl;
PySSLContext *ctx; /* weakref to SSL context */
int eager_recv;
char shutdown_seen_zero;
enum py_ssl_server_or_client socket_type;
PyObject *owner; /* Python level "owner" passed to servername callback */
Expand Down Expand Up @@ -805,6 +806,7 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
self->Socket = NULL;
self->ctx = sslctx;
Py_INCREF(sslctx);
self->eager_recv = 0;
self->shutdown_seen_zero = 0;
self->owner = NULL;
self->server_hostname = NULL;
Expand Down Expand Up @@ -2130,6 +2132,22 @@ static int PySSL_set_context(PySSLSocket *self, PyObject *value,
return 0;
}

static PyObject *
PySSL_get_eager_recv(PySSLSocket *self, void *c)
{
return PyBool_FromLong(self->eager_recv);
}

static int
PySSL_set_eager_recv(PySSLSocket *self, PyObject *arg, void *c)
{
int eager_recv;
if (!PyArg_Parse(arg, "p", &eager_recv))
return -1;
self->eager_recv = eager_recv;
return 0;
}

PyDoc_STRVAR(PySSL_set_context_doc,
"_setter_context(ctx)\n\
\
Expand Down Expand Up @@ -2444,7 +2462,7 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len,
PyObject *dest = NULL;
char *mem;
size_t count = 0;
size_t got = 0;
size_t readbytes = 0;
int retval;
int sockstate;
_PySSLError err;
Expand Down Expand Up @@ -2509,19 +2527,18 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len,
do {
PySSL_BEGIN_ALLOW_THREADS
do {
retval = SSL_read_ex(self->ssl, mem + got, len, &count);
if(retval <= 0) {
retval = SSL_read_ex(self->ssl, mem + count, len, &readbytes);
if (retval <= 0) {
break;
}

got += count;
len -= count;
} while(nonblocking && len > 0);
count += readbytes;
len -= readbytes;
} while (nonblocking && self->eager_recv && len > 0);
err = _PySSL_errno(retval == 0, self->ssl, retval);
PySSL_END_ALLOW_THREADS
self->err = err;

if(got > 0) {
if (count > 0) {
break;
}

Expand All @@ -2539,7 +2556,7 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len,
} else if (err.ssl == SSL_ERROR_ZERO_RETURN &&
SSL_get_shutdown(self->ssl) == SSL_RECEIVED_SHUTDOWN)
{
got = 0;
count = 0;
goto done;
}
else
Expand All @@ -2555,7 +2572,7 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len,
} while (err.ssl == SSL_ERROR_WANT_READ ||
err.ssl == SSL_ERROR_WANT_WRITE);

if (got == 0) {
if (count == 0) {
PySSL_SetError(self, retval, __FILE__, __LINE__);
goto error;
}
Expand All @@ -2565,11 +2582,11 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len,
done:
Py_XDECREF(sock);
if (!group_right_1) {
_PyBytes_Resize(&dest, got);
_PyBytes_Resize(&dest, count);
return dest;
}
else {
return PyLong_FromSize_t(got);
return PyLong_FromSize_t(count);
}

error:
Expand Down Expand Up @@ -2905,6 +2922,8 @@ PyDoc_STRVAR(PySSL_get_session_reused_doc,
static PyGetSetDef ssl_getsetlist[] = {
{"context", (getter) PySSL_get_context,
(setter) PySSL_set_context, PySSL_set_context_doc},
{"eager_recv", (getter) PySSL_get_eager_recv,
(setter) PySSL_set_eager_recv, NULL},
{"server_side", (getter) PySSL_get_server_side, NULL,
PySSL_get_server_side_doc},
{"server_hostname", (getter) PySSL_get_server_hostname, NULL,
Expand Down

0 comments on commit f2b5c05

Please sign in to comment.