blob: b239f3d84fee20931e139de9685b2058bb2ab42c [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 "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.
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);
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);
SendResponse(temp_channel, response, temp_channel.AcquireBuffer());
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.
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.
span<byte> response_buffer = request.PayloadUsableSpace(buffer);
internal::ServerCall call(*this, channel, *service, *method);
StatusWithSize result =
method->Invoke(call, request.payload(), response_buffer);
Channel* Server::FindChannel(uint32_t id) const {
for (Channel& c : channels_) {
if ( == 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?
PW_LOG_ERROR("Failed to encode response packet to channel buffer");
static_assert(std::is_base_of<internal::BaseMethod, internal::Method>(),
"The Method implementation must be derived from "
} // namespace pw::rpc