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 "pw_bytes/span.h"
17 #include "pw_rpc/internal/base_server_writer.h"
18 #include "pw_rpc/internal/method.h"
19 #include "pw_rpc/internal/method_type.h"
20 #include "pw_status/status_with_size.h"
21 
22 namespace pw::rpc {
23 
24 class RawServerWriter : public internal::BaseServerWriter {
25  public:
26   RawServerWriter() = default;
27   RawServerWriter(RawServerWriter&&) = default;
28   RawServerWriter& operator=(RawServerWriter&&) = default;
29 
30   ~RawServerWriter();
31 
32   // Returns a buffer in which a response payload can be built.
PayloadBuffer()33   ByteSpan PayloadBuffer() { return AcquirePayloadBuffer(); }
34 
35   // Sends a response packet with the given raw payload. The payload can either
36   // be in the buffer previously acquired from PayloadBuffer(), or an arbitrary
37   // external buffer.
38   Status Write(ConstByteSpan response);
39 };
40 
41 namespace internal {
42 
43 // A RawMethod is a method invoker which does not perform any automatic protobuf
44 // serialization or deserialization. The implementer is given the raw binary
45 // payload of incoming requests, and is responsible for encoding responses to a
46 // provided buffer. This is intended for use in methods which would have large
47 // protobuf data structure overhead to lower stack usage, or in methods packing
48 // responses up to a channel's MTU.
49 class RawMethod : public Method {
50  public:
51   template <auto method>
matches()52   static constexpr bool matches() {
53     return std::is_same_v<MethodImplementation<method>, RawMethod>;
54   }
55 
56   template <auto method>
Unary(uint32_t id)57   static constexpr RawMethod Unary(uint32_t id) {
58     constexpr UnaryFunction wrapper =
59         [](ServerCall& call, ConstByteSpan req, ByteSpan res) {
60           return CallMethodImplFunction<method>(call, req, res);
61         };
62     return RawMethod(id, UnaryInvoker, Function{.unary = wrapper});
63   }
64 
65   template <auto method>
ServerStreaming(uint32_t id)66   static constexpr RawMethod ServerStreaming(uint32_t id) {
67     constexpr ServerStreamingFunction wrapper =
68         [](ServerCall& call, ConstByteSpan request, BaseServerWriter& writer) {
69           CallMethodImplFunction<method>(
70               call, request, static_cast<RawServerWriter&>(writer));
71         };
72     return RawMethod(
73         id, ServerStreamingInvoker, Function{.server_streaming = wrapper});
74   }
75 
76   // Represents an invalid method. Used to reduce error message verbosity.
Invalid()77   static constexpr RawMethod Invalid() { return {0, InvalidInvoker, {}}; }
78 
79  private:
80   using UnaryFunction = StatusWithSize (*)(ServerCall&,
81                                            ConstByteSpan,
82                                            ByteSpan);
83 
84   using ServerStreamingFunction = void (*)(ServerCall&,
85                                            ConstByteSpan,
86                                            BaseServerWriter&);
87   union Function {
88     UnaryFunction unary;
89     ServerStreamingFunction server_streaming;
90     // TODO(frolv): Support client and bidirectional streaming.
91   };
92 
RawMethod(uint32_t id,Invoker invoker,Function function)93   constexpr RawMethod(uint32_t id, Invoker invoker, Function function)
94       : Method(id, invoker), function_(function) {}
95 
UnaryInvoker(const Method & method,ServerCall & call,const Packet & request)96   static void UnaryInvoker(const Method& method,
97                            ServerCall& call,
98                            const Packet& request) {
99     static_cast<const RawMethod&>(method).CallUnary(call, request);
100   }
101 
ServerStreamingInvoker(const Method & method,ServerCall & call,const Packet & request)102   static void ServerStreamingInvoker(const Method& method,
103                                      ServerCall& call,
104                                      const Packet& request) {
105     static_cast<const RawMethod&>(method).CallServerStreaming(call, request);
106   }
107 
108   void CallUnary(ServerCall& call, const Packet& request) const;
109   void CallServerStreaming(ServerCall& call, const Packet& request) const;
110 
111   // Stores the user-defined RPC in a generic wrapper.
112   Function function_;
113 };
114 
115 // MethodTraits specialization for a static raw unary method.
116 template <>
117 struct MethodTraits<StatusWithSize (*)(
118     ServerContext&, ConstByteSpan, ByteSpan)> {
119   using Implementation = RawMethod;
120   static constexpr MethodType kType = MethodType::kUnary;
121 };
122 
123 // MethodTraits specialization for a raw unary method.
124 template <typename T>
125 struct MethodTraits<StatusWithSize (T::*)(
126     ServerContext&, ConstByteSpan, ByteSpan)> {
127   using Implementation = RawMethod;
128   static constexpr MethodType kType = MethodType::kUnary;
129   using Service = T;
130 };
131 
132 // MethodTraits specialization for a static raw server streaming method.
133 template <>
134 struct MethodTraits<void (*)(ServerContext&, ConstByteSpan, RawServerWriter&)> {
135   using Implementation = RawMethod;
136   static constexpr MethodType kType = MethodType::kServerStreaming;
137 };
138 
139 // MethodTraits specialization for a raw server streaming method.
140 template <typename T>
141 struct MethodTraits<void (T::*)(
142     ServerContext&, ConstByteSpan, RawServerWriter&)> {
143   using Implementation = RawMethod;
144   static constexpr MethodType kType = MethodType::kServerStreaming;
145   using Service = T;
146 };
147 
148 }  // namespace internal
149 }  // namespace pw::rpc
150