Skip to content

Commit eb7387d

Browse files
committed
Fixed clean shutdown and added test.
Fixed https://bugs.python.org/issue30698
1 parent 42db18c commit eb7387d

File tree

2 files changed

+71
-5
lines changed

2 files changed

+71
-5
lines changed

tests/test_tcp.py

+66
Original file line numberDiff line numberDiff line change
@@ -2231,6 +2231,72 @@ async def start_server():
22312231
for client in clients:
22322232
client.stop()
22332233

2234+
def test_shutdown_cleanly(self):
2235+
if self.implementation == 'asyncio':
2236+
raise unittest.SkipTest()
2237+
2238+
CNT = 0
2239+
TOTAL_CNT = 25
2240+
2241+
A_DATA = b'A' * 1024 * 1024
2242+
2243+
sslctx = self._create_server_ssl_context(self.ONLYCERT, self.ONLYKEY)
2244+
client_sslctx = self._create_client_ssl_context()
2245+
2246+
def server(sock):
2247+
sock.starttls(
2248+
sslctx,
2249+
server_side=True)
2250+
2251+
data = sock.recv_all(len(A_DATA))
2252+
self.assertEqual(data, A_DATA)
2253+
sock.send(b'OK')
2254+
2255+
sock.unwrap()
2256+
2257+
sock.close()
2258+
2259+
async def client(addr):
2260+
extras = {}
2261+
if self.implementation != 'asyncio' or self.PY37:
2262+
extras = dict(ssl_handshake_timeout=10.0)
2263+
2264+
reader, writer = await asyncio.open_connection(
2265+
*addr,
2266+
ssl=client_sslctx,
2267+
server_hostname='',
2268+
loop=self.loop,
2269+
**extras)
2270+
2271+
writer.write(A_DATA)
2272+
self.assertEqual(await reader.readexactly(2), b'OK')
2273+
2274+
await writer.wait_closed()
2275+
2276+
nonlocal CNT
2277+
CNT += 1
2278+
2279+
writer.close()
2280+
2281+
def run(coro):
2282+
nonlocal CNT
2283+
CNT = 0
2284+
2285+
with self.tcp_server(server,
2286+
max_clients=TOTAL_CNT,
2287+
backlog=TOTAL_CNT) as srv:
2288+
tasks = []
2289+
for _ in range(TOTAL_CNT):
2290+
tasks.append(coro(srv.addr))
2291+
2292+
self.loop.run_until_complete(
2293+
asyncio.gather(*tasks, loop=self.loop))
2294+
2295+
self.assertEqual(CNT, TOTAL_CNT)
2296+
2297+
with self._silence_eof_received_warning():
2298+
run(client)
2299+
22342300

22352301
class Test_UV_TCPSSL(_TestSSL, tb.UVTestCase):
22362302
pass

uvloop/sslproto.pyx

+5-5
Original file line numberDiff line numberDiff line change
@@ -305,14 +305,13 @@ class SSLProtocol(object):
305305
meaning a regular EOF is received or the connection was
306306
aborted or closed).
307307
"""
308-
if self._state in (_WRAPPED, _SHUTDOWN):
309-
self._loop.call_soon(self._app_protocol.connection_lost, exc)
310-
else:
311-
# Most likely an exception occurred while in SSL handshake.
308+
if self._state == _DO_HANDSHAKE:
312309
# Just mark the app transport as closed so that its __del__
313310
# doesn't complain.
314311
if self._app_transport is not None:
315312
self._app_transport._closed = True
313+
else:
314+
self._loop.call_soon(self._app_protocol.connection_lost, exc)
316315
self._set_state(_UNWRAPPED)
317316
self._transport = None
318317
self._app_transport = None
@@ -498,7 +497,6 @@ class SSLProtocol(object):
498497
if shutdown_exc:
499498
self._fatal_error(shutdown_exc)
500499
else:
501-
self._set_state(_UNWRAPPED)
502500
self._loop.call_soon(self._transport.close)
503501

504502
def _abort(self):
@@ -582,6 +580,7 @@ class SSLProtocol(object):
582580
if offset:
583581
self._app_protocol.buffer_updated(offset)
584582
if not count:
583+
# close_notify
585584
self._start_shutdown()
586585

587586
def _do_read__copied(self):
@@ -601,6 +600,7 @@ class SSLProtocol(object):
601600
if data:
602601
self._app_protocol.data_received(b''.join(data))
603602
if not chunk:
603+
# close_notify
604604
self._start_shutdown()
605605

606606
# Flow control for writes from APP socket

0 commit comments

Comments
 (0)