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