blob: b239f3d84fee20931e139de9685b2058bb2ab42c [file] [log] [blame]
Alexei Frolov5d6d3922020-05-08 13:57:02 -07001// 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
15#include "pw_rpc/server.h"
16
17#include "pw_log/log.h"
Wyatt Hepler80f26ff2020-06-01 09:30:17 -070018#include "pw_rpc/internal/method.h"
Alexei Frolov5d6d3922020-05-08 13:57:02 -070019#include "pw_rpc/internal/packet.h"
Wyatt Hepler80f26ff2020-06-01 09:30:17 -070020#include "pw_rpc/server_context.h"
Alexei Frolov5d6d3922020-05-08 13:57:02 -070021
22namespace pw::rpc {
23
Wyatt Hepler80f26ff2020-06-01 09:30:17 -070024using std::byte;
25
26using internal::Method;
Alexei Frolov5d6d3922020-05-08 13:57:02 -070027using internal::Packet;
Alexei Frolov33a1e8f2020-05-26 08:39:32 -070028using internal::PacketType;
Alexei Frolov5d6d3922020-05-08 13:57:02 -070029
Wyatt Hepler80f26ff2020-06-01 09:30:17 -070030void Server::ProcessPacket(span<const byte> data, ChannelOutput& interface) {
Alexei Frolov5d6d3922020-05-08 13:57:02 -070031 Packet packet = Packet::FromBuffer(data);
32 if (packet.is_control()) {
33 // TODO(frolv): Handle control packets.
34 return;
35 }
36
Alexei Frolov33a1e8f2020-05-26 08:39:32 -070037 if (packet.channel_id() == Channel::kUnassignedChannelId ||
38 packet.service_id() == 0 || packet.method_id() == 0) {
Alexei Frolov5d6d3922020-05-08 13:57:02 -070039 // Malformed packet; don't even try to process it.
Wyatt Hepler51a2eb72020-06-04 14:47:00 -070040 PW_LOG_ERROR("Received incomplete RPC packet on interface %s",
41 interface.name());
Alexei Frolov5d6d3922020-05-08 13:57:02 -070042 return;
43 }
44
Wyatt Hepler142616c2020-06-01 10:16:04 -070045 Packet response(PacketType::RPC);
Alexei Frolov33a1e8f2020-05-26 08:39:32 -070046
Alexei Frolov5d6d3922020-05-08 13:57:02 -070047 Channel* channel = FindChannel(packet.channel_id());
48 if (channel == nullptr) {
Alexei Frolov33a1e8f2020-05-26 08:39:32 -070049 // If the requested channel doesn't exist, try to dynamically assign one.
50 channel = AssignChannel(packet.channel_id(), interface);
51 if (channel == nullptr) {
52 // If a channel can't be assigned, send back a response indicating that
53 // the server cannot process the request. The channel_id in the response
54 // is not set, to allow clients to detect this error case.
55 Channel temp_channel(packet.channel_id(), &interface);
56 response.set_status(Status::RESOURCE_EXHAUSTED);
57 SendResponse(temp_channel, response, temp_channel.AcquireBuffer());
58 return;
59 }
Alexei Frolov5d6d3922020-05-08 13:57:02 -070060 }
61
Alexei Frolov33a1e8f2020-05-26 08:39:32 -070062 response.set_channel_id(channel->id());
Wyatt Hepler80f26ff2020-06-01 09:30:17 -070063 const span<byte> response_buffer = channel->AcquireBuffer();
Alexei Frolov5d6d3922020-05-08 13:57:02 -070064
Wyatt Hepler80f26ff2020-06-01 09:30:17 -070065 // Invoke the method with matching service and method IDs, if any.
66 InvokeMethod(packet, *channel, response, response_buffer);
67 SendResponse(*channel, response, response_buffer);
68}
69
70void Server::InvokeMethod(const Packet& request,
71 Channel& channel,
72 internal::Packet& response,
Wyatt Hepler7da973a2020-06-09 10:04:48 -070073 span<std::byte> buffer) {
74 internal::Service* service = services_.Find(request.service_id());
Alexei Frolov5d6d3922020-05-08 13:57:02 -070075 if (service == nullptr) {
Alexei Frolov33a1e8f2020-05-26 08:39:32 -070076 // Couldn't find the requested service. Reply with a NOT_FOUND response
Wyatt Hepler80f26ff2020-06-01 09:30:17 -070077 // without the service_id field set.
Alexei Frolov33a1e8f2020-05-26 08:39:32 -070078 response.set_status(Status::NOT_FOUND);
Alexei Frolov5d6d3922020-05-08 13:57:02 -070079 return;
80 }
81
Wyatt Hepler80f26ff2020-06-01 09:30:17 -070082 response.set_service_id(service->id());
83
84 const internal::Method* method = service->FindMethod(request.method_id());
85
86 if (method == nullptr) {
87 // Couldn't find the requested method. Reply with a NOT_FOUND response
88 // without the method_id field set.
89 response.set_status(Status::NOT_FOUND);
90 return;
91 }
92
93 response.set_method_id(method->id());
94
Wyatt Hepler80f26ff2020-06-01 09:30:17 -070095 span<byte> response_buffer = request.PayloadUsableSpace(buffer);
Wyatt Hepler7da973a2020-06-09 10:04:48 -070096
97 internal::ServerCall call(*this, channel, *service, *method);
Wyatt Hepler80f26ff2020-06-01 09:30:17 -070098 StatusWithSize result =
Wyatt Hepler7da973a2020-06-09 10:04:48 -070099 method->Invoke(call, request.payload(), response_buffer);
100
Wyatt Hepler80f26ff2020-06-01 09:30:17 -0700101 response.set_status(result.status());
102 response.set_payload(response_buffer.first(result.size()));
Alexei Frolov5d6d3922020-05-08 13:57:02 -0700103}
104
Alexei Frolov33a1e8f2020-05-26 08:39:32 -0700105Channel* Server::FindChannel(uint32_t id) const {
Alexei Frolov5d6d3922020-05-08 13:57:02 -0700106 for (Channel& c : channels_) {
107 if (c.id() == id) {
108 return &c;
109 }
110 }
111 return nullptr;
112}
113
Alexei Frolov33a1e8f2020-05-26 08:39:32 -0700114Channel* Server::AssignChannel(uint32_t id, ChannelOutput& interface) {
115 Channel* channel = FindChannel(Channel::kUnassignedChannelId);
116 if (channel == nullptr) {
117 return nullptr;
118 }
119
120 *channel = Channel(id, &interface);
121 return channel;
122}
123
124void Server::SendResponse(const Channel& channel,
125 const Packet& response,
Wyatt Hepler80f26ff2020-06-01 09:30:17 -0700126 span<byte> response_buffer) const {
Alexei Frolov33a1e8f2020-05-26 08:39:32 -0700127 StatusWithSize sws = response.Encode(response_buffer);
128 if (!sws.ok()) {
129 // TODO(frolv): What should be done here?
130 channel.SendAndReleaseBuffer(0);
131 PW_LOG_ERROR("Failed to encode response packet to channel buffer");
132 return;
133 }
134
135 channel.SendAndReleaseBuffer(sws.size());
136}
137
Wyatt Hepler80f26ff2020-06-01 09:30:17 -0700138static_assert(std::is_base_of<internal::BaseMethod, internal::Method>(),
139 "The Method implementation must be derived from "
140 "pw::rpc::internal::BaseMethod");
141
Alexei Frolov5d6d3922020-05-08 13:57:02 -0700142} // namespace pw::rpc