| // 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/server.h" |
| |
| #include "pw_log/log.h" |
| #include "pw_rpc/internal/method.h" |
| #include "pw_rpc/internal/packet.h" |
| #include "pw_rpc/server_context.h" |
| |
| namespace pw::rpc { |
| |
| using std::byte; |
| |
| using internal::Method; |
| using internal::Packet; |
| using internal::PacketType; |
| |
| void Server::ProcessPacket(span<const byte> data, ChannelOutput& interface) { |
| Packet packet = Packet::FromBuffer(data); |
| if (packet.is_control()) { |
| // TODO(frolv): Handle control packets. |
| return; |
| } |
| |
| if (packet.channel_id() == Channel::kUnassignedChannelId || |
| packet.service_id() == 0 || packet.method_id() == 0) { |
| // Malformed packet; don't even try to process it. |
| PW_LOG_ERROR("Received incomplete RPC packet on interface %s", |
| interface.name()); |
| return; |
| } |
| |
| Packet response(PacketType::RPC); |
| |
| Channel* channel = FindChannel(packet.channel_id()); |
| if (channel == nullptr) { |
| // If the requested channel doesn't exist, try to dynamically assign one. |
| channel = AssignChannel(packet.channel_id(), interface); |
| if (channel == nullptr) { |
| // If a channel can't be assigned, send back a response indicating that |
| // the server cannot process the request. The channel_id in the response |
| // is not set, to allow clients to detect this error case. |
| Channel temp_channel(packet.channel_id(), &interface); |
| response.set_status(Status::RESOURCE_EXHAUSTED); |
| SendResponse(temp_channel, response, temp_channel.AcquireBuffer()); |
| return; |
| } |
| } |
| |
| response.set_channel_id(channel->id()); |
| const span<byte> response_buffer = channel->AcquireBuffer(); |
| |
| // Invoke the method with matching service and method IDs, if any. |
| InvokeMethod(packet, *channel, response, response_buffer); |
| SendResponse(*channel, response, response_buffer); |
| } |
| |
| void Server::InvokeMethod(const Packet& request, |
| Channel& channel, |
| internal::Packet& response, |
| span<std::byte> buffer) { |
| internal::Service* service = services_.Find(request.service_id()); |
| if (service == nullptr) { |
| // Couldn't find the requested service. Reply with a NOT_FOUND response |
| // without the service_id field set. |
| response.set_status(Status::NOT_FOUND); |
| return; |
| } |
| |
| response.set_service_id(service->id()); |
| |
| const internal::Method* method = service->FindMethod(request.method_id()); |
| |
| if (method == nullptr) { |
| // Couldn't find the requested method. Reply with a NOT_FOUND response |
| // without the method_id field set. |
| response.set_status(Status::NOT_FOUND); |
| return; |
| } |
| |
| response.set_method_id(method->id()); |
| |
| span<byte> response_buffer = request.PayloadUsableSpace(buffer); |
| |
| internal::ServerCall call(*this, channel, *service, *method); |
| StatusWithSize result = |
| method->Invoke(call, request.payload(), response_buffer); |
| |
| response.set_status(result.status()); |
| response.set_payload(response_buffer.first(result.size())); |
| } |
| |
| Channel* Server::FindChannel(uint32_t id) const { |
| for (Channel& c : channels_) { |
| if (c.id() == id) { |
| return &c; |
| } |
| } |
| return nullptr; |
| } |
| |
| Channel* Server::AssignChannel(uint32_t id, ChannelOutput& interface) { |
| Channel* channel = FindChannel(Channel::kUnassignedChannelId); |
| if (channel == nullptr) { |
| return nullptr; |
| } |
| |
| *channel = Channel(id, &interface); |
| return channel; |
| } |
| |
| void Server::SendResponse(const Channel& channel, |
| const Packet& response, |
| span<byte> response_buffer) const { |
| StatusWithSize sws = response.Encode(response_buffer); |
| if (!sws.ok()) { |
| // TODO(frolv): What should be done here? |
| channel.SendAndReleaseBuffer(0); |
| PW_LOG_ERROR("Failed to encode response packet to channel buffer"); |
| return; |
| } |
| |
| channel.SendAndReleaseBuffer(sws.size()); |
| } |
| |
| static_assert(std::is_base_of<internal::BaseMethod, internal::Method>(), |
| "The Method implementation must be derived from " |
| "pw::rpc::internal::BaseMethod"); |
| |
| } // namespace pw::rpc |