@@ -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