1 // Copyright 2020 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include <cstddef> 17 #include <span> 18 #include <tuple> 19 20 #include "pw_containers/intrusive_list.h" 21 #include "pw_rpc/channel.h" 22 #include "pw_rpc/internal/channel.h" 23 #include "pw_rpc/internal/endpoint.h" 24 #include "pw_rpc/internal/lock.h" 25 #include "pw_rpc/internal/method.h" 26 #include "pw_rpc/internal/method_info.h" 27 #include "pw_rpc/internal/server_call.h" 28 #include "pw_rpc/service.h" 29 #include "pw_status/status.h" 30 31 namespace pw::rpc { 32 33 class Server : public internal::Endpoint { 34 public: Server(std::span<Channel> channels)35 _PW_RPC_CONSTEXPR Server(std::span<Channel> channels) : Endpoint(channels) {} 36 37 // Registers one or more services with the server. This should not be called 38 // directly with a Service; instead, use a generated class which inherits 39 // from it. 40 // 41 // This function may be called with any number of services. Combining 42 // registration into fewer calls is preferred so the RPC mutex is only 43 // locked/unlocked once. 44 template <typename... OtherServices> RegisterService(Service & service,OtherServices &...services)45 void RegisterService(Service& service, OtherServices&... services) 46 PW_LOCKS_EXCLUDED(internal::rpc_lock()) { 47 internal::LockGuard lock(internal::rpc_lock()); 48 services_.push_front(service); // Register the first service 49 50 // Register any additional services by expanding the parameter pack. This 51 // is a fold expression of the comma operator. 52 (services_.push_front(services), ...); 53 } 54 55 // Processes an RPC packet. The packet may contain an RPC request or a control 56 // packet, the result of which is processed in this function. Returns whether 57 // the packet was able to be processed: 58 // 59 // OK - The packet was processed by the server. 60 // DATA_LOSS - Failed to decode the packet. 61 // INVALID_ARGUMENT - The packet is intended for a client, not a server. 62 // UNAVAILABLE - No RPC channel with the requested ID was found. 63 // 64 // ProcessPacket optionally accepts a ChannelOutput as a second argument. If 65 // provided, the server respond on that interface if an unknown channel is 66 // requested. ProcessPacket(ConstByteSpan packet_data)67 Status ProcessPacket(ConstByteSpan packet_data) 68 PW_LOCKS_EXCLUDED(internal::rpc_lock()) { 69 return ProcessPacket(packet_data, nullptr); 70 } ProcessPacket(ConstByteSpan packet_data,ChannelOutput & interface)71 Status ProcessPacket(ConstByteSpan packet_data, ChannelOutput& interface) 72 PW_LOCKS_EXCLUDED(internal::rpc_lock()) { 73 return ProcessPacket(packet_data, &interface); 74 } 75 76 private: 77 friend class internal::Call; 78 friend class ClientServer; 79 80 // Give call classes access to OpenContext. 81 friend class RawServerReaderWriter; 82 friend class RawServerWriter; 83 friend class RawServerReader; 84 friend class RawUnaryResponder; 85 86 template <typename, typename> 87 friend class NanopbServerReaderWriter; 88 template <typename> 89 friend class NanopbServerWriter; 90 template <typename, typename> 91 friend class NanopbServerReader; 92 template <typename> 93 friend class NanopbUnaryResponder; 94 95 // Creates a call context for a particular RPC. Unlike the CallContext 96 // constructor, this function checks the type of RPC at compile time. 97 template <auto kMethod, 98 MethodType kExpected, 99 typename ServiceImpl, 100 typename MethodImpl> OpenContext(uint32_t channel_id,ServiceImpl & service,const MethodImpl & method)101 internal::CallContext OpenContext(uint32_t channel_id, 102 ServiceImpl& service, 103 const MethodImpl& method) 104 PW_EXCLUSIVE_LOCKS_REQUIRED(internal::rpc_lock()) { 105 using Info = internal::MethodInfo<kMethod>; 106 if constexpr (kExpected == MethodType::kUnary) { 107 static_assert( 108 Info::kType == kExpected, 109 "UnaryResponder objects may only be opened for unary RPCs."); 110 } else if constexpr (kExpected == MethodType::kServerStreaming) { 111 static_assert( 112 Info::kType == kExpected, 113 "ServerWriters may only be opened for server streaming RPCs."); 114 } else if constexpr (kExpected == MethodType::kClientStreaming) { 115 static_assert( 116 Info::kType == kExpected, 117 "ServerReaders may only be opened for client streaming RPCs."); 118 } else if constexpr (kExpected == MethodType::kBidirectionalStreaming) { 119 static_assert(Info::kType == kExpected, 120 "ServerReaderWriters may only be opened for bidirectional " 121 "streaming RPCs."); 122 } 123 124 // Unrequested RPCs always use 0 as the call ID. When an actual request is 125 // sent, the call will be replaced with its real ID. 126 constexpr uint32_t kOpenCallId = 0; 127 128 return internal::CallContext( 129 *this, channel_id, service, method, kOpenCallId); 130 } 131 132 Status ProcessPacket(ConstByteSpan packet_data, ChannelOutput* interface) 133 PW_LOCKS_EXCLUDED(internal::rpc_lock()); 134 135 std::tuple<Service*, const internal::Method*> FindMethod( 136 const internal::Packet& packet) 137 PW_EXCLUSIVE_LOCKS_REQUIRED(internal::rpc_lock()); 138 139 void HandleClientStreamPacket(const internal::Packet& packet, 140 internal::Channel& channel, 141 internal::ServerCall* call) const 142 PW_UNLOCK_FUNCTION(internal::rpc_lock()); 143 144 // Remove these internal::Endpoint functions from the public interface. 145 using Endpoint::active_call_count; 146 using Endpoint::GetInternalChannel; 147 148 IntrusiveList<Service> services_ PW_GUARDED_BY(internal::rpc_lock()); 149 }; 150 151 } // namespace pw::rpc 152