@@ -555,12 +555,93 @@ class SelectEventLoopTests(BaseSockTestsMixin,
555
555
def create_event_loop (self ):
556
556
return asyncio .SelectorEventLoop ()
557
557
558
+
558
559
class ProactorEventLoopTests (BaseSockTestsMixin ,
559
560
test_utils .TestCase ):
560
561
561
562
def create_event_loop (self ):
562
563
return asyncio .ProactorEventLoop ()
563
564
565
+
566
+ async def _basetest_datagram_send_to_non_listening_address (self ,
567
+ recvfrom ):
568
+ # see:
569
+ # https://github.com/python/cpython/issues/91227
570
+ # https://github.com/python/cpython/issues/88906
571
+ # https://bugs.python.org/issue47071
572
+ # https://bugs.python.org/issue44743
573
+ # The Proactor event loop would fail to receive datagram messages
574
+ # after sending a message to an address that wasn't listening.
575
+
576
+ def create_socket ():
577
+ sock = socket .socket (socket .AF_INET , socket .SOCK_DGRAM )
578
+ sock .setblocking (False )
579
+ sock .bind (('127.0.0.1' , 0 ))
580
+ return sock
581
+
582
+ socket_1 = create_socket ()
583
+ addr_1 = socket_1 .getsockname ()
584
+
585
+ socket_2 = create_socket ()
586
+ addr_2 = socket_2 .getsockname ()
587
+
588
+ # creating and immediately closing this to try to get an address
589
+ # that is not listening
590
+ socket_3 = create_socket ()
591
+ addr_3 = socket_3 .getsockname ()
592
+ socket_3 .shutdown (socket .SHUT_RDWR )
593
+ socket_3 .close ()
594
+
595
+ socket_1_recv_task = self .loop .create_task (recvfrom (socket_1 ))
596
+ socket_2_recv_task = self .loop .create_task (recvfrom (socket_2 ))
597
+ await asyncio .sleep (0 )
598
+
599
+ await self .loop .sock_sendto (socket_1 , b'a' , addr_2 )
600
+ self .assertEqual (await socket_2_recv_task , b'a' )
601
+
602
+ await self .loop .sock_sendto (socket_2 , b'b' , addr_1 )
603
+ self .assertEqual (await socket_1_recv_task , b'b' )
604
+ socket_1_recv_task = self .loop .create_task (recvfrom (socket_1 ))
605
+ await asyncio .sleep (0 )
606
+
607
+ # this should send to an address that isn't listening
608
+ await self .loop .sock_sendto (socket_1 , b'c' , addr_3 )
609
+ self .assertEqual (await socket_1_recv_task , b'' )
610
+ socket_1_recv_task = self .loop .create_task (recvfrom (socket_1 ))
611
+ await asyncio .sleep (0 )
612
+
613
+ # socket 1 should still be able to receive messages after sending
614
+ # to an address that wasn't listening
615
+ socket_2 .sendto (b'd' , addr_1 )
616
+ self .assertEqual (await socket_1_recv_task , b'd' )
617
+
618
+ socket_1 .shutdown (socket .SHUT_RDWR )
619
+ socket_1 .close ()
620
+ socket_2 .shutdown (socket .SHUT_RDWR )
621
+ socket_2 .close ()
622
+
623
+
624
+ def test_datagram_send_to_non_listening_address_recvfrom (self ):
625
+ async def recvfrom (socket ):
626
+ data , _ = await self .loop .sock_recvfrom (socket , 4096 )
627
+ return data
628
+
629
+ self .loop .run_until_complete (
630
+ self ._basetest_datagram_send_to_non_listening_address (
631
+ recvfrom ))
632
+
633
+
634
+ def test_datagram_send_to_non_listening_address_recvfrom_into (self ):
635
+ async def recvfrom_into (socket ):
636
+ buf = bytearray (4096 )
637
+ length , _ = await self .loop .sock_recvfrom_into (socket , buf ,
638
+ 4096 )
639
+ return buf [:length ]
640
+
641
+ self .loop .run_until_complete (
642
+ self ._basetest_datagram_send_to_non_listening_address (
643
+ recvfrom_into ))
644
+
564
645
else :
565
646
import selectors
566
647
0 commit comments