summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorKristján Valur Jónsson <kristjan@ccpgames.com>2012-04-07 11:23:31 (GMT)
committerKristján Valur Jónsson <kristjan@ccpgames.com>2012-04-07 11:23:31 (GMT)
commit10f383a9376df13635bb53d5885d43297d0022cd (patch)
treec6ffb4eee7fabdaa0ffa87e5f6282e6b7a2e6f37 /Lib
parent0f9eec19ee1652a61d4b2e860e599c617d88b707 (diff)
downloadcpython-10f383a9376df13635bb53d5885d43297d0022cd.zip
cpython-10f383a9376df13635bb53d5885d43297d0022cd.tar.gz
cpython-10f383a9376df13635bb53d5885d43297d0022cd.tar.bz2
Issue #14310: inter-process socket duplication for windows
Diffstat (limited to 'Lib')
-rw-r--r--Lib/socket.py10
-rw-r--r--Lib/test/test_socket.py105
2 files changed, 114 insertions, 1 deletions
diff --git a/Lib/socket.py b/Lib/socket.py
index 8b2d1cd..1378b0f 100644
--- a/Lib/socket.py
+++ b/Lib/socket.py
@@ -12,6 +12,7 @@ Functions:
socket() -- create a new socket object
socketpair() -- create a pair of new socket objects [*]
fromfd() -- create a socket object from an open file descriptor [*]
+fromshare() -- create a socket object from data received from socket.share() [*]
gethostname() -- return the current hostname
gethostbyname() -- map a hostname to its IP number
gethostbyaddr() -- map an IP number or hostname to DNS info
@@ -209,7 +210,6 @@ class socket(_socket.socket):
self._closed = True
return super().detach()
-
def fromfd(fd, family, type, proto=0):
""" fromfd(fd, family, type[, proto]) -> socket object
@@ -219,6 +219,14 @@ def fromfd(fd, family, type, proto=0):
nfd = dup(fd)
return socket(family, type, proto, nfd)
+if hasattr(_socket.socket, "share"):
+ def fromshare(info):
+ """ fromshare(info) -> socket object
+
+ Create a socket object from a the bytes object returned by
+ socket.share(pid).
+ """
+ return socket(0, 0, 0, info)
if hasattr(_socket, "socketpair"):
diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
index 17c5486..6da423a 100644
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -26,6 +26,10 @@ try:
import fcntl
except ImportError:
fcntl = False
+try:
+ import multiprocessing
+except ImportError:
+ multiprocessing = False
HOST = support.HOST
MSG = 'Michael Gilfix was here\u1234\r\n'.encode('utf-8') ## test unicode string and carriage return
@@ -4643,6 +4647,106 @@ class NonblockConstantTest(unittest.TestCase):
socket.setdefaulttimeout(t)
+@unittest.skipUnless(os.name == "nt", "Windows specific")
+@unittest.skipUnless(multiprocessing, "need multiprocessing")
+class TestSocketSharing(SocketTCPTest):
+ # This must be classmethod and not staticmethod or multiprocessing
+ # won't be able to bootstrap it.
+ @classmethod
+ def remoteProcessServer(cls, q):
+ # Recreate socket from shared data
+ sdata = q.get()
+ message = q.get()
+
+ s = socket.fromshare(sdata)
+ s2, c = s.accept()
+
+ # Send the message
+ s2.sendall(message)
+ s2.close()
+ s.close()
+
+ def testShare(self):
+ # Transfer the listening server socket to another process
+ # and service it from there.
+
+ # Create process:
+ q = multiprocessing.Queue()
+ p = multiprocessing.Process(target=self.remoteProcessServer, args=(q,))
+ p.start()
+
+ # Get the shared socket data
+ data = self.serv.share(p.pid)
+
+ # Pass the shared socket to the other process
+ addr = self.serv.getsockname()
+ self.serv.close()
+ q.put(data)
+
+ # The data that the server will send us
+ message = b"slapmahfro"
+ q.put(message)
+
+ # Connect
+ s = socket.create_connection(addr)
+ # listen for the data
+ m = []
+ while True:
+ data = s.recv(100)
+ if not data:
+ break
+ m.append(data)
+ s.close()
+ received = b"".join(m)
+ self.assertEqual(received, message)
+ p.join()
+
+ def testShareLength(self):
+ data = self.serv.share(os.getpid())
+ self.assertRaises(ValueError, socket.fromshare, data[:-1])
+ self.assertRaises(ValueError, socket.fromshare, data+b"foo")
+
+ def compareSockets(self, org, other):
+ # socket sharing is expected to work only for blocking socket
+ # since the internal python timout value isn't transfered.
+ self.assertEqual(org.gettimeout(), None)
+ self.assertEqual(org.gettimeout(), other.gettimeout())
+
+ self.assertEqual(org.family, other.family)
+ self.assertEqual(org.type, other.type)
+ # If the user specified "0" for proto, then
+ # internally windows will have picked the correct value.
+ # Python introspection on the socket however will still return
+ # 0. For the shared socket, the python value is recreated
+ # from the actual value, so it may not compare correctly.
+ if org.proto != 0:
+ self.assertEqual(org.proto, other.proto)
+
+ def testShareLocal(self):
+ data = self.serv.share(os.getpid())
+ s = socket.fromshare(data)
+ try:
+ self.compareSockets(self.serv, s)
+ finally:
+ s.close()
+
+ def testTypes(self):
+ families = [socket.AF_INET, socket.AF_INET6]
+ types = [socket.SOCK_STREAM, socket.SOCK_DGRAM]
+ for f in families:
+ for t in types:
+ source = socket.socket(f, t)
+ try:
+ data = source.share(os.getpid())
+ shared = socket.fromshare(data)
+ try:
+ self.compareSockets(source, shared)
+ finally:
+ shared.close()
+ finally:
+ source.close()
+
+
def test_main():
tests = [GeneralModuleTests, BasicTCPTest, TCPCloserTest, TCPTimeoutTest,
TestExceptions, BufferIOTest, BasicTCPTest2, BasicUDPTest, UDPTimeoutTest ]
@@ -4699,6 +4803,7 @@ def test_main():
# These are slow when setitimer() is not available
InterruptedRecvTimeoutTest,
InterruptedSendTimeoutTest,
+ TestSocketSharing,
])
thread_info = support.threading_setup()