diff options
author | Alexei Frolov <frolv@google.com> | 2021-04-12 14:53:06 -0700 |
---|---|---|
committer | CQ Bot Account <pigweed-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2021-04-14 00:17:06 +0000 |
commit | 3e28092cd59477cf09727d6e6357cc993d565f9b (patch) | |
tree | b8f6aba55024f0d4789a100c866eff12f87a08e4 | |
parent | 4ea2de8fa56cd6fc5e622a229c4d94d77bcc2fe9 (diff) | |
download | pigweed-3e28092cd59477cf09727d6e6357cc993d565f9b.tar.gz |
pw_rpc: Add ClientServer combination
This adds a class which wraps both an RPC client and server, simplifying
setup and usage in systems that require both.
Change-Id: I00e3cbeef91b8703c432800f58a96db5faff63f4
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/40624
Reviewed-by: Wyatt Hepler <hepler@google.com>
Commit-Queue: Alexei Frolov <frolv@google.com>
-rw-r--r-- | pw_rpc/BUILD | 19 | ||||
-rw-r--r-- | pw_rpc/BUILD.gn | 20 | ||||
-rw-r--r-- | pw_rpc/CMakeLists.txt | 8 | ||||
-rw-r--r-- | pw_rpc/client_server.cc | 30 | ||||
-rw-r--r-- | pw_rpc/client_server_test.cc | 85 | ||||
-rw-r--r-- | pw_rpc/docs.rst | 23 | ||||
-rw-r--r-- | pw_rpc/public/pw_rpc/client_server.h | 40 |
7 files changed, 225 insertions, 0 deletions
diff --git a/pw_rpc/BUILD b/pw_rpc/BUILD index 996a8a0a3..b2cd78946 100644 --- a/pw_rpc/BUILD +++ b/pw_rpc/BUILD @@ -63,6 +63,16 @@ pw_cc_library( ) pw_cc_library( + name = "client_server", + srcs = ["client_server.cc"], + hdrs = ["public/pw_rpc/client_server.h"], + deps = [ + ":client", + ":server", + ], +) + +pw_cc_library( name = "common", srcs = [ "channel.cc", @@ -186,6 +196,15 @@ pw_cc_test( ], ) +pw_cc_test( + name = "client_server_test", + srcs = ["client_server_test.cc"], + deps = [ + ":client_server", + "//pw_rpc/raw:method_union", + ], +) + proto_library( name = "packet_proto", srcs = [ diff --git a/pw_rpc/BUILD.gn b/pw_rpc/BUILD.gn index 5f98883bb..9f4a95fba 100644 --- a/pw_rpc/BUILD.gn +++ b/pw_rpc/BUILD.gn @@ -82,6 +82,16 @@ pw_source_set("client") { ] } +pw_source_set("client_server") { + public_configs = [ ":public_include_path" ] + public_deps = [ + ":client", + ":server", + ] + public = [ "public/pw_rpc/client_server.h" ] + sources = [ "client_server.cc" ] +} + # Classes shared by the server and client. pw_source_set("common") { public_configs = [ ":public_include_path" ] @@ -184,6 +194,7 @@ pw_test_group("tests") { ":base_server_writer_test", ":channel_test", ":client_test", + ":client_server_test", ":ids_test", ":packet_test", ":server_test", @@ -262,6 +273,15 @@ pw_test("client_test") { sources = [ "client_test.cc" ] } +pw_test("client_server_test") { + deps = [ + ":client_server", + ":test_utils", + "raw:method_union", + ] + sources = [ "client_server_test.cc" ] +} + pw_test("base_client_call_test") { deps = [ ":client", diff --git a/pw_rpc/CMakeLists.txt b/pw_rpc/CMakeLists.txt index b929d5a1c..c3635fe5e 100644 --- a/pw_rpc/CMakeLists.txt +++ b/pw_rpc/CMakeLists.txt @@ -43,6 +43,14 @@ pw_add_module_library(pw_rpc.client pw_log ) +pw_add_module_library(pw_rpc.client_server + SOURCES + client_server.cc + PUBLIC_DEPS + pw_rpc.client + pw_rpc.server +) + pw_add_module_library(pw_rpc.common SOURCES channel.cc diff --git a/pw_rpc/client_server.cc b/pw_rpc/client_server.cc new file mode 100644 index 000000000..f0c34ab8d --- /dev/null +++ b/pw_rpc/client_server.cc @@ -0,0 +1,30 @@ +// Copyright 2021 The Pigweed Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#include "pw_rpc/client_server.h" + +namespace pw::rpc { + +Status ClientServer::ProcessPacket(std::span<const std::byte> packet, + ChannelOutput& interface) { + Status status = server_.ProcessPacket(packet, interface); + if (status.IsInvalidArgument()) { + // INVALID_ARGUMENT indicates the packet is intended for a client. + status = client_.ProcessPacket(packet); + } + + return status; +} + +} // namespace pw::rpc diff --git a/pw_rpc/client_server_test.cc b/pw_rpc/client_server_test.cc new file mode 100644 index 000000000..5104c66c7 --- /dev/null +++ b/pw_rpc/client_server_test.cc @@ -0,0 +1,85 @@ +// Copyright 2020 The Pigweed Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#include "pw_rpc/client_server.h" + +#include "gtest/gtest.h" +#include "pw_rpc/internal/packet.h" +#include "pw_rpc/internal/raw_method_union.h" +#include "pw_rpc/server_context.h" +#include "pw_rpc/service.h" +#include "pw_rpc_private/internal_test_utils.h" + +namespace pw::rpc::internal { +namespace { + +constexpr uint32_t kFakeChannelId = 1; +constexpr uint32_t kFakeServiceId = 3; +constexpr uint32_t kFakeMethodId = 10; + +TestOutput<32> output; +rpc::Channel channels[] = {Channel::Create<kFakeChannelId>(&output)}; + +StatusWithSize FakeMethod(ServerContext&, ConstByteSpan, ByteSpan) { + return StatusWithSize::Unimplemented(); +} + +class FakeService : public Service { + public: + FakeService(uint32_t id) : Service(id, kMethods) {} + + static constexpr std::array<RawMethodUnion, 1> kMethods = { + RawMethod::Unary<FakeMethod>(kFakeMethodId), + }; +}; + +FakeService service(kFakeServiceId); + +TEST(ClientServer, ProcessPacket_CallsServer) { + ClientServer client_server(channels); + client_server.server().RegisterService(service); + + Packet packet( + PacketType::REQUEST, kFakeChannelId, kFakeServiceId, kFakeMethodId); + std::array<std::byte, 32> buffer; + Result result = packet.Encode(buffer); + EXPECT_EQ(result.status(), OkStatus()); + + EXPECT_EQ(client_server.ProcessPacket(result.value(), output), OkStatus()); +} + +TEST(ClientServer, ProcessPacket_CallsClient) { + ClientServer client_server(channels); + client_server.server().RegisterService(service); + + // Same packet as above, but type RESPONSE will skip the server and call into + // the client. + Packet packet( + PacketType::RESPONSE, kFakeChannelId, kFakeServiceId, kFakeMethodId); + std::array<std::byte, 32> buffer; + Result result = packet.Encode(buffer); + EXPECT_EQ(result.status(), OkStatus()); + + // No calls are registered on the client, so this should fail. + EXPECT_EQ(client_server.ProcessPacket(result.value(), output), + Status::NotFound()); +} + +TEST(ClientServer, ProcessPacket_BadData) { + ClientServer client_server(channels); + EXPECT_EQ(client_server.ProcessPacket({}, output), Status::DataLoss()); +} + +} // namespace +} // namespace pw::rpc::internal diff --git a/pw_rpc/docs.rst b/pw_rpc/docs.rst index fc041656a..daece792f 100644 --- a/pw_rpc/docs.rst +++ b/pw_rpc/docs.rst @@ -848,3 +848,26 @@ interfaces for working with RPCs. The RPC server stores a list of all of active ``ClientCall`` objects. When an incoming packet is recieved, it dispatches to one of its active calls, which then decodes the payload and presents it to the user. + +ClientServer +============ +Sometimes, a device needs to both process RPCs as a server, as well as making +calls to another device as a client. To do this, both a client and server must +be set up, and incoming packets must be sent to both of them. + +Pigweed simplifies this setup by providing a ``ClientServer`` class which wraps +an RPC client and server with the same set of channels. + +.. code-block:: cpp + + pw::rpc::Channel channels[] = { + pw::rpc::Channel::Create<1>(&channel_output)}; + + // Creates both a client and a server. + pw::rpc::ClientServer client_server(channels); + + void ProcessRpcData(pw::ConstByteSpan packet) { + // Calls into both the client and the server, sending the packet to the + // appropriate one. + client_server.ProcessPacket(packet, output); + } diff --git a/pw_rpc/public/pw_rpc/client_server.h b/pw_rpc/public/pw_rpc/client_server.h new file mode 100644 index 000000000..219ed676c --- /dev/null +++ b/pw_rpc/public/pw_rpc/client_server.h @@ -0,0 +1,40 @@ +// Copyright 2021 The Pigweed Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +#pragma once + +#include "pw_rpc/client.h" +#include "pw_rpc/server.h" + +namespace pw::rpc { + +// Class that wraps both an RPC client and a server, simplifying RPC setup when +// a device needs to function as both. +class ClientServer { + public: + constexpr ClientServer(std::span<Channel> channels) + : client_(channels), server_(channels) {} + + // Sends a packet to either the client or the server, depending on its type. + Status ProcessPacket(std::span<const std::byte> packet, + ChannelOutput& interface); + + constexpr Client& client() { return client_; } + constexpr Server& server() { return server_; } + + private: + Client client_; + Server server_; +}; + +} // namespace pw::rpc |