| // Copyright 2019 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 <fidl/test/compatibility/llcpp/fidl.h> |
| #include <fuchsia/sys/cpp/fidl.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/fdio/directory.h> |
| #include <lib/fdio/fd.h> |
| #include <lib/fdio/fdio.h> |
| #include <lib/fidl-async/cpp/bind.h> |
| #include <lib/fidl/cpp/interface_request.h> |
| #include <lib/sys/cpp/component_context.h> |
| #include <zircon/status.h> |
| |
| #include <cstdlib> |
| #include <iostream> |
| #include <string> |
| |
| constexpr const char kEchoInterfaceName[] = "fidl.test.compatibility.Echo"; |
| |
| namespace fidl { |
| namespace test { |
| namespace compatibility { |
| |
| class EchoClientApp { |
| public: |
| EchoClientApp(::fidl::StringView server_url) |
| : context_(sys::ComponentContext::Create()), |
| client_(Echo::SyncClient(ConnectTo(server_url))) {} |
| |
| ::fidl::DecodeResult<Echo::EchoStructResponse> EchoStruct( |
| ::fidl::BytePart request_buffer, Struct value, |
| ::fidl::StringView forward_to_server, ::fidl::BytePart response_buffer) { |
| Echo::EchoStructRequest request = {}; |
| request.value = std::move(value); |
| request.forward_to_server = forward_to_server; |
| auto linearize_result = |
| ::fidl::Linearize(&request, std::move(request_buffer)); |
| if (linearize_result.status != ZX_OK) { |
| return ::fidl::DecodeResult<Echo::EchoStructResponse>( |
| linearize_result.status, linearize_result.error); |
| } |
| return client_.EchoStruct(std::move(linearize_result.message), |
| std::move(response_buffer)); |
| } |
| |
| ::fidl::DecodeResult<Echo::EchoEventResponse> EchoStructNoRetVal( |
| Struct value, ::fidl::StringView forward_to_server, |
| ::fidl::BytePart response_buffer) { |
| auto status = |
| client_.EchoStructNoRetVal(std::move(value), forward_to_server); |
| if (status != ZX_OK) { |
| return ::fidl::DecodeResult<Echo::EchoEventResponse>( |
| status, "Failed to send echo event"); |
| } |
| // Wait for event |
| ZX_ASSERT( |
| client_end_->wait_one(ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED, |
| zx::time::infinite(), nullptr) == ZX_OK); |
| ::fidl::EncodedMessage<Echo::EchoEventResponse> encoded_msg; |
| encoded_msg.Initialize( |
| [&](::fidl::BytePart& bytes, ::fidl::HandlePart& handles) { |
| bytes = std::move(response_buffer); |
| uint32_t actual_bytes = 0; |
| uint32_t actual_handles = 0; |
| ZX_ASSERT(client_end_->rea2(0, bytes.data(), handles.data(), |
| bytes.capacity(), handles.capacity(), |
| &actual_bytes, &actual_handles) == ZX_OK); |
| // TODO(FIDL-350): Hard-coding the event ordinal due to no event |
| // support in llcpp; refactor once that lands. |
| constexpr uint32_t kEchoStructEventOrdinal = 849359397u; |
| ZX_ASSERT(actual_bytes > sizeof(fidl_message_header_t)); |
| ZX_ASSERT( |
| reinterpret_cast<fidl_message_header_t*>(bytes.data())->ordinal == |
| kEchoStructEventOrdinal); |
| bytes.set_actual(actual_bytes); |
| handles.set_actual(actual_handles); |
| }); |
| // Decode the event |
| return ::fidl::Decode(std::move(encoded_msg)); |
| } |
| |
| EchoClientApp(const EchoClientApp&) = delete; |
| EchoClientApp& operator=(const EchoClientApp&) = delete; |
| |
| private: |
| // Called once upon construction to launch and connect to the server. |
| zx::channel ConnectTo(::fidl::StringView server_url) { |
| fuchsia::sys::LaunchInfo launch_info; |
| launch_info.url = std::string(server_url.data(), server_url.size()); |
| echo_provider_ = sys::ServiceDirectory::CreateWithRequest( |
| &launch_info.directory_request); |
| |
| fuchsia::sys::LauncherPtr launcher; |
| context_->svc()->Connect(launcher.NewRequest()); |
| launcher->CreateComponent(std::move(launch_info), controller_.NewRequest()); |
| |
| zx::channel server_end, client_end; |
| ZX_ASSERT(zx::channel::create(0, &client_end, &server_end) == ZX_OK); |
| ZX_ASSERT(echo_provider_->Connect(kEchoInterfaceName, |
| std::move(server_end)) == ZX_OK); |
| |
| client_end_ = zx::unowned_channel(client_end); |
| return client_end; |
| } |
| |
| std::unique_ptr<sys::ComponentContext> context_; |
| std::shared_ptr<sys::ServiceDirectory> echo_provider_; |
| fuchsia::sys::ComponentControllerPtr controller_; |
| // Used to monitor events |
| zx::unowned_channel client_end_; |
| Echo::SyncClient client_; |
| }; |
| |
| class EchoConnection final : public Echo::Interface { |
| public: |
| explicit EchoConnection(zx::unowned_channel channel) : channel_(channel) {} |
| |
| void EchoStruct(Struct value, ::fidl::StringView forward_to_server, |
| EchoStructCompleter::Sync completer) override { |
| if (forward_to_server.empty()) { |
| completer.Reply(std::move(value)); |
| } else { |
| std::vector<uint8_t> request_buffer(ZX_CHANNEL_MAX_MSG_BYTES); |
| std::vector<uint8_t> response_buffer(ZX_CHANNEL_MAX_MSG_BYTES); |
| EchoClientApp app(forward_to_server); |
| char empty[1] = {}; |
| auto result = app.EchoStruct( |
| ::fidl::BytePart(&request_buffer[0], |
| static_cast<uint32_t>(request_buffer.size())), |
| std::move(value), ::fidl::StringView{0, empty}, |
| ::fidl::BytePart(&response_buffer[0], |
| static_cast<uint32_t>(response_buffer.size()))); |
| ZX_ASSERT_MSG(result.status == ZX_OK, |
| "Forwarding failed: %s", |
| result.error); |
| completer.Reply(std::move(result.message.message()->value)); |
| } |
| } |
| |
| void EchoStructNoRetVal( |
| Struct value, ::fidl::StringView forward_to_server, |
| EchoStructNoRetValCompleter::Sync completer) override { |
| if (forward_to_server.empty()) { |
| auto status = Echo::SendEchoEventEvent(zx::unowned_channel(channel_), |
| std::move(value)); |
| ZX_ASSERT_MSG(status == ZX_OK, |
| "Replying with event failed: %s", |
| zx_status_get_string(status)); |
| } else { |
| std::vector<uint8_t> response_buffer(ZX_CHANNEL_MAX_MSG_BYTES); |
| EchoClientApp app(forward_to_server); |
| char empty[1] = {}; |
| auto result = app.EchoStructNoRetVal( |
| std::move(value), ::fidl::StringView{0, empty}, |
| ::fidl::BytePart(&response_buffer[0], |
| static_cast<uint32_t>(response_buffer.size()))); |
| ZX_ASSERT_MSG(result.status == ZX_OK, |
| "Forwarding failed: %s", |
| result.error); |
| auto status = |
| Echo::SendEchoEventEvent(zx::unowned_channel(channel_), |
| std::move(result.message.message()->value)); |
| ZX_ASSERT_MSG(status == ZX_OK, |
| "Replying with event failed: %s", |
| zx_status_get_string(status)); |
| } |
| } |
| |
| private: |
| zx::unowned_channel channel_; |
| }; |
| |
| } // namespace compatibility |
| } // namespace test |
| } // namespace fidl |
| |
| int main(int argc, const char** argv) { |
| // The FIDL support lib requires async_get_default_dispatcher() to return |
| // non-null. |
| async::Loop loop(&kAsyncLoopConfigAttachToThread); |
| auto context = sys::ComponentContext::Create(); |
| std::vector<std::unique_ptr<fidl::test::compatibility::EchoConnection>> |
| connections; |
| |
| context->outgoing()->AddPublicService( |
| std::make_unique<vfs::Service>([&](zx::channel request, |
| async_dispatcher_t* dispatcher) { |
| auto conn = std::make_unique<fidl::test::compatibility::EchoConnection>( |
| zx::unowned_channel(request)); |
| ZX_ASSERT(fidl::Bind(dispatcher, std::move(request), conn.get()) == |
| ZX_OK); |
| connections.push_back(std::move(conn)); |
| }), |
| kEchoInterfaceName); |
| |
| loop.Run(); |
| return EXIT_SUCCESS; |
| } |