Skip to content

Commit 42db18c

Browse files
committed
Added handshake_shutdown_timeout and test
See also python/cpython#4402
1 parent 41ae455 commit 42db18c

File tree

11 files changed

+221
-35
lines changed

11 files changed

+221
-35
lines changed

tests/test_tcp.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import asyncio
22
import asyncio.sslproto
33
import gc
4+
import os
5+
import select
46
import socket
57
import unittest.mock
68
import uvloop
@@ -2128,6 +2130,107 @@ def run(coro):
21282130
with self._silence_eof_received_warning():
21292131
run(client_sock)
21302132

2133+
def test_shutdown_timeout(self):
2134+
if self.implementation == 'asyncio':
2135+
raise unittest.SkipTest()
2136+
2137+
CNT = 0 # number of clients that were successful
2138+
TOTAL_CNT = 25 # total number of clients that test will create
2139+
TIMEOUT = 10.0 # timeout for this test
2140+
2141+
A_DATA = b'A' * 1024 * 1024
2142+
2143+
sslctx = self._create_server_ssl_context(self.ONLYCERT, self.ONLYKEY)
2144+
client_sslctx = self._create_client_ssl_context()
2145+
2146+
clients = []
2147+
2148+
async def handle_client(reader, writer):
2149+
nonlocal CNT
2150+
2151+
data = await reader.readexactly(len(A_DATA))
2152+
self.assertEqual(data, A_DATA)
2153+
writer.write(b'OK')
2154+
await writer.drain()
2155+
writer.close()
2156+
with self.assertRaisesRegex(TimeoutError,
2157+
'SSL shutdown timed out'):
2158+
await reader.read()
2159+
CNT += 1
2160+
2161+
async def test_client(addr):
2162+
fut = asyncio.Future(loop=self.loop)
2163+
2164+
def prog(sock):
2165+
try:
2166+
sock.starttls(client_sslctx)
2167+
sock.connect(addr)
2168+
sock.send(A_DATA)
2169+
2170+
data = sock.recv_all(2)
2171+
self.assertEqual(data, b'OK')
2172+
2173+
data = sock.recv(1024)
2174+
self.assertEqual(data, b'')
2175+
2176+
fd = sock.detach()
2177+
try:
2178+
select.select([fd], [], [], 3)
2179+
finally:
2180+
os.close(fd)
2181+
2182+
except Exception as ex:
2183+
self.loop.call_soon_threadsafe(fut.set_exception, ex)
2184+
else:
2185+
self.loop.call_soon_threadsafe(fut.set_result, None)
2186+
2187+
client = self.tcp_client(prog)
2188+
client.start()
2189+
clients.append(client)
2190+
2191+
await fut
2192+
2193+
async def start_server():
2194+
extras = {}
2195+
if self.implementation != 'asyncio' or self.PY37:
2196+
extras['ssl_handshake_timeout'] = 10.0
2197+
if self.implementation != 'asyncio': # or self.PY38
2198+
extras['ssl_shutdown_timeout'] = 0.5
2199+
2200+
srv = await asyncio.start_server(
2201+
handle_client,
2202+
'127.0.0.1', 0,
2203+
family=socket.AF_INET,
2204+
ssl=sslctx,
2205+
loop=self.loop,
2206+
**extras)
2207+
2208+
try:
2209+
srv_socks = srv.sockets
2210+
self.assertTrue(srv_socks)
2211+
2212+
addr = srv_socks[0].getsockname()
2213+
2214+
tasks = []
2215+
for _ in range(TOTAL_CNT):
2216+
tasks.append(test_client(addr))
2217+
2218+
await asyncio.wait_for(
2219+
asyncio.gather(*tasks, loop=self.loop),
2220+
TIMEOUT, loop=self.loop)
2221+
2222+
finally:
2223+
self.loop.call_soon(srv.close)
2224+
await srv.wait_closed()
2225+
2226+
with self._silence_eof_received_warning():
2227+
self.loop.run_until_complete(start_server())
2228+
2229+
self.assertEqual(CNT, TOTAL_CNT)
2230+
2231+
for client in clients:
2232+
client.stop()
2233+
21312234

21322235
class Test_UV_TCPSSL(_TestSSL, tb.UVTestCase):
21332236
pass

uvloop/handles/pipe.pxd

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ cdef class UnixServer(UVStreamServer):
44

55
@staticmethod
66
cdef UnixServer new(Loop loop, object protocol_factory, Server server,
7-
object ssl, object ssl_handshake_timeout)
7+
object ssl,
8+
object ssl_handshake_timeout,
9+
object ssl_shutdown_timeout)
810

911

1012
cdef class UnixTransport(UVStream):

uvloop/handles/pipe.pyx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,14 @@ cdef class UnixServer(UVStreamServer):
3939

4040
@staticmethod
4141
cdef UnixServer new(Loop loop, object protocol_factory, Server server,
42-
object ssl, object ssl_handshake_timeout):
42+
object ssl,
43+
object ssl_handshake_timeout,
44+
object ssl_shutdown_timeout):
4345

4446
cdef UnixServer handle
4547
handle = UnixServer.__new__(UnixServer)
4648
handle._init(loop, protocol_factory, server,
47-
ssl, ssl_handshake_timeout)
49+
ssl, ssl_handshake_timeout, ssl_shutdown_timeout)
4850
__pipe_init_uv_handle(<UVStream>handle, loop)
4951
return handle
5052

uvloop/handles/streamserver.pxd

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@ cdef class UVStreamServer(UVSocketHandle):
22
cdef:
33
object ssl
44
object ssl_handshake_timeout
5+
object ssl_shutdown_timeout
56
object protocol_factory
67
bint opened
78
Server _server
89

910
# All "inline" methods are final
1011

1112
cdef inline _init(self, Loop loop, object protocol_factory,
12-
Server server, object ssl, object ssl_handshake_timeout)
13+
Server server, object ssl,
14+
object ssl_handshake_timeout,
15+
object ssl_shutdown_timeout)
1316

1417
cdef inline _mark_as_open(self)
1518

uvloop/handles/streamserver.pyx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@ cdef class UVStreamServer(UVSocketHandle):
66
self._server = None
77
self.ssl = None
88
self.ssl_handshake_timeout = None
9+
self.ssl_shutdown_timeout = None
910
self.protocol_factory = None
1011

1112
cdef inline _init(self, Loop loop, object protocol_factory,
12-
Server server, object ssl, object ssl_handshake_timeout):
13+
Server server, object ssl,
14+
object ssl_handshake_timeout,
15+
object ssl_shutdown_timeout):
1316

1417
if ssl is not None:
1518
if not isinstance(ssl, ssl_SSLContext):
@@ -20,9 +23,13 @@ cdef class UVStreamServer(UVSocketHandle):
2023
if ssl_handshake_timeout is not None:
2124
raise ValueError(
2225
'ssl_handshake_timeout is only meaningful with ssl')
26+
if ssl_shutdown_timeout is not None:
27+
raise ValueError(
28+
'ssl_shutdown_timeout is only meaningful with ssl')
2329

2430
self.ssl = ssl
2531
self.ssl_handshake_timeout = ssl_handshake_timeout
32+
self.ssl_shutdown_timeout = ssl_shutdown_timeout
2633

2734
self._start_init(loop)
2835
self.protocol_factory = protocol_factory
@@ -67,7 +74,8 @@ cdef class UVStreamServer(UVSocketHandle):
6774
waiter,
6875
server_side=True,
6976
server_hostname=None,
70-
ssl_handshake_timeout=self.ssl_handshake_timeout)
77+
ssl_handshake_timeout=self.ssl_handshake_timeout,
78+
ssl_shutdown_timeout=self.ssl_shutdown_timeout)
7179

7280
client = self._make_new_transport(ssl_protocol, None)
7381

uvloop/handles/tcp.pxd

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ cdef class TCPServer(UVStreamServer):
44
@staticmethod
55
cdef TCPServer new(Loop loop, object protocol_factory, Server server,
66
object ssl, unsigned int flags,
7-
object ssl_handshake_timeout)
7+
object ssl_handshake_timeout,
8+
object ssl_shutdown_timeout)
89

910

1011
cdef class TCPTransport(UVStream):

uvloop/handles/tcp.pyx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,13 @@ cdef class TCPServer(UVStreamServer):
5959
@staticmethod
6060
cdef TCPServer new(Loop loop, object protocol_factory, Server server,
6161
object ssl, unsigned int flags,
62-
object ssl_handshake_timeout):
62+
object ssl_handshake_timeout,
63+
object ssl_shutdown_timeout):
6364

6465
cdef TCPServer handle
6566
handle = TCPServer.__new__(TCPServer)
6667
handle._init(loop, protocol_factory, server,
67-
ssl, ssl_handshake_timeout)
68+
ssl, ssl_handshake_timeout, ssl_shutdown_timeout)
6869
__tcp_init_uv_handle(<UVStream>handle, loop, flags)
6970
return handle
7071

uvloop/includes/consts.pxi

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,6 @@ DEF LOG_THRESHOLD_FOR_CONNLOST_WRITES = 5
1717
# Number of seconds to wait for SSL handshake to complete
1818
# The default timeout matches that of Nginx.
1919
DEF SSL_HANDSHAKE_TIMEOUT = 60.0
20+
# Number of seconds to wait for SSL shutdown to complete
21+
# The default timeout mimics lingering_time
22+
DEF SSL_SHUTDOWN_TIMEOUT = 30.0

uvloop/loop.pxd

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,8 @@ cdef class Loop:
168168
object ssl,
169169
bint reuse_port,
170170
object backlog,
171-
object ssl_handshake_timeout)
171+
object ssl_handshake_timeout,
172+
object ssl_shutdown_timeout)
172173

173174
cdef _track_transport(self, UVBaseTransport transport)
174175
cdef _fileobj_to_fd(self, fileobj)

0 commit comments

Comments
 (0)