Skip to content

Commit 611741a

Browse files
committed
Cythonize sslproto and improve performance.
1 parent 5b06936 commit 611741a

File tree

8 files changed

+371
-171
lines changed

8 files changed

+371
-171
lines changed

examples/bench/echoserver.py

+29-3
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,21 @@ def data_received(self, data):
7474
self.transport.write(data)
7575

7676

77+
class EchoBufferedProtocol(asyncio.BufferedProtocol):
78+
def connection_made(self, transport):
79+
self.transport = transport
80+
self.buffer = bytearray(256 * 1024)
81+
82+
def connection_lost(self, exc):
83+
self.transport = None
84+
85+
def get_buffer(self, sizehint):
86+
return self.buffer
87+
88+
def buffer_updated(self, nbytes):
89+
self.transport.write(self.buffer[:nbytes])
90+
91+
7792
async def print_debug(loop):
7893
while True:
7994
print(chr(27) + "[2J") # clear screen
@@ -89,6 +104,7 @@ async def print_debug(loop):
89104
parser.add_argument('--addr', default='127.0.0.1:25000', type=str)
90105
parser.add_argument('--print', default=False, action='store_true')
91106
parser.add_argument('--ssl', default=False, action='store_true')
107+
parser.add_argument('--buffered', default=False, action='store_true')
92108
args = parser.parse_args()
93109

94110
if args.uvloop:
@@ -140,6 +156,10 @@ async def print_debug(loop):
140156
print('cannot use --stream and --proto simultaneously')
141157
exit(1)
142158

159+
if args.buffered:
160+
print('cannot use --stream and --buffered simultaneously')
161+
exit(1)
162+
143163
print('using asyncio/streams')
144164
if unix:
145165
coro = asyncio.start_unix_server(echo_client_streams,
@@ -155,12 +175,18 @@ async def print_debug(loop):
155175
print('cannot use --stream and --proto simultaneously')
156176
exit(1)
157177

158-
print('using simple protocol')
178+
if args.buffered:
179+
print('using buffered protocol')
180+
protocol = EchoBufferedProtocol
181+
else:
182+
print('using simple protocol')
183+
protocol = EchoProtocol
184+
159185
if unix:
160-
coro = loop.create_unix_server(EchoProtocol, addr,
186+
coro = loop.create_unix_server(protocol, addr,
161187
ssl=server_context)
162188
else:
163-
coro = loop.create_server(EchoProtocol, *addr,
189+
coro = loop.create_server(protocol, *addr,
164190
ssl=server_context)
165191
srv = loop.run_until_complete(coro)
166192
else:

tests/test_tcp.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -2345,7 +2345,7 @@ async def client(addr):
23452345
writer.write(b'I AM WRITING NOWHERE2' * 100)
23462346

23472347
self.assertEqual(
2348-
len(writer.transport._ssl_protocol._write_backlog), 0)
2348+
writer.transport.get_write_buffer_size(), 0)
23492349

23502350
await future
23512351

@@ -2548,8 +2548,7 @@ async def client(addr):
25482548
# fill write backlog in a hacky way - renegotiation won't help
25492549
ssl_protocol = writer.transport._ssl_protocol
25502550
for _ in range(SIZE):
2551-
ssl_protocol._write_backlog.append(b'x' * CHUNK)
2552-
ssl_protocol._write_buffer_size += CHUNK
2551+
ssl_protocol._append_write_backlog(b'x' * CHUNK)
25532552

25542553
try:
25552554
data = await reader.read()

uvloop/includes/python.pxd

+7
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ cdef extern from "Python.h":
1414
int _PyImport_ReleaseLock()
1515
void _Py_RestoreSignals()
1616

17+
object PyMemoryView_FromMemory(char *mem, ssize_t size, int flags)
18+
object PyMemoryView_FromObject(object obj)
19+
int PyMemoryView_Check(object obj)
20+
21+
cdef enum:
22+
PyBUF_WRITE
23+
1724

1825
cdef extern from "includes/compat.h":
1926
ctypedef struct PyContext

uvloop/includes/stdlib.pxi

+1
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ cdef ssl_SSLContext = ssl.SSLContext
120120
cdef ssl_MemoryBIO = ssl.MemoryBIO
121121
cdef ssl_create_default_context = ssl.create_default_context
122122
cdef ssl_SSLError = ssl.SSLError
123+
cdef ssl_SSLAgainErrors = (ssl.SSLWantReadError, ssl.SSLSyscallError)
123124
cdef ssl_CertificateError = ssl.CertificateError
124125
cdef int ssl_SSL_ERROR_WANT_READ = ssl.SSL_ERROR_WANT_READ
125126
cdef int ssl_SSL_ERROR_WANT_WRITE = ssl.SSL_ERROR_WANT_WRITE

uvloop/loop.pxd

+1
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ include "handles/pipe.pxd"
227227
include "handles/process.pxd"
228228

229229
include "request.pxd"
230+
include "sslproto.pxd"
230231

231232
include "handles/udp.pxd"
232233

uvloop/loop.pyx

+7-5
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ from .includes.python cimport PY_VERSION_HEX, \
1818
PyContext, \
1919
PyContext_CopyCurrent, \
2020
PyContext_Enter, \
21-
PyContext_Exit
21+
PyContext_Exit, \
22+
PyMemoryView_FromMemory, PyBUF_WRITE, \
23+
PyMemoryView_FromObject, PyMemoryView_Check
2224

2325
from libc.stdint cimport uint64_t
2426
from libc.string cimport memset, strerror, memcpy
@@ -1566,7 +1568,7 @@ cdef class Loop:
15661568
resume_cb.cancel()
15671569
raise
15681570

1569-
return ssl_protocol._app_transport
1571+
return (<SSLProtocol>ssl_protocol)._app_transport
15701572

15711573
@cython.iterable_coroutine
15721574
async def create_server(self, protocol_factory, host=None, port=None,
@@ -1939,7 +1941,7 @@ cdef class Loop:
19391941
except Exception:
19401942
tr._close()
19411943
raise
1942-
return protocol._app_transport, app_protocol
1944+
return (<SSLProtocol>protocol)._app_transport, app_protocol
19431945
else:
19441946
return tr, protocol
19451947

@@ -2169,7 +2171,7 @@ cdef class Loop:
21692171
except Exception:
21702172
tr._close()
21712173
raise
2172-
return protocol._app_transport, app_protocol
2174+
return (<SSLProtocol>protocol)._app_transport, app_protocol
21732175
else:
21742176
return tr, protocol
21752177

@@ -2533,7 +2535,7 @@ cdef class Loop:
25332535
raise
25342536

25352537
if ssl:
2536-
return protocol._app_transport, protocol
2538+
return (<SSLProtocol>protocol)._app_transport, protocol
25372539
else:
25382540
return transport, protocol
25392541

uvloop/sslproto.pxd

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
cdef enum ProtocolState:
2+
_UNWRAPPED = 0
3+
_DO_HANDSHAKE = 1
4+
_WRAPPED = 2
5+
_FLUSHING = 3
6+
_SHUTDOWN = 4
7+
8+
9+
cdef class SSLProtocol:
10+
cdef:
11+
bint _server_side
12+
str _server_hostname
13+
object _sslcontext
14+
15+
object _extra
16+
17+
object _write_backlog
18+
size_t _write_buffer_size
19+
20+
object _waiter
21+
object _loop
22+
object _app_transport
23+
24+
object _transport
25+
bint _call_connection_made
26+
object _ssl_handshake_timeout
27+
object _ssl_shutdown_timeout
28+
29+
object _sslobj
30+
object _sslobj_read
31+
object _sslobj_write
32+
object _incoming
33+
object _incoming_write
34+
object _outgoing
35+
object _outgoing_read
36+
char* _ssl_buffer
37+
size_t _ssl_buffer_len
38+
object _ssl_buffer_view
39+
ProtocolState _state
40+
size_t _conn_lost
41+
bint _eof_received
42+
43+
bint _ssl_writing_paused
44+
bint _app_reading_paused
45+
46+
size_t _incoming_high_water
47+
size_t _incoming_low_water
48+
bint _ssl_reading_paused
49+
50+
bint _app_writing_paused
51+
size_t _outgoing_high_water
52+
size_t _outgoing_low_water
53+
54+
object _app_protocol
55+
bint _app_protocol_is_buffer
56+
object _app_protocol_get_buffer
57+
object _app_protocol_buffer_updated
58+
59+
object _handshake_start_time
60+
object _handshake_timeout_handle
61+
object _shutdown_timeout_handle
62+
63+
cdef _set_app_protocol(self, app_protocol)
64+
cdef _wakeup_waiter(self, exc=*)
65+
cdef _get_extra_info(self, name, default=*)
66+
cdef _set_state(self, ProtocolState new_state)
67+
68+
# Handshake flow
69+
70+
cdef _start_handshake(self)
71+
cdef _check_handshake_timeout(self)
72+
cdef _do_handshake(self)
73+
cdef _on_handshake_complete(self, handshake_exc)
74+
75+
# Shutdown flow
76+
77+
cdef _start_shutdown(self)
78+
cdef _check_shutdown_timeout(self)
79+
cdef _do_flush(self)
80+
cdef _do_shutdown(self)
81+
cdef _on_shutdown_complete(self, shutdown_exc)
82+
cdef _abort(self, exc)
83+
84+
# Outgoing flow
85+
86+
cdef _write_appdata(self, list_of_data)
87+
cdef _do_write(self)
88+
cdef _process_outgoing(self)
89+
90+
# Incoming flow
91+
92+
cdef _do_read(self)
93+
cdef _do_read__buffered(self)
94+
cdef _do_read__copied(self)
95+
cdef _call_eof_received(self)
96+
97+
# Flow control for writes from APP socket
98+
99+
cdef _control_app_writing(self)
100+
cdef size_t _get_write_buffer_size(self)
101+
cdef _set_write_buffer_limits(self, high=*, low=*)
102+
103+
# Flow control for reads to APP socket
104+
105+
cdef _pause_reading(self)
106+
cdef _resume_reading(self)
107+
108+
# Flow control for reads from SSL socket
109+
110+
cdef _control_ssl_reading(self)
111+
cdef _set_read_buffer_limits(self, high=*, low=*)
112+
cdef size_t _get_read_buffer_size(self)
113+
cdef _fatal_error(self, exc, message=*)

0 commit comments

Comments
 (0)