Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020 Project CHIP Authors
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : /**
19 : * @file
20 : *
21 : * Defines a compound transport type (tuple) that can merge several transports
22 : * to look like a single one.
23 : */
24 : #pragma once
25 :
26 : #include <tuple>
27 : #include <type_traits>
28 :
29 : #include <transport/raw/Base.h>
30 :
31 : namespace chip {
32 : namespace Transport {
33 :
34 : /**
35 : * Groups together several transports of different types and presents them as a unified transport.
36 : *
37 : * The usage intent is to be able to group several distinct transport types and make them look
38 : * as a single transport.
39 : *
40 : * Having an example class definition of `Tuple<UDP, UDP, TCP>`
41 : *
42 : * Is equivalent to:
43 : *
44 : * ~~~~~~~~~
45 : * class TupleOfUdpUdpTcp : public BASE {
46 : * private:
47 : * UDP mUdp1;
48 : * UDP mUdp2;
49 : * TCP mTcp3;
50 : * public:
51 : * Init(UDPListenParameters &, UDPListenParameters&, TCPListenParameters) {...}
52 : * CHIP_ERROR SendMessage(...) override;
53 : * bool CanSendToPeer(...) override;
54 : * }
55 : * ~~~~~~~~~
56 : *
57 : * The intent of this is to allow applications to use any transport types without CHIP pre-defining
58 : * popular mappings (like UDP only, UDP and BLE, BLE only etc.) and without using \#ifdefs to create
59 : * a single 'standard transport'.
60 : *
61 : * Transport logic:
62 : * - Every transport can decide if a 'PeerAddress' can be sent over 'self'
63 : *
64 : * - Within a mixed tuple, if several transports support a peer address (e.g. TCP and UDP both
65 : * support IP), the first one found wins.
66 : *
67 : * - Expected message sending logic:
68 : * - BLE transports only support BLE. TCP/UDP support IP addresses
69 : * - Multicasts only supported by UDP
70 : *
71 : * @tparam TransportTypes the ORDERED list of transport types grouped together. Order matters in
72 : * determining what transport is used when multiple transports can reach a Peer.
73 : *
74 : * Transports (TransportTypes) are assumed to have an Init call with EXACTLY one argument and returning
75 : * a CHIP_ERROR, with a signature like:
76 : *
77 : * CHIP_ERROR Init(AnyType);
78 : *
79 : */
80 : template <typename... TransportTypes>
81 : class Tuple : public Base
82 : {
83 : public:
84 0 : CHIP_ERROR SendMessage(const PeerAddress & address, System::PacketBufferHandle && msgBuf) override
85 : {
86 0 : return SendMessageImpl<0>(address, std::move(msgBuf));
87 : }
88 :
89 0 : CHIP_ERROR MulticastGroupJoinLeave(const Transport::PeerAddress & address, bool join) override
90 : {
91 0 : return MulticastGroupJoinLeaveImpl<0>(address, join);
92 : }
93 :
94 0 : bool CanSendToPeer(const PeerAddress & address) override { return CanSendToPeerImpl<0>(address); }
95 :
96 0 : void Disconnect(const PeerAddress & address) override { return DisconnectImpl<0>(address); }
97 :
98 1 : void Close() override { return CloseImpl<0>(); }
99 :
100 : /**
101 : * Initialization method that forwards arguments for initialization to each of the underlying
102 : * transports.
103 : *
104 : * Transports are assumed to have an Init call with EXACTLY one argument. This method MUST initialize
105 : * all underlying transports.
106 : *
107 : * @param delegate the delegate to handle messages.
108 : * @param args initialization arguments, forwarded as-is to the underlying transports.
109 : */
110 : template <typename... Args, typename std::enable_if<(sizeof...(Args) == sizeof...(TransportTypes))>::type * = nullptr>
111 1 : CHIP_ERROR Init(RawTransportDelegate * delegate, Args &&... args)
112 : {
113 1 : return InitImpl(delegate, std::forward<Args>(args)...);
114 : }
115 :
116 : private:
117 : /**
118 : * Recursive cansend implementation iterating through transport members.
119 : *
120 : * Will return true if any transport with index N and up can CanSendToPeer(address);
121 : *
122 : * @tparam N the index of the underlying transport to check for CanSendToPeer
123 : *
124 : * @param address what address to check.
125 : */
126 : template <size_t N, typename std::enable_if<(N < sizeof...(TransportTypes))>::type * = nullptr>
127 0 : bool CanSendToPeerImpl(const PeerAddress & address)
128 : {
129 0 : return std::get<N>(mTransports).CanSendToPeer(address) || CanSendToPeerImpl<N + 1>(address);
130 : }
131 :
132 : /**
133 : * CanSend template for out of range N. Always returns false.
134 : */
135 : template <size_t N, typename std::enable_if<(N >= sizeof...(TransportTypes))>::type * = nullptr>
136 0 : bool CanSendToPeerImpl(const PeerAddress & address)
137 : {
138 0 : return false;
139 : }
140 :
141 : /**
142 : * Recursive disconnect implementation iterating through transport members.
143 : *
144 : * @tparam N the index of the underlying transport to send disconnect to
145 : *
146 : * @param address what address to check.
147 : */
148 : template <size_t N, typename std::enable_if<(N < sizeof...(TransportTypes))>::type * = nullptr>
149 0 : void DisconnectImpl(const PeerAddress & address)
150 : {
151 0 : std::get<N>(mTransports).Disconnect(address);
152 0 : DisconnectImpl<N + 1>(address);
153 0 : }
154 :
155 : /**
156 : * DisconnectImpl template for out of range N.
157 : */
158 : template <size_t N, typename std::enable_if<(N >= sizeof...(TransportTypes))>::type * = nullptr>
159 0 : void DisconnectImpl(const PeerAddress & address)
160 0 : {}
161 :
162 : /**
163 : * Recursive disconnect implementation iterating through transport members.
164 : *
165 : * @tparam N the index of the underlying transport to send close to
166 : */
167 : template <size_t N, typename std::enable_if<(N < sizeof...(TransportTypes))>::type * = nullptr>
168 3 : void CloseImpl()
169 : {
170 3 : std::get<N>(mTransports).Close();
171 3 : CloseImpl<N + 1>();
172 3 : }
173 :
174 : /**
175 : * CloseImpl template for out of range N.
176 : */
177 : template <size_t N, typename std::enable_if<(N >= sizeof...(TransportTypes))>::type * = nullptr>
178 1 : void CloseImpl()
179 1 : {}
180 :
181 : /**
182 : * Recursive sendmessage implementation iterating through transport members.
183 : *
184 : * Message is sent through the first transport from index N or above, which returns 'CanSendToPeer'
185 : *
186 : * @tparam N the index of the underlying transport to run SendMessage throug.
187 : *
188 : * @param address where to send the message
189 : * @param msgBuf the message to send. Includes all CHIP message fields except optional length.
190 : */
191 : template <size_t N, typename std::enable_if<(N < sizeof...(TransportTypes))>::type * = nullptr>
192 0 : CHIP_ERROR SendMessageImpl(const PeerAddress & address, System::PacketBufferHandle && msgBuf)
193 : {
194 0 : Base * base = &std::get<N>(mTransports);
195 0 : if (base->CanSendToPeer(address))
196 : {
197 0 : return base->SendMessage(address, std::move(msgBuf));
198 : }
199 0 : return SendMessageImpl<N + 1>(address, std::move(msgBuf));
200 : }
201 :
202 : /**
203 : * SendMessageImpl when N is out of range. Always returns an error code.
204 : */
205 : template <size_t N, typename std::enable_if<(N >= sizeof...(TransportTypes))>::type * = nullptr>
206 0 : CHIP_ERROR SendMessageImpl(const PeerAddress & address, System::PacketBufferHandle msgBuf)
207 : {
208 0 : return CHIP_ERROR_NO_MESSAGE_HANDLER;
209 : }
210 :
211 : /**
212 : * Recursive GroupJoinLeave implementation iterating through transport members.
213 : *
214 : * Listener is activated through the first transport from index N or above, which returns 'CanListenMulticast'
215 : *
216 : * @tparam N the index of the underlying transport to run GroupJoinLeave through.
217 : *
218 : * @param address where to send the message
219 : * @param join a boolean indicating if the transport should join or leave the group
220 : */
221 : template <size_t N, typename std::enable_if<(N < sizeof...(TransportTypes))>::type * = nullptr>
222 0 : CHIP_ERROR MulticastGroupJoinLeaveImpl(const Transport::PeerAddress & address, bool join)
223 : {
224 0 : Base * base = &std::get<N>(mTransports);
225 0 : if (base->CanListenMulticast())
226 : {
227 0 : return base->MulticastGroupJoinLeave(address, join);
228 : }
229 0 : return MulticastGroupJoinLeaveImpl<N + 1>(address, join);
230 : }
231 :
232 : /**
233 : * GroupJoinLeave when N is out of range. Always returns an error code.
234 : */
235 : template <size_t N, typename std::enable_if<(N >= sizeof...(TransportTypes))>::type * = nullptr>
236 0 : CHIP_ERROR MulticastGroupJoinLeaveImpl(const Transport::PeerAddress & address, bool join)
237 : {
238 0 : return CHIP_ERROR_NO_MESSAGE_HANDLER;
239 : }
240 :
241 : /**
242 : * Recursive init implementation iterating through transport members
243 : *
244 : * Given a set of arguments 'a1, a2, a3, ... aN' will call an Init method on the last N
245 : * transports.
246 : *
247 : * Method is expected to be called initially with exactly sizeof(TransportTypes) to initialize
248 : * all transports.
249 : *
250 : * @param arg the next initialize argument to pass to the transport Init method
251 : * @param rest tail arguments to be passed to the rest of transport Init methods.
252 : */
253 : template <typename InitArg, typename... Rest>
254 3 : CHIP_ERROR InitImpl(RawTransportDelegate * delegate, InitArg && arg, Rest &&... rest)
255 : {
256 3 : auto transport = &std::get<sizeof...(TransportTypes) - sizeof...(Rest) - 1>(mTransports);
257 :
258 3 : CHIP_ERROR err = transport->Init(std::forward<InitArg>(arg));
259 3 : if (err != CHIP_NO_ERROR)
260 : {
261 0 : return err;
262 : }
263 :
264 3 : transport->SetDelegate(delegate);
265 :
266 3 : return InitImpl(delegate, std::forward<Rest>(rest)...);
267 : }
268 :
269 : /**
270 : * Base case where initialization finishes.
271 : *
272 : * Provided to ensure that recursive InitImpl finishes compiling.
273 : */
274 1 : CHIP_ERROR InitImpl(RawTransportDelegate * delegate) { return CHIP_NO_ERROR; }
275 :
276 : std::tuple<TransportTypes...> mTransports;
277 :
278 : public:
279 : template <size_t i>
280 1 : auto GetImplAtIndex() -> decltype(std::get<i>(mTransports)) &
281 : {
282 1 : return std::get<i>(mTransports);
283 : }
284 :
285 : std::tuple<TransportTypes...> & GetTransports() { return mTransports; }
286 : };
287 :
288 : } // namespace Transport
289 : } // namespace chip
|