@@ -542,19 +542,27 @@ class SocketPairTest(unittest.TestCase, ThreadableTest):
542542 def __init__ (self , methodName = 'runTest' ):
543543 unittest .TestCase .__init__ (self , methodName = methodName )
544544 ThreadableTest .__init__ (self )
545+ self .cli = None
546+ self .serv = None
547+
548+ def socketpair (self ):
549+ # To be overridden by some child classes.
550+ return socket .socketpair ()
545551
546552 def setUp (self ):
547- self .serv , self .cli = socket .socketpair ()
553+ self .serv , self .cli = self .socketpair ()
548554
549555 def tearDown (self ):
550- self .serv .close ()
556+ if self .serv :
557+ self .serv .close ()
551558 self .serv = None
552559
553560 def clientSetUp (self ):
554561 pass
555562
556563 def clientTearDown (self ):
557- self .cli .close ()
564+ if self .cli :
565+ self .cli .close ()
558566 self .cli = None
559567 ThreadableTest .clientTearDown (self )
560568
@@ -4667,6 +4675,120 @@ def _testSend(self):
46674675 self .assertEqual (msg , MSG )
46684676
46694677
4678+ class PurePythonSocketPairTest (SocketPairTest ):
4679+
4680+ # Explicitly use socketpair AF_INET or AF_INET6 to ensure that is the
4681+ # code path we're using regardless platform is the pure python one where
4682+ # `_socket.socketpair` does not exist. (AF_INET does not work with
4683+ # _socket.socketpair on many platforms).
4684+ def socketpair (self ):
4685+ # called by super().setUp().
4686+ try :
4687+ return socket .socketpair (socket .AF_INET6 )
4688+ except OSError :
4689+ return socket .socketpair (socket .AF_INET )
4690+
4691+ # Local imports in this class make for easy security fix backporting.
4692+
4693+ def setUp (self ):
4694+ import _socket
4695+ self ._orig_sp = getattr (_socket , 'socketpair' , None )
4696+ if self ._orig_sp is not None :
4697+ # This forces the version using the non-OS provided socketpair
4698+ # emulation via an AF_INET socket in Lib/socket.py.
4699+ del _socket .socketpair
4700+ import importlib
4701+ global socket
4702+ socket = importlib .reload (socket )
4703+ else :
4704+ pass # This platform already uses the non-OS provided version.
4705+ super ().setUp ()
4706+
4707+ def tearDown (self ):
4708+ super ().tearDown ()
4709+ import _socket
4710+ if self ._orig_sp is not None :
4711+ # Restore the default socket.socketpair definition.
4712+ _socket .socketpair = self ._orig_sp
4713+ import importlib
4714+ global socket
4715+ socket = importlib .reload (socket )
4716+
4717+ def test_recv (self ):
4718+ msg = self .serv .recv (1024 )
4719+ self .assertEqual (msg , MSG )
4720+
4721+ def _test_recv (self ):
4722+ self .cli .send (MSG )
4723+
4724+ def test_send (self ):
4725+ self .serv .send (MSG )
4726+
4727+ def _test_send (self ):
4728+ msg = self .cli .recv (1024 )
4729+ self .assertEqual (msg , MSG )
4730+
4731+ def test_ipv4 (self ):
4732+ cli , srv = socket .socketpair (socket .AF_INET )
4733+ cli .close ()
4734+ srv .close ()
4735+
4736+ def _test_ipv4 (self ):
4737+ pass
4738+
4739+ @unittest .skipIf (not hasattr (_socket , 'IPPROTO_IPV6' ) or
4740+ not hasattr (_socket , 'IPV6_V6ONLY' ),
4741+ "IPV6_V6ONLY option not supported" )
4742+ @unittest .skipUnless (socket_helper .IPV6_ENABLED , 'IPv6 required for this test' )
4743+ def test_ipv6 (self ):
4744+ cli , srv = socket .socketpair (socket .AF_INET6 )
4745+ cli .close ()
4746+ srv .close ()
4747+
4748+ def _test_ipv6 (self ):
4749+ pass
4750+
4751+ def test_injected_authentication_failure (self ):
4752+ orig_getsockname = socket .socket .getsockname
4753+ inject_sock = None
4754+
4755+ def inject_getsocketname (self ):
4756+ nonlocal inject_sock
4757+ sockname = orig_getsockname (self )
4758+ # Connect to the listening socket ahead of the
4759+ # client socket.
4760+ if inject_sock is None :
4761+ inject_sock = socket .socket (socket .AF_INET , socket .SOCK_STREAM )
4762+ inject_sock .setblocking (False )
4763+ try :
4764+ inject_sock .connect (sockname [:2 ])
4765+ except (BlockingIOError , InterruptedError ):
4766+ pass
4767+ inject_sock .setblocking (True )
4768+ return sockname
4769+
4770+ sock1 = sock2 = None
4771+ try :
4772+ socket .socket .getsockname = inject_getsocketname
4773+ with self .assertRaises (OSError ):
4774+ sock1 , sock2 = socket .socketpair ()
4775+ finally :
4776+ socket .socket .getsockname = orig_getsockname
4777+ if inject_sock :
4778+ inject_sock .close ()
4779+ if sock1 : # This cleanup isn't needed on a successful test.
4780+ sock1 .close ()
4781+ if sock2 :
4782+ sock2 .close ()
4783+
4784+ def _test_injected_authentication_failure (self ):
4785+ # No-op. Exists for base class threading infrastructure to call.
4786+ # We could refactor this test into its own lesser class along with the
4787+ # setUp and tearDown code to construct an ideal; it is simpler to keep
4788+ # it here and live with extra overhead one this _one_ failure test.
4789+ pass
4790+
4791+
46704792class NonBlockingTCPTests (ThreadedTCPSocketTest ):
46714793
46724794 def __init__ (self , methodName = 'runTest' ):
0 commit comments