blob: 7f20a36a17fb41003c6abc6a2a04bb11905edc51 [file] [log] [blame]
// 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
// 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 <algorithm>
#include "pw_log/log.h"
#include "pw_rpc/internal/method.h"
#include "pw_rpc/internal/packet.h"
#include "pw_rpc/internal/server.h"
#include "pw_rpc/server_context.h"
namespace pw::rpc {
using std::byte;
using internal::Packet;
using internal::PacketType;
void Server::ProcessPacket(std::span<const byte> data,
ChannelOutput& interface) {
Packet packet = Packet::FromBuffer(data);
if (packet.is_control()) {
// TODO(frolv): Handle control packets.
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",;
Packet response(PacketType::RPC);
internal::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.
internal::Channel temp_channel(packet.channel_id(), &interface);
auto response_buffer = temp_channel.AcquireBuffer();
temp_channel.Send(response_buffer, response);
auto response_buffer = channel->AcquireBuffer();
// Invoke the method with matching service and method IDs, if any.
InvokeMethod(packet, *channel, response, response_buffer.payload(response));
channel->Send(response_buffer, response);
void Server::InvokeMethod(const Packet& request,
Channel& channel,
internal::Packet& response,
std::span<std::byte> payload_buffer) {
auto service = std::find_if(services_.begin(), services_.end(), [&](auto& s) {
return == request.service_id();
if (service == services_.end()) {
// Couldn't find the requested service. Reply with a NOT_FOUND response
// without the service_id field set.
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.
internal::ServerCall call(static_cast<internal::Server&>(*this),
StatusWithSize result =
method->Invoke(call, request.payload(), payload_buffer);
internal::Channel* Server::FindChannel(uint32_t id) const {
for (internal::Channel& c : channels_) {
if ( == id) {
return &c;
return nullptr;
internal::Channel* Server::AssignChannel(uint32_t id,
ChannelOutput& interface) {
internal::Channel* channel = FindChannel(Channel::kUnassignedChannelId);
if (channel == nullptr) {
return nullptr;
*channel = internal::Channel(id, &interface);
return channel;
static_assert(std::is_base_of<internal::BaseMethod, internal::Method>(),
"The Method implementation must be derived from "
} // namespace pw::rpc