From f18d1a349592e02c2a6b3f5ccdf238bbd9c94ad3 Mon Sep 17 00:00:00 2001 From: taras Date: Mon, 9 Sep 2024 16:00:09 +0200 Subject: [PATCH 1/9] Refactor and optimize buffered reads --- uvloop/sslproto.pyx | 76 ++++++++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 32 deletions(-) diff --git a/uvloop/sslproto.pyx b/uvloop/sslproto.pyx index 42bb7644..03d4c2e8 100644 --- a/uvloop/sslproto.pyx +++ b/uvloop/sslproto.pyx @@ -719,48 +719,60 @@ cdef class SSLProtocol: cdef _do_read__buffered(self): cdef: - Py_buffer pybuf - bint pybuf_inited = False - size_t wants, offset = 0 - int count = 1 - object buf + Py_ssize_t pending = self._incoming.pending + object app_buffer = self._app_protocol_get_buffer(pending) + Py_ssize_t app_buffer_size = len(app_buffer) + + if app_buffer_size == 0: + return - buf = self._app_protocol_get_buffer(self._get_read_buffer_size()) - wants = len(buf) + cdef: + Py_ssize_t bytes_read + Py_ssize_t total_bytes_read = 0 + Py_buffer pybuf + bint pybuf_initialized = False try: - count = self._sslobj_read(wants, buf) - - if count > 0: - offset = count - if offset < wants: - PyObject_GetBuffer(buf, &pybuf, PyBUF_WRITABLE) - pybuf_inited = True - while offset < wants: - buf = PyMemoryView_FromMemory( - (pybuf.buf) + offset, - wants - offset, + # SSLObject.read may not return all requested data in one go. + # We keep calling read until all pending data is read + # Run test_create_server_ssl_over_ssl to reproduce it + while pending > 0: + if total_bytes_read > 0: + if not pybuf_initialized: + PyObject_GetBuffer(app_buffer, &pybuf, PyBUF_WRITABLE) + pybuf_initialized = True + + app_buffer = PyMemoryView_FromMemory( + (pybuf.buf) + total_bytes_read, + app_buffer_size - total_bytes_read, PyBUF_WRITE) - count = self._sslobj_read(wants - offset, buf) - if count > 0: - offset += count - else: - break - else: + + print(f"calling initial SSLObject.read, wants={app_buffer_size}, pending={self._incoming.pending}") + + bytes_read = self._sslobj_read(app_buffer_size, app_buffer) + total_bytes_read += bytes_read + pending -= bytes_read + + print(f"SSLObject.read returned {bytes_read}") + + # User buffer may not fit all available data. + # In such case we schedule _do_read to run again later + if total_bytes_read == app_buffer_size: self._loop._call_soon_handle( new_MethodHandle(self._loop, "SSLProtocol._do_read", - self._do_read, + self._do_read, None, # current context is good self)) - except ssl_SSLAgainErrors as exc: - pass + break finally: - if pybuf_inited: + if pybuf_initialized: PyBuffer_Release(&pybuf) - if offset > 0: - self._app_protocol_buffer_updated(offset) - if not count: + + if total_bytes_read > 0: + self._app_protocol_buffer_updated(total_bytes_read) + + if self._incoming.eof: # close_notify self._call_eof_received() self._start_shutdown() @@ -772,7 +784,7 @@ cdef class SSLProtocol: bint zero = True, one = False try: - while True: + while self._incoming.pending > 0: chunk = self._sslobj_read(SSL_READ_MAX_SIZE) if not chunk: break From 951e8df134ce4c1d503344a6de4f7c177aa69f61 Mon Sep 17 00:00:00 2001 From: taras Date: Mon, 9 Sep 2024 16:44:15 +0200 Subject: [PATCH 2/9] Refactor ssl buffered reads --- uvloop/sslproto.pyx | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/uvloop/sslproto.pyx b/uvloop/sslproto.pyx index 03d4c2e8..3aae18e0 100644 --- a/uvloop/sslproto.pyx +++ b/uvloop/sslproto.pyx @@ -719,24 +719,39 @@ cdef class SSLProtocol: cdef _do_read__buffered(self): cdef: - Py_ssize_t pending = self._incoming.pending - object app_buffer = self._app_protocol_get_buffer(pending) + object app_buffer = self._app_protocol_get_buffer(self._incoming.pending) Py_ssize_t app_buffer_size = len(app_buffer) if app_buffer_size == 0: return cdef: - Py_ssize_t bytes_read Py_ssize_t total_bytes_read = 0 Py_buffer pybuf bint pybuf_initialized = False try: - # SSLObject.read may not return all requested data in one go. - # We keep calling read until all pending data is read - # Run test_create_server_ssl_over_ssl to reproduce it - while pending > 0: + # SSLObject.read may not return all available data in one go. + # We have to keep calling read until it throw SSLWantReadError. + # However, throwing SSLWantReadError is very expensive + # (checked with python 3.12.5). Maybe it will be optimized later + # but we would like to prevent it as much as possible anyway. + + # One way to do it is to check self._incoming.pending > 0. + # SSLObject.read may still throw SSLWantReadError + # even when self._incoming.pending > 0 but this should happen + # relatively rarely when ssl frame is split up by tcp stack. + + # This optimization works really well especially for peers + # exchanging small messages and wanting to have minimal latency. + + # On a side note: self._incoming.pending means how many data hasn't + # been processed by ssl yes (read: "still encrypted"). The final + # unencrypted data size maybe different. + + # Run test_create_server_ssl_over_ssl to reproduce different cases + # for this method. + while self._incoming.pending > 0: if total_bytes_read > 0: if not pybuf_initialized: PyObject_GetBuffer(app_buffer, &pybuf, PyBUF_WRITABLE) @@ -747,13 +762,7 @@ cdef class SSLProtocol: app_buffer_size - total_bytes_read, PyBUF_WRITE) - print(f"calling initial SSLObject.read, wants={app_buffer_size}, pending={self._incoming.pending}") - - bytes_read = self._sslobj_read(app_buffer_size, app_buffer) - total_bytes_read += bytes_read - pending -= bytes_read - - print(f"SSLObject.read returned {bytes_read}") + total_bytes_read += self._sslobj_read(app_buffer_size, app_buffer) # User buffer may not fit all available data. # In such case we schedule _do_read to run again later @@ -765,6 +774,8 @@ cdef class SSLProtocol: None, # current context is good self)) break + except ssl_SSLAgainErrors as exc: + pass finally: if pybuf_initialized: PyBuffer_Release(&pybuf) From 26ede3c893f226bf2516cf966bbf70ca5eff3c54 Mon Sep 17 00:00:00 2001 From: taras Date: Mon, 9 Sep 2024 17:12:54 +0200 Subject: [PATCH 3/9] Fix tests --- uvloop/sslproto.pyx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/uvloop/sslproto.pyx b/uvloop/sslproto.pyx index 3aae18e0..9f7e0d9b 100644 --- a/uvloop/sslproto.pyx +++ b/uvloop/sslproto.pyx @@ -726,6 +726,7 @@ cdef class SSLProtocol: return cdef: + Py_ssize_t last_bytes_read = -1 Py_ssize_t total_bytes_read = 0 Py_buffer pybuf bint pybuf_initialized = False @@ -762,7 +763,8 @@ cdef class SSLProtocol: app_buffer_size - total_bytes_read, PyBUF_WRITE) - total_bytes_read += self._sslobj_read(app_buffer_size, app_buffer) + last_bytes_read = self._sslobj_read(app_buffer_size, app_buffer) + total_bytes_read += last_bytes_read # User buffer may not fit all available data. # In such case we schedule _do_read to run again later @@ -783,7 +785,9 @@ cdef class SSLProtocol: if total_bytes_read > 0: self._app_protocol_buffer_updated(total_bytes_read) - if self._incoming.eof: + # SSLObject.read() may return 0 instead of throwing SSLWantReadError + # This indicates that we reached EOF + if last_bytes_read == 0: # close_notify self._call_eof_received() self._start_shutdown() From eb7ce66d2960c925b5541f4a5b25ece29e5b8443 Mon Sep 17 00:00:00 2001 From: taras Date: Tue, 10 Sep 2024 00:04:34 +0200 Subject: [PATCH 4/9] WIP: still broken, seems it is not possible cause SSLObject.read is too unpredictable --- uvloop/sslproto.pxd | 4 +-- uvloop/sslproto.pyx | 75 +++++++++++++++++++++++++++------------------ 2 files changed, 47 insertions(+), 32 deletions(-) diff --git a/uvloop/sslproto.pxd b/uvloop/sslproto.pxd index 3da10f00..c2ad566b 100644 --- a/uvloop/sslproto.pxd +++ b/uvloop/sslproto.pxd @@ -114,8 +114,8 @@ cdef class SSLProtocol: # Incoming flow - cdef _do_read(self) - cdef _do_read__buffered(self) + cdef _do_read(self, bint use_pending_size) + cdef _do_read__buffered(self, bint use_pending_size) cdef _do_read__copied(self) cdef _call_eof_received(self, object context=*) diff --git a/uvloop/sslproto.pyx b/uvloop/sslproto.pyx index 9f7e0d9b..7f91c818 100644 --- a/uvloop/sslproto.pyx +++ b/uvloop/sslproto.pyx @@ -375,11 +375,13 @@ cdef class SSLProtocol: self._incoming_write(PyMemoryView_FromMemory( self._ssl_buffer, nbytes, PyBUF_WRITE)) + print(f"added {nbytes} to incoming bio, pending={self._incoming.pending}") + if self._state == DO_HANDSHAKE: self._do_handshake() elif self._state == WRAPPED: - self._do_read() + self._do_read(1) elif self._state == FLUSHING: self._do_flush() @@ -548,11 +550,12 @@ cdef class SSLProtocol: # that the user get a chance to e.g. check ALPN with the transport # before having to handle the first data. self._loop._call_soon_handle( - new_MethodHandle(self._loop, - "SSLProtocol._do_read", - self._do_read, - None, # current context is good - self)) + new_MethodHandle1(self._loop, + "SSLProtocol._do_read", + self._do_read, + None, # current context is good + self, + 0)) # Shutdown flow @@ -700,13 +703,13 @@ cdef class SSLProtocol: # Incoming flow - cdef _do_read(self): + cdef _do_read(self, bint use_pending_size): if self._state != WRAPPED: return try: if not self._app_reading_paused: if self._app_protocol_is_buffer: - self._do_read__buffered() + self._do_read__buffered(use_pending_size) else: self._do_read__copied() if self._write_backlog: @@ -717,11 +720,16 @@ cdef class SSLProtocol: except Exception as ex: self._fatal_error(ex, 'Fatal error on SSL protocol') - cdef _do_read__buffered(self): + cdef _do_read__buffered(self, bint use_pending_size): cdef: - object app_buffer = self._app_protocol_get_buffer(self._incoming.pending) + object buffer_size_hint = \ + self._incoming.pending if use_pending_size else \ + max(16*1024, self._incoming.pending) + object app_buffer = self._app_protocol_get_buffer(buffer_size_hint) Py_ssize_t app_buffer_size = len(app_buffer) + print(f"entered _do_read__buffered, hint={buffer_size_hint}, bufsz={app_buffer_size}, use_pending_size={use_pending_size}") + if app_buffer_size == 0: return @@ -734,14 +742,14 @@ cdef class SSLProtocol: try: # SSLObject.read may not return all available data in one go. # We have to keep calling read until it throw SSLWantReadError. - # However, throwing SSLWantReadError is very expensive - # (checked with python 3.12.5). Maybe it will be optimized later - # but we would like to prevent it as much as possible anyway. + # However, throwing SSLWantReadError is very expensive. + # (checked with python 3.12.5). - # One way to do it is to check self._incoming.pending > 0. - # SSLObject.read may still throw SSLWantReadError - # even when self._incoming.pending > 0 but this should happen - # relatively rarely when ssl frame is split up by tcp stack. + # One way to reduce reliance on SSLWantReadError is to check + # self._incoming.pending > 0. SSLObject.read may still throw + # SSLWantReadError even when self._incoming.pending > 0 but this + # should happen relatively rarely when ssl frame is split up by + # tcp stack. # This optimization works really well especially for peers # exchanging small messages and wanting to have minimal latency. @@ -752,7 +760,7 @@ cdef class SSLProtocol: # Run test_create_server_ssl_over_ssl to reproduce different cases # for this method. - while self._incoming.pending > 0: + while not use_pending_size or self._incoming.pending > 0: if total_bytes_read > 0: if not pybuf_initialized: PyObject_GetBuffer(app_buffer, &pybuf, PyBUF_WRITABLE) @@ -763,21 +771,27 @@ cdef class SSLProtocol: app_buffer_size - total_bytes_read, PyBUF_WRITE) + print(f"call read(app_buffer={len(app_buffer)}), pending={self._incoming.pending}") last_bytes_read = self._sslobj_read(app_buffer_size, app_buffer) total_bytes_read += last_bytes_read + print(f"read(...)={last_bytes_read}, total_read={total_bytes_read}, pending={self._incoming.pending}") + + if last_bytes_read == 0: + break # User buffer may not fit all available data. - # In such case we schedule _do_read to run again later if total_bytes_read == app_buffer_size: + print("reschedule _do_read") self._loop._call_soon_handle( - new_MethodHandle(self._loop, - "SSLProtocol._do_read", - self._do_read, - None, # current context is good - self)) + new_MethodHandle1(self._loop, + "SSLProtocol._do_read", + self._do_read, + None, # current context is good + self, + 0)) break except ssl_SSLAgainErrors as exc: - pass + print(f"SSLAgainErrors: pending={self._incoming.pending}, eof={self._incoming.eof}") finally: if pybuf_initialized: PyBuffer_Release(&pybuf) @@ -907,11 +921,12 @@ cdef class SSLProtocol: self._app_reading_paused = False if self._state == WRAPPED: self._loop._call_soon_handle( - new_MethodHandle(self._loop, - "SSLProtocol._do_read", - self._do_read, - context, - self)) + new_MethodHandle1(self._loop, + "SSLProtocol._do_read", + self._do_read, + context, + self, + 0)) # Flow control for reads from SSL socket From 473214bbbb5e55ec90a557a5017b9d189981e49e Mon Sep 17 00:00:00 2001 From: taras Date: Wed, 11 Sep 2024 17:29:01 +0200 Subject: [PATCH 5/9] Cleanup --- uvloop/sslproto.pxd | 5 ++- uvloop/sslproto.pyx | 92 +++++++++++++++++++++++---------------------- 2 files changed, 51 insertions(+), 46 deletions(-) diff --git a/uvloop/sslproto.pxd b/uvloop/sslproto.pxd index c2ad566b..87fc6a9e 100644 --- a/uvloop/sslproto.pxd +++ b/uvloop/sslproto.pxd @@ -53,6 +53,7 @@ cdef class SSLProtocol: object _sslobj object _sslobj_read object _sslobj_write + object _sslobj_pending object _incoming object _incoming_write object _outgoing @@ -114,8 +115,8 @@ cdef class SSLProtocol: # Incoming flow - cdef _do_read(self, bint use_pending_size) - cdef _do_read__buffered(self, bint use_pending_size) + cdef _do_read(self) + cdef _do_read__buffered(self) cdef _do_read__copied(self) cdef _call_eof_received(self, object context=*) diff --git a/uvloop/sslproto.pyx b/uvloop/sslproto.pyx index 7f91c818..c76addbc 100644 --- a/uvloop/sslproto.pyx +++ b/uvloop/sslproto.pyx @@ -375,13 +375,11 @@ cdef class SSLProtocol: self._incoming_write(PyMemoryView_FromMemory( self._ssl_buffer, nbytes, PyBUF_WRITE)) - print(f"added {nbytes} to incoming bio, pending={self._incoming.pending}") - if self._state == DO_HANDSHAKE: self._do_handshake() elif self._state == WRAPPED: - self._do_read(1) + self._do_read() elif self._state == FLUSHING: self._do_flush() @@ -482,6 +480,7 @@ cdef class SSLProtocol: server_hostname=self._server_hostname) self._sslobj_read = self._sslobj.read self._sslobj_write = self._sslobj.write + self._sslobj_pending = self._sslobj.pending except Exception as ex: self._on_handshake_complete(ex) else: @@ -550,12 +549,11 @@ cdef class SSLProtocol: # that the user get a chance to e.g. check ALPN with the transport # before having to handle the first data. self._loop._call_soon_handle( - new_MethodHandle1(self._loop, - "SSLProtocol._do_read", - self._do_read, + new_MethodHandle(self._loop, + "SSLProtocol._do_read", + self._do_read, None, # current context is good - self, - 0)) + self)) # Shutdown flow @@ -703,13 +701,13 @@ cdef class SSLProtocol: # Incoming flow - cdef _do_read(self, bint use_pending_size): + cdef _do_read(self): if self._state != WRAPPED: return try: if not self._app_reading_paused: if self._app_protocol_is_buffer: - self._do_read__buffered(use_pending_size) + self._do_read__buffered() else: self._do_read__copied() if self._write_backlog: @@ -720,16 +718,16 @@ cdef class SSLProtocol: except Exception as ex: self._fatal_error(ex, 'Fatal error on SSL protocol') - cdef _do_read__buffered(self, bint use_pending_size): + cdef _do_read__buffered(self): cdef: - object buffer_size_hint = \ - self._incoming.pending if use_pending_size else \ - max(16*1024, self._incoming.pending) - object app_buffer = self._app_protocol_get_buffer(buffer_size_hint) + Py_ssize_t total_pending = (self._incoming.pending + + self._sslobj_pending()) + # Ask for a little extra in case when decrypted data is bigger than + # original + object app_buffer = self._app_protocol_get_buffer( + total_pending + 256) Py_ssize_t app_buffer_size = len(app_buffer) - print(f"entered _do_read__buffered, hint={buffer_size_hint}, bufsz={app_buffer_size}, use_pending_size={use_pending_size}") - if app_buffer_size == 0: return @@ -742,25 +740,31 @@ cdef class SSLProtocol: try: # SSLObject.read may not return all available data in one go. # We have to keep calling read until it throw SSLWantReadError. - # However, throwing SSLWantReadError is very expensive. - # (checked with python 3.12.5). + # However, throwing SSLWantReadError is very expensive even in + # the master trunk of cpython. + # # One way to reduce reliance on SSLWantReadError is to check - # self._incoming.pending > 0. SSLObject.read may still throw - # SSLWantReadError even when self._incoming.pending > 0 but this - # should happen relatively rarely when ssl frame is split up by - # tcp stack. + # self._incoming.pending > 0 or SSLObject.pending() > 0. + # SSLObject.read may still throw SSLWantReadError even when + # self._incoming.pending > 0 but this should happen relatively + # rarely, only when ssl frame is partially received. # This optimization works really well especially for peers # exchanging small messages and wanting to have minimal latency. - # On a side note: self._incoming.pending means how many data hasn't - # been processed by ssl yes (read: "still encrypted"). The final + # On a side note: + + # self._incoming.pending means how much data hasn't + # been processed by ssl yet (read: "still encrypted"). The final # unencrypted data size maybe different. + # self._sslobj.pending() means how much data has been already + # decrypted and can be directly read with SSLObject.read. + # Run test_create_server_ssl_over_ssl to reproduce different cases # for this method. - while not use_pending_size or self._incoming.pending > 0: + while total_pending > 0: if total_bytes_read > 0: if not pybuf_initialized: PyObject_GetBuffer(app_buffer, &pybuf, PyBUF_WRITABLE) @@ -771,27 +775,27 @@ cdef class SSLProtocol: app_buffer_size - total_bytes_read, PyBUF_WRITE) - print(f"call read(app_buffer={len(app_buffer)}), pending={self._incoming.pending}") - last_bytes_read = self._sslobj_read(app_buffer_size, app_buffer) + last_bytes_read = self._sslobj_read( + app_buffer_size, app_buffer) total_bytes_read += last_bytes_read - print(f"read(...)={last_bytes_read}, total_read={total_bytes_read}, pending={self._incoming.pending}") if last_bytes_read == 0: break # User buffer may not fit all available data. if total_bytes_read == app_buffer_size: - print("reschedule _do_read") self._loop._call_soon_handle( - new_MethodHandle1(self._loop, - "SSLProtocol._do_read", - self._do_read, - None, # current context is good - self, - 0)) + new_MethodHandle(self._loop, + "SSLProtocol._do_read", + self._do_read, + None, # current context is good + self)) break + + total_pending = (self._incoming.pending + + self._sslobj_pending()) except ssl_SSLAgainErrors as exc: - print(f"SSLAgainErrors: pending={self._incoming.pending}, eof={self._incoming.eof}") + pass finally: if pybuf_initialized: PyBuffer_Release(&pybuf) @@ -813,7 +817,8 @@ cdef class SSLProtocol: bint zero = True, one = False try: - while self._incoming.pending > 0: + while (self._incoming.pending > 0 or + self._sslobj.pending() > 0): chunk = self._sslobj_read(SSL_READ_MAX_SIZE) if not chunk: break @@ -921,12 +926,11 @@ cdef class SSLProtocol: self._app_reading_paused = False if self._state == WRAPPED: self._loop._call_soon_handle( - new_MethodHandle1(self._loop, - "SSLProtocol._do_read", - self._do_read, - context, - self, - 0)) + new_MethodHandle(self._loop, + "SSLProtocol._do_read", + self._do_read, + context, + self)) # Flow control for reads from SSL socket From 62272e1067505ce0553730f9280e9572580efaa0 Mon Sep 17 00:00:00 2001 From: taras Date: Wed, 11 Sep 2024 17:33:41 +0200 Subject: [PATCH 6/9] Fix comments --- uvloop/sslproto.pyx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/uvloop/sslproto.pyx b/uvloop/sslproto.pyx index c76addbc..ed628648 100644 --- a/uvloop/sslproto.pyx +++ b/uvloop/sslproto.pyx @@ -742,19 +742,18 @@ cdef class SSLProtocol: # We have to keep calling read until it throw SSLWantReadError. # However, throwing SSLWantReadError is very expensive even in # the master trunk of cpython. - # + # See https://github.com/python/cpython/issues/123954 # One way to reduce reliance on SSLWantReadError is to check - # self._incoming.pending > 0 or SSLObject.pending() > 0. + # self._incoming.pending > 0 and SSLObject.pending() > 0. # SSLObject.read may still throw SSLWantReadError even when - # self._incoming.pending > 0 but this should happen relatively - # rarely, only when ssl frame is partially received. + # self._incoming.pending > 0 SSLObject.pending() == 0, + # but this should happen relatively rarely, only when ssl frame + # is partially received. # This optimization works really well especially for peers # exchanging small messages and wanting to have minimal latency. - # On a side note: - # self._incoming.pending means how much data hasn't # been processed by ssl yet (read: "still encrypted"). The final # unencrypted data size maybe different. From 7b1f8100d8b92744548393fc779221538a8592fa Mon Sep 17 00:00:00 2001 From: taras Date: Wed, 11 Sep 2024 17:36:55 +0200 Subject: [PATCH 7/9] Cleanup --- uvloop/sslproto.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uvloop/sslproto.pyx b/uvloop/sslproto.pyx index ed628648..457ac272 100644 --- a/uvloop/sslproto.pyx +++ b/uvloop/sslproto.pyx @@ -817,7 +817,7 @@ cdef class SSLProtocol: try: while (self._incoming.pending > 0 or - self._sslobj.pending() > 0): + self._sslobj_pending() > 0): chunk = self._sslobj_read(SSL_READ_MAX_SIZE) if not chunk: break From a73d44ee63ff26bdacef7afdd8051a0492263813 Mon Sep 17 00:00:00 2001 From: taras Date: Wed, 11 Sep 2024 17:40:26 +0200 Subject: [PATCH 8/9] Cleanup --- uvloop/sslproto.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uvloop/sslproto.pyx b/uvloop/sslproto.pyx index 457ac272..19d706f2 100644 --- a/uvloop/sslproto.pyx +++ b/uvloop/sslproto.pyx @@ -816,8 +816,8 @@ cdef class SSLProtocol: bint zero = True, one = False try: - while (self._incoming.pending > 0 or - self._sslobj_pending() > 0): + while (self._incoming.pending > 0 or + self._sslobj_pending() > 0): chunk = self._sslobj_read(SSL_READ_MAX_SIZE) if not chunk: break From 1078fc23c5299278f84fd393a5e7b989c36cf8d7 Mon Sep 17 00:00:00 2001 From: taras Date: Wed, 11 Sep 2024 17:57:56 +0200 Subject: [PATCH 9/9] Remove unintented change --- uvloop/sslproto.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uvloop/sslproto.pyx b/uvloop/sslproto.pyx index 19d706f2..27bdad8a 100644 --- a/uvloop/sslproto.pyx +++ b/uvloop/sslproto.pyx @@ -552,7 +552,7 @@ cdef class SSLProtocol: new_MethodHandle(self._loop, "SSLProtocol._do_read", self._do_read, - None, # current context is good + None, # current context is good self)) # Shutdown flow