From f18d1a349592e02c2a6b3f5ccdf238bbd9c94ad3 Mon Sep 17 00:00:00 2001
From: taras <nomail@nomail>
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(
-                        (<char*>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(
+                        (<char*>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",
-                                         <method_t>self._do_read,
+                                         <method_t> 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 <size_t>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 <nomail@nomail>
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 += <Py_ssize_t>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 <nomail@nomail>
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 += <Py_ssize_t>self._sslobj_read(app_buffer_size, app_buffer)
+                last_bytes_read = <Py_ssize_t>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 <nomail@nomail>
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",
-                             <method_t> self._do_read,
-                             None,  # current context is good
-                             self))
+            new_MethodHandle1(self._loop,
+                              "SSLProtocol._do_read",
+                              <method1_t> 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 <Py_ssize_t>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 = <Py_ssize_t>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",
-                                         <method_t> self._do_read,
-                                         None,  # current context is good
-                                         self))
+                        new_MethodHandle1(self._loop,
+                                          "SSLProtocol._do_read",
+                                          <method1_t> 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",
-                                     <method_t>self._do_read,
-                                     context,
-                                     self))
+                    new_MethodHandle1(self._loop,
+                                      "SSLProtocol._do_read",
+                                      <method1_t>self._do_read,
+                                      context,
+                                      self,
+                                      0))
 
     # Flow control for reads from SSL socket
 

From 473214bbbb5e55ec90a557a5017b9d189981e49e Mon Sep 17 00:00:00 2001
From: taras <nomail@nomail>
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",
-                              <method1_t> self._do_read,
+            new_MethodHandle(self._loop,
+                             "SSLProtocol._do_read",
+                             <method_t> 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 = (<Py_ssize_t>self._incoming.pending
+                                        + <Py_ssize_t>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 <Py_ssize_t>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 = <Py_ssize_t>self._sslobj_read(app_buffer_size, app_buffer)
+                last_bytes_read = <Py_ssize_t>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",
-                                          <method1_t> self._do_read,
-                                          None,  # current context is good
-                                          self,
-                                          0))
+                        new_MethodHandle(self._loop,
+                                         "SSLProtocol._do_read",
+                                         <method_t> self._do_read,
+                                         None,  # current context is good
+                                         self))
                     break
+
+                total_pending = (<Py_ssize_t>self._incoming.pending +
+                                 <Py_ssize_t>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 <size_t>self._incoming.pending > 0:
+            while (<size_t>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",
-                                      <method1_t>self._do_read,
-                                      context,
-                                      self,
-                                      0))
+                    new_MethodHandle(self._loop,
+                                     "SSLProtocol._do_read",
+                                     <method_t>self._do_read,
+                                     context,
+                                     self))
 
     # Flow control for reads from SSL socket
 

From 62272e1067505ce0553730f9280e9572580efaa0 Mon Sep 17 00:00:00 2001
From: taras <nomail@nomail>
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 <nomail@nomail>
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 (<size_t>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 <nomail@nomail>
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 (<size_t>self._incoming.pending > 0 or
-                   self._sslobj_pending() > 0):
+            while (<Py_ssize_t>self._incoming.pending > 0 or
+                   <Py_ssize_t>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 <nomail@nomail>
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",
                              <method_t> self._do_read,
-                              None,  # current context is good
+                             None,  # current context is good
                              self))
 
     # Shutdown flow