@@ -555,19 +555,27 @@ class SocketPairTest(unittest.TestCase, ThreadableTest):
555555 def __init__ (self , methodName = 'runTest' ):
556556 unittest .TestCase .__init__ (self , methodName = methodName )
557557 ThreadableTest .__init__ (self )
558+ self .cli = None
559+ self .serv = None
560+
561+ def socketpair (self ):
562+ # To be overridden by some child classes.
563+ return socket .socketpair ()
558564
559565 def setUp (self ):
560- self .serv , self .cli = socket .socketpair ()
566+ self .serv , self .cli = self .socketpair ()
561567
562568 def tearDown (self ):
563- self .serv .close ()
569+ if self .serv :
570+ self .serv .close ()
564571 self .serv = None
565572
566573 def clientSetUp (self ):
567574 pass
568575
569576 def clientTearDown (self ):
570- self .cli .close ()
577+ if self .cli :
578+ self .cli .close ()
571579 self .cli = None
572580 ThreadableTest .clientTearDown (self )
573581
@@ -4613,6 +4621,120 @@ def _testSend(self):
46134621 self .assertEqual (msg , MSG )
46144622
46154623
4624+ class PurePythonSocketPairTest (SocketPairTest ):
4625+
4626+ # Explicitly use socketpair AF_INET or AF_INET6 to ensure that is the
4627+ # code path we're using regardless platform is the pure python one where
4628+ # `_socket.socketpair` does not exist. (AF_INET does not work with
4629+ # _socket.socketpair on many platforms).
4630+ def socketpair (self ):
4631+ # called by super().setUp().
4632+ try :
4633+ return socket .socketpair (socket .AF_INET6 )
4634+ except OSError :
4635+ return socket .socketpair (socket .AF_INET )
4636+
4637+ # Local imports in this class make for easy security fix backporting.
4638+
4639+ def setUp (self ):
4640+ import _socket
4641+ self ._orig_sp = getattr (_socket , 'socketpair' , None )
4642+ if self ._orig_sp is not None :
4643+ # This forces the version using the non-OS provided socketpair
4644+ # emulation via an AF_INET socket in Lib/socket.py.
4645+ del _socket .socketpair
4646+ import importlib
4647+ global socket
4648+ socket = importlib .reload (socket )
4649+ else :
4650+ pass # This platform already uses the non-OS provided version.
4651+ super ().setUp ()
4652+
4653+ def tearDown (self ):
4654+ super ().tearDown ()
4655+ import _socket
4656+ if self ._orig_sp is not None :
4657+ # Restore the default socket.socketpair definition.
4658+ _socket .socketpair = self ._orig_sp
4659+ import importlib
4660+ global socket
4661+ socket = importlib .reload (socket )
4662+
4663+ def test_recv (self ):
4664+ msg = self .serv .recv (1024 )
4665+ self .assertEqual (msg , MSG )
4666+
4667+ def _test_recv (self ):
4668+ self .cli .send (MSG )
4669+
4670+ def test_send (self ):
4671+ self .serv .send (MSG )
4672+
4673+ def _test_send (self ):
4674+ msg = self .cli .recv (1024 )
4675+ self .assertEqual (msg , MSG )
4676+
4677+ def test_ipv4 (self ):
4678+ cli , srv = socket .socketpair (socket .AF_INET )
4679+ cli .close ()
4680+ srv .close ()
4681+
4682+ def _test_ipv4 (self ):
4683+ pass
4684+
4685+ @unittest .skipIf (not hasattr (_socket , 'IPPROTO_IPV6' ) or
4686+ not hasattr (_socket , 'IPV6_V6ONLY' ),
4687+ "IPV6_V6ONLY option not supported" )
4688+ @unittest .skipUnless (socket_helper .IPV6_ENABLED , 'IPv6 required for this test' )
4689+ def test_ipv6 (self ):
4690+ cli , srv = socket .socketpair (socket .AF_INET6 )
4691+ cli .close ()
4692+ srv .close ()
4693+
4694+ def _test_ipv6 (self ):
4695+ pass
4696+
4697+ def test_injected_authentication_failure (self ):
4698+ orig_getsockname = socket .socket .getsockname
4699+ inject_sock = None
4700+
4701+ def inject_getsocketname (self ):
4702+ nonlocal inject_sock
4703+ sockname = orig_getsockname (self )
4704+ # Connect to the listening socket ahead of the
4705+ # client socket.
4706+ if inject_sock is None :
4707+ inject_sock = socket .socket (socket .AF_INET , socket .SOCK_STREAM )
4708+ inject_sock .setblocking (False )
4709+ try :
4710+ inject_sock .connect (sockname [:2 ])
4711+ except (BlockingIOError , InterruptedError ):
4712+ pass
4713+ inject_sock .setblocking (True )
4714+ return sockname
4715+
4716+ sock1 = sock2 = None
4717+ try :
4718+ socket .socket .getsockname = inject_getsocketname
4719+ with self .assertRaises (OSError ):
4720+ sock1 , sock2 = socket .socketpair ()
4721+ finally :
4722+ socket .socket .getsockname = orig_getsockname
4723+ if inject_sock :
4724+ inject_sock .close ()
4725+ if sock1 : # This cleanup isn't needed on a successful test.
4726+ sock1 .close ()
4727+ if sock2 :
4728+ sock2 .close ()
4729+
4730+ def _test_injected_authentication_failure (self ):
4731+ # No-op. Exists for base class threading infrastructure to call.
4732+ # We could refactor this test into its own lesser class along with the
4733+ # setUp and tearDown code to construct an ideal; it is simpler to keep
4734+ # it here and live with extra overhead one this _one_ failure test.
4735+ pass
4736+
4737+
46164738class NonBlockingTCPTests (ThreadedTCPSocketTest ):
46174739
46184740 def __init__ (self , methodName = 'runTest' ):
0 commit comments