Skip to content

Commit f88ae92

Browse files
committed
Split ssl test into another case, test_remote_shutdown_receives_trailing_data_on_slow_socket
1 parent c326506 commit f88ae92

File tree

1 file changed

+131
-0
lines changed

1 file changed

+131
-0
lines changed

Lib/test/test_asyncio/test_ssl.py

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1319,6 +1319,137 @@ def test_remote_shutdown_receives_trailing_data(self):
13191319
client_sslctx = self._create_client_ssl_context()
13201320
future = None
13211321

1322+
def server(sock):
1323+
incoming = ssl.MemoryBIO()
1324+
outgoing = ssl.MemoryBIO()
1325+
sslobj = sslctx.wrap_bio(incoming, outgoing, server_side=True)
1326+
1327+
while True:
1328+
try:
1329+
sslobj.do_handshake()
1330+
except ssl.SSLWantReadError:
1331+
if outgoing.pending:
1332+
sock.send(outgoing.read())
1333+
incoming.write(sock.recv(16384))
1334+
else:
1335+
if outgoing.pending:
1336+
sock.send(outgoing.read())
1337+
break
1338+
1339+
while True:
1340+
try:
1341+
data = sslobj.read(4)
1342+
except ssl.SSLWantReadError:
1343+
incoming.write(sock.recv(16384))
1344+
else:
1345+
break
1346+
1347+
self.assertEqual(data, b'ping')
1348+
sslobj.write(b'pong')
1349+
sock.send(outgoing.read())
1350+
1351+
time.sleep(0.2) # wait for the peer to fill its backlog
1352+
1353+
# send close_notify but don't wait for response
1354+
with self.assertRaises(ssl.SSLWantReadError):
1355+
sslobj.unwrap()
1356+
sock.send(outgoing.read())
1357+
1358+
# should receive all data
1359+
data_len = 0
1360+
while True:
1361+
try:
1362+
chunk = len(sslobj.read(16384))
1363+
data_len += chunk
1364+
except ssl.SSLWantReadError:
1365+
incoming.write(sock.recv(16384))
1366+
except ssl.SSLZeroReturnError:
1367+
break
1368+
1369+
self.assertEqual(data_len, CHUNK * SIZE)
1370+
1371+
# verify that close_notify is received
1372+
sslobj.unwrap()
1373+
1374+
sock.close()
1375+
1376+
def eof_server(sock):
1377+
sock.starttls(sslctx, server_side=True)
1378+
self.assertEqual(sock.recv_all(4), b'ping')
1379+
sock.send(b'pong')
1380+
1381+
time.sleep(0.2) # wait for the peer to fill its backlog
1382+
1383+
# send EOF
1384+
sock.shutdown(socket.SHUT_WR)
1385+
1386+
# should receive all data
1387+
data = sock.recv_all(CHUNK * SIZE)
1388+
self.assertEqual(len(data), CHUNK * SIZE)
1389+
1390+
sock.close()
1391+
1392+
async def client(addr):
1393+
nonlocal future
1394+
future = self.loop.create_future()
1395+
1396+
reader, writer = await asyncio.open_connection(
1397+
*addr,
1398+
ssl=client_sslctx,
1399+
server_hostname='')
1400+
writer.write(b'ping')
1401+
data = await reader.readexactly(4)
1402+
self.assertEqual(data, b'pong')
1403+
1404+
# fill write backlog in a hacky way - renegotiation won't help
1405+
for _ in range(SIZE):
1406+
writer.transport._test__append_write_backlog(b'x' * CHUNK)
1407+
1408+
try:
1409+
data = await reader.read()
1410+
self.assertEqual(data, b'')
1411+
except (BrokenPipeError, ConnectionResetError):
1412+
pass
1413+
1414+
await future
1415+
1416+
writer.close()
1417+
await self.wait_closed(writer)
1418+
1419+
def run(meth):
1420+
def wrapper(sock):
1421+
try:
1422+
meth(sock)
1423+
except Exception as ex:
1424+
self.loop.call_soon_threadsafe(future.set_exception, ex)
1425+
else:
1426+
self.loop.call_soon_threadsafe(future.set_result, None)
1427+
return wrapper
1428+
1429+
with self.tcp_server(run(server)) as srv:
1430+
self.loop.run_until_complete(client(srv.addr))
1431+
1432+
with self.tcp_server(run(eof_server)) as srv:
1433+
self.loop.run_until_complete(client(srv.addr))
1434+
1435+
def test_remote_shutdown_receives_trailing_data_on_slow_socket(self):
1436+
# This test is the same as test_remote_shutdown_receives_trailing_data,
1437+
# except it simulates a socket that is not able to write data in time,
1438+
# thus triggering different code path in _SelectorSocketTransport.
1439+
# This triggers bug gh-115514, also tested using mocks in
1440+
# test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_buffer_after_close
1441+
# The slow path is triggered here by setting SO_SNDBUF, see code and comment below.
1442+
1443+
CHUNK = 1024 * 128
1444+
SIZE = 32
1445+
1446+
sslctx = self._create_server_ssl_context(
1447+
test_utils.ONLYCERT,
1448+
test_utils.ONLYKEY
1449+
)
1450+
client_sslctx = self._create_client_ssl_context()
1451+
future = None
1452+
13221453
def server(sock):
13231454
incoming = ssl.MemoryBIO()
13241455
outgoing = ssl.MemoryBIO()

0 commit comments

Comments
 (0)