-
-
Notifications
You must be signed in to change notification settings - Fork 31.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
gh-128881: Do not ignore address
and flags
parameters in socket.{send,recv}_fds
#128882
base: main
Are you sure you want to change the base?
Changes from all commits
b9ae8e2
45b28f6
fbb2668
c58d2de
aefccca
6b58f4b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7037,19 +7037,22 @@ def test_dual_stack_client_v6(self): | |
@requireAttrs(socket, "recv_fds") | ||
@requireAttrs(socket, "AF_UNIX") | ||
class SendRecvFdsTests(unittest.TestCase): | ||
def testSendAndRecvFds(self): | ||
def close_pipes(pipes): | ||
for fd1, fd2 in pipes: | ||
os.close(fd1) | ||
os.close(fd2) | ||
|
||
def _cleanup_fds(self, fds): | ||
def close_fds(fds): | ||
for fd in fds: | ||
os.close(fd) | ||
self.addCleanup(close_fds, fds) | ||
|
||
def _test_pipe(self, rfd, wfd, msg): | ||
assert len(msg) < 512 | ||
os.write(wfd, msg) | ||
data = os.read(rfd, 512) | ||
self.assertEqual(data, msg) | ||
|
||
def testSendAndRecvFds(self): | ||
# send 10 file descriptors | ||
pipes = [os.pipe() for _ in range(10)] | ||
self.addCleanup(close_pipes, pipes) | ||
self._cleanup_fds(fd for pair in pipes for fd in pair) | ||
fds = [rfd for rfd, wfd in pipes] | ||
|
||
# use a UNIX socket pair to exchange file descriptors locally | ||
|
@@ -7058,21 +7061,95 @@ def close_fds(fds): | |
socket.send_fds(sock1, [MSG], fds) | ||
# request more data and file descriptors than expected | ||
msg, fds2, flags, addr = socket.recv_fds(sock2, len(MSG) * 2, len(fds) * 2) | ||
self.addCleanup(close_fds, fds2) | ||
self._cleanup_fds(fds2) | ||
|
||
self.assertEqual(msg, MSG) | ||
self.assertEqual(len(fds2), len(fds)) | ||
self.assertEqual(flags, 0) | ||
# don't test addr | ||
|
||
# test that file descriptors are connected | ||
for index, fds in enumerate(pipes): | ||
rfd, wfd = fds | ||
os.write(wfd, str(index).encode()) | ||
for index, ((_, wfd), rfd) in enumerate(zip(pipes, fds2)): | ||
self._test_pipe(rfd, wfd, str(index).encode()) | ||
|
||
@unittest.skipUnless(sys.platform in ("linux", "android", "darwin"), | ||
"works on Linux and macOS") | ||
def test_send_recv_fds_with_addrs(self): | ||
sock1 = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) | ||
sock2 = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) | ||
rfd, wfd = os.pipe() | ||
self.addCleanup(os.close, rfd) | ||
self.addCleanup(os.close, wfd) | ||
|
||
with tempfile.TemporaryDirectory() as tmpdir, sock1, sock2: | ||
sock1_addr = os.path.join(tmpdir, "sock1") | ||
sock2_addr = os.path.join(tmpdir, "sock2") | ||
sock1.bind(sock1_addr) | ||
sock2.bind(sock2_addr) | ||
sock2.setblocking(False) | ||
|
||
socket.send_fds(sock1, [MSG], [rfd], address=sock2_addr) | ||
msg, fds, flags, addr = socket.recv_fds(sock2, len(MSG), 1) | ||
self._cleanup_fds(fds) | ||
|
||
self.assertEqual(msg, MSG) | ||
self.assertEqual(len(fds), 1) | ||
self.assertEqual(addr, sock1_addr) | ||
|
||
self._test_pipe(fds[0], wfd, MSG) | ||
|
||
for index, rfd in enumerate(fds2): | ||
data = os.read(rfd, 100) | ||
self.assertEqual(data, str(index).encode()) | ||
@requireAttrs(socket, "MSG_PEEK") | ||
@unittest.skipUnless(sys.platform in ("linux", "android"), "works on Linux") | ||
def test_recv_fds_peek(self): | ||
rfd, wfd = os.pipe() | ||
self.addCleanup(os.close, rfd) | ||
self.addCleanup(os.close, wfd) | ||
|
||
sock1, sock2 = socket.socketpair(socket.AF_UNIX, socket.SOCK_DGRAM) | ||
with sock1, sock2: | ||
socket.send_fds(sock1, [MSG], [rfd]) | ||
sock2.setblocking(False) | ||
|
||
# peek message on sock2 | ||
peek_len = len(MSG) // 2 | ||
msg, fds, flags, addr = socket.recv_fds(sock2, peek_len, 1, | ||
flags=socket.MSG_PEEK) | ||
self._cleanup_fds(fds) | ||
|
||
self.assertEqual(len(msg), peek_len) | ||
self.assertEqual(msg, MSG[:peek_len]) | ||
self.assertEqual(flags & socket.MSG_TRUNC, socket.MSG_TRUNC) | ||
picnixz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
self.assertEqual(len(fds), 1) | ||
self._test_pipe(fds[0], wfd, MSG) | ||
|
||
# will raise BlockingIOError if MSG_PEEK didn't work | ||
msg, fds, flags, addr = socket.recv_fds(sock2, len(MSG), 1) | ||
self._cleanup_fds(fds) | ||
|
||
self.assertEqual(msg, MSG) | ||
self.assertEqual(len(fds), 1) | ||
self._test_pipe(fds[0], wfd, MSG) | ||
|
||
@requireAttrs(socket, "MSG_DONTWAIT") | ||
@unittest.skipUnless(sys.platform in ("linux", "android"), "Linux specific test") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. CI shows that sendmsg raises |
||
def test_send_fds_dontwait(self): | ||
rfd, wfd = os.pipe() | ||
self.addCleanup(os.close, rfd) | ||
self.addCleanup(os.close, wfd) | ||
|
||
sock1, sock2 = socket.socketpair(socket.AF_UNIX, socket.SOCK_DGRAM) | ||
with sock1, sock2: | ||
sock1.setblocking(True) | ||
with self.assertRaises(BlockingIOError): | ||
for _ in range(64 * 1024): | ||
socket.send_fds(sock1, [MSG], [rfd], socket.MSG_DONTWAIT) | ||
|
||
msg, fds, flags, addr = socket.recv_fds(sock2, len(MSG), 1) | ||
self._cleanup_fds(fds) | ||
|
||
self.assertEqual(msg, MSG) | ||
self.assertEqual(len(fds), 1) | ||
self._test_pipe(fds[0], wfd, MSG) | ||
|
||
|
||
class FreeThreadingTests(unittest.TestCase): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
Fix ``flags`` and ``address`` parameters which were ignored in | ||
:func:`socket.send_fds` and :func:`socket.recv_fds`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way to have a flag test on macOS or not at all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about it, but I'm not sure how to implement it. Is it good practice to write a
_test_recv_fds_peek(self, skip_fd_check=False)
method and 2 wrappers with different arguments?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A good practice is to check if there's a flag that is supported both on Unix and Windows. The reason is that the availability of
recv_fds
is marked as Unix and Windows but not WASI. So we shouldn't have this kind of skip in the first place.Now, if there is not a common flag that is supported on all platforms, just writing three different tests for three different flags is also fine (each of which protected by a
skipUnless
)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
recv_fds
requiresrecvmsg
, which is only available on Unix but not WASI, so I think it may be a documentation error.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok actually we have #122153 which wants to implement it but we never implemented it. Good to know that it's a doc issue. We can open a PR (using 122153 as the GH issue number) to amend the docs. For now, can you add some comment saying that it's not available on Windows yet but can be (and add some "see gh-122153)? thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Of course!
However sendmsg/recvmsg aren't the only requirements, CPython doesn't support
AF_UNIX
on Windows, and Windows doesn't supportSCM_RIGHTS
at all, so I think it's not possible to implement send_fds/recv_fds on Windows.ref: #77589 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doc issue was introduced in e3b6ff1