| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <fuchsia/io/llcpp/fidl.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async-loop/default.h> |
| #include <lib/fdio/fd.h> |
| #include <lib/fidl-async/cpp/bind.h> |
| |
| #include <fbl/unique_fd.h> |
| #include <zxtest/zxtest.h> |
| |
| namespace { |
| |
| class Server final : public llcpp::fuchsia::io::Node::Interface { |
| public: |
| explicit Server(llcpp::fuchsia::io::NodeInfo describe_info) |
| : describe_info_(std::move(describe_info)) {} |
| |
| void Clone(uint32_t flags, ::zx::channel object, CloneCompleter::Sync& completer) override { |
| ADD_FAILURE("Clone should not be called"); |
| completer.Close(ZX_ERR_NOT_SUPPORTED); |
| } |
| |
| void Close(CloseCompleter::Sync& completer) override { |
| EXPECT_OK(completer.Reply(ZX_OK).status()); |
| // FDIO expects the channel to be closed after replying. |
| completer.Close(ZX_OK); |
| } |
| |
| void Describe(DescribeCompleter::Sync& completer) override { |
| ASSERT_TRUE(describe_info_.has_value(), "Describe called more than once"); |
| EXPECT_OK(completer.Reply(std::move(*describe_info_)).status()); |
| describe_info_.reset(); |
| } |
| |
| void Sync(SyncCompleter::Sync& completer) override { |
| ADD_FAILURE("Sync should not be called"); |
| completer.Close(ZX_ERR_NOT_SUPPORTED); |
| } |
| |
| void GetAttr(GetAttrCompleter::Sync& completer) override { |
| ADD_FAILURE("GetAttr should not be called"); |
| completer.Close(ZX_ERR_NOT_SUPPORTED); |
| } |
| |
| void SetAttr(uint32_t flags, ::llcpp::fuchsia::io::NodeAttributes attributes, |
| SetAttrCompleter::Sync& completer) override { |
| ADD_FAILURE("SetAttr should not be called"); |
| completer.Close(ZX_ERR_NOT_SUPPORTED); |
| } |
| |
| private: |
| fit::optional<llcpp::fuchsia::io::NodeInfo> describe_info_; |
| }; |
| |
| // Serves |node_info| over |server_channel| to |client_channel| using a |Server| instance by |
| // creating a file descriptor from |client_channel| and immediately closing it. |
| void ServeAndExerciseFileDescriptionTeardown(llcpp::fuchsia::io::NodeInfo node_info, |
| zx::channel client_channel, |
| zx::channel server_channel) { |
| Server server(std::move(node_info)); |
| async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread); |
| |
| auto bind_result = fidl::BindServer(loop.dispatcher(), std::move(server_channel), &server); |
| ASSERT_TRUE(bind_result.is_ok(), "failed to bind server: %s", |
| zx_status_get_string(bind_result.error())); |
| ASSERT_OK(loop.StartThread("fake-socket-server")); |
| |
| { |
| fbl::unique_fd fd; |
| ASSERT_OK(fdio_fd_create(client_channel.release(), fd.reset_and_get_address())); |
| } |
| } |
| |
| TEST(SocketCleanup, Datagram) { |
| zx::channel client_channel, server_channel; |
| ASSERT_OK(zx::channel::create(0, &client_channel, &server_channel)); |
| |
| zx::eventpair client_event, server_event; |
| ASSERT_OK(zx::eventpair::create(0, &client_event, &server_event)); |
| |
| llcpp::fuchsia::io::DatagramSocket dgram_info{.event = std::move(client_event)}; |
| llcpp::fuchsia::io::NodeInfo node_info; |
| node_info.set_datagram_socket(fidl::unowned_ptr(&dgram_info)); |
| |
| zx::unowned_channel client_handle(client_channel); |
| ASSERT_NO_FATAL_FAILURES(ServeAndExerciseFileDescriptionTeardown( |
| std::move(node_info), std::move(client_channel), std::move(server_channel))); |
| |
| // Client must have disposed of its channel and eventpair handle on close. |
| EXPECT_STATUS(client_handle->wait_one(ZX_CHANNEL_PEER_CLOSED, zx::time::infinite_past(), nullptr), |
| ZX_ERR_BAD_HANDLE); |
| EXPECT_OK(server_event.wait_one(ZX_EVENTPAIR_PEER_CLOSED, zx::time::infinite_past(), nullptr)); |
| } |
| |
| TEST(SocketCleanup, Stream) { |
| zx::channel client_channel, server_channel; |
| ASSERT_OK(zx::channel::create(0, &client_channel, &server_channel)); |
| |
| zx::socket client_socket, server_socket; |
| ASSERT_OK(zx::socket::create(0, &client_socket, &server_socket)); |
| |
| llcpp::fuchsia::io::StreamSocket stream_info{.socket = std::move(client_socket)}; |
| llcpp::fuchsia::io::NodeInfo node_info; |
| node_info.set_stream_socket(fidl::unowned_ptr(&stream_info)); |
| |
| zx::unowned_channel client_handle(client_channel); |
| ASSERT_NO_FATAL_FAILURES(ServeAndExerciseFileDescriptionTeardown( |
| std::move(node_info), std::move(client_channel), std::move(server_channel))); |
| |
| // Client must have disposed of its channel and socket handles on close. |
| EXPECT_STATUS(client_handle->wait_one(ZX_CHANNEL_PEER_CLOSED, zx::time::infinite_past(), nullptr), |
| ZX_ERR_BAD_HANDLE); |
| EXPECT_OK(server_socket.wait_one(ZX_SOCKET_PEER_CLOSED, zx::time::infinite_past(), nullptr)); |
| } |
| |
| } // namespace |