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 : #if INET_CONFIG_ENABLE_TCP_ENDPOINT
97 0 : CHIP_ERROR TCPConnect(const PeerAddress & address, Transport::AppTCPConnectionCallbackCtxt * appState,
98 : Transport::ActiveTCPConnectionState ** peerConnState) override
99 : {
100 0 : return TCPConnectImpl<0>(address, appState, peerConnState);
101 : }
102 :
103 0 : void TCPDisconnect(const PeerAddress & address) override { return TCPDisconnectImpl<0>(address); }
104 :
105 0 : void TCPDisconnect(Transport::ActiveTCPConnectionState * conn, bool shouldAbort = 0) override
106 : {
107 0 : return TCPDisconnectImpl<0>(conn, shouldAbort);
108 : }
109 :
110 : #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT
111 :
112 1 : void Close() override { return CloseImpl<0>(); }
113 :
114 : /**
115 : * Initialization method that forwards arguments for initialization to each of the underlying
116 : * transports.
117 : *
118 : * Transports are assumed to have an Init call with EXACTLY one argument. This method MUST initialize
119 : * all underlying transports.
120 : *
121 : * @param delegate the delegate to handle messages.
122 : * @param args initialization arguments, forwarded as-is to the underlying transports.
123 : */
124 : template <typename... Args, typename std::enable_if<(sizeof...(Args) == sizeof...(TransportTypes))>::type * = nullptr>
125 1 : CHIP_ERROR Init(RawTransportDelegate * delegate, Args &&... args)
126 : {
127 1 : return InitImpl(delegate, std::forward<Args>(args)...);
128 : }
129 :
130 : private:
131 : /**
132 : * Recursive cansend implementation iterating through transport members.
133 : *
134 : * Will return true if any transport with index N and up can CanSendToPeer(address);
135 : *
136 : * @tparam N the index of the underlying transport to check for CanSendToPeer
137 : *
138 : * @param address what address to check.
139 : */
140 : template <size_t N, typename std::enable_if<(N < sizeof...(TransportTypes))>::type * = nullptr>
141 0 : bool CanSendToPeerImpl(const PeerAddress & address)
142 : {
143 0 : return std::get<N>(mTransports).CanSendToPeer(address) || CanSendToPeerImpl<N + 1>(address);
144 : }
145 :
146 : /**
147 : * CanSend template for out of range N. Always returns false.
148 : */
149 : template <size_t N, typename std::enable_if<(N >= sizeof...(TransportTypes))>::type * = nullptr>
150 0 : bool CanSendToPeerImpl(const PeerAddress & address)
151 : {
152 0 : return false;
153 : }
154 :
155 : #if INET_CONFIG_ENABLE_TCP_ENDPOINT
156 : /**
157 : * Recursive TCPConnect implementation iterating through transport members.
158 : *
159 : * @tparam N the index of the underlying transport to send disconnect to
160 : *
161 : * @param address what address to connect to.
162 : */
163 : template <size_t N, typename std::enable_if<(N < sizeof...(TransportTypes))>::type * = nullptr>
164 0 : CHIP_ERROR TCPConnectImpl(const PeerAddress & address, Transport::AppTCPConnectionCallbackCtxt * appState,
165 : Transport::ActiveTCPConnectionState ** peerConnState)
166 : {
167 0 : Base * base = &std::get<N>(mTransports);
168 0 : if (base->CanSendToPeer(address))
169 : {
170 0 : return base->TCPConnect(address, appState, peerConnState);
171 : }
172 0 : return TCPConnectImpl<N + 1>(address, appState, peerConnState);
173 : }
174 :
175 : /**
176 : * TCPConnectImpl template for out of range N.
177 : */
178 : template <size_t N, typename std::enable_if<(N >= sizeof...(TransportTypes))>::type * = nullptr>
179 0 : CHIP_ERROR TCPConnectImpl(const PeerAddress & address, Transport::AppTCPConnectionCallbackCtxt * appState,
180 : Transport::ActiveTCPConnectionState ** peerConnState)
181 : {
182 0 : return CHIP_ERROR_NO_MESSAGE_HANDLER;
183 : }
184 :
185 : /**
186 : * Recursive disconnect implementation iterating through transport members.
187 : *
188 : * @tparam N the index of the underlying transport to send disconnect to
189 : *
190 : * @param address what address to disconnect from.
191 : */
192 : template <size_t N, typename std::enable_if<(N < sizeof...(TransportTypes))>::type * = nullptr>
193 0 : void TCPDisconnectImpl(const PeerAddress & address)
194 : {
195 0 : std::get<N>(mTransports).TCPDisconnect(address);
196 0 : TCPDisconnectImpl<N + 1>(address);
197 0 : }
198 :
199 : /**
200 : * TCPDisconnectImpl template for out of range N.
201 : */
202 : template <size_t N, typename std::enable_if<(N >= sizeof...(TransportTypes))>::type * = nullptr>
203 0 : void TCPDisconnectImpl(const PeerAddress & address)
204 0 : {}
205 :
206 : /**
207 : * Recursive disconnect implementation iterating through transport members.
208 : *
209 : * @tparam N the index of the underlying transport to send disconnect to
210 : *
211 : * @param conn pointer to the connection to the peer.
212 : */
213 : template <size_t N, typename std::enable_if<(N < sizeof...(TransportTypes))>::type * = nullptr>
214 0 : void TCPDisconnectImpl(Transport::ActiveTCPConnectionState * conn, bool shouldAbort = 0)
215 : {
216 0 : std::get<N>(mTransports).TCPDisconnect(conn, shouldAbort);
217 0 : TCPDisconnectImpl<N + 1>(conn, shouldAbort);
218 0 : }
219 :
220 : /**
221 : * TCPDisconnectImpl template for out of range N.
222 : */
223 : template <size_t N, typename std::enable_if<(N >= sizeof...(TransportTypes))>::type * = nullptr>
224 0 : void TCPDisconnectImpl(Transport::ActiveTCPConnectionState * conn, bool shouldAbort = 0)
225 0 : {}
226 : #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT
227 :
228 : /**
229 : * Recursive disconnect implementation iterating through transport members.
230 : *
231 : * @tparam N the index of the underlying transport to send close to
232 : */
233 : template <size_t N, typename std::enable_if<(N < sizeof...(TransportTypes))>::type * = nullptr>
234 4 : void CloseImpl()
235 : {
236 4 : std::get<N>(mTransports).Close();
237 4 : CloseImpl<N + 1>();
238 4 : }
239 :
240 : /**
241 : * CloseImpl template for out of range N.
242 : */
243 : template <size_t N, typename std::enable_if<(N >= sizeof...(TransportTypes))>::type * = nullptr>
244 1 : void CloseImpl()
245 1 : {}
246 :
247 : /**
248 : * Recursive sendmessage implementation iterating through transport members.
249 : *
250 : * Message is sent through the first transport from index N or above, which returns 'CanSendToPeer'
251 : *
252 : * @tparam N the index of the underlying transport to run SendMessage throug.
253 : *
254 : * @param address where to send the message
255 : * @param msgBuf the message to send. Includes all CHIP message fields except optional length.
256 : */
257 : template <size_t N, typename std::enable_if<(N < sizeof...(TransportTypes))>::type * = nullptr>
258 0 : CHIP_ERROR SendMessageImpl(const PeerAddress & address, System::PacketBufferHandle && msgBuf)
259 : {
260 0 : Base * base = &std::get<N>(mTransports);
261 0 : if (base->CanSendToPeer(address))
262 : {
263 0 : return base->SendMessage(address, std::move(msgBuf));
264 : }
265 0 : return SendMessageImpl<N + 1>(address, std::move(msgBuf));
266 : }
267 :
268 : /**
269 : * SendMessageImpl when N is out of range. Always returns an error code.
270 : */
271 : template <size_t N, typename std::enable_if<(N >= sizeof...(TransportTypes))>::type * = nullptr>
272 0 : CHIP_ERROR SendMessageImpl(const PeerAddress & address, System::PacketBufferHandle msgBuf)
273 : {
274 0 : return CHIP_ERROR_NO_MESSAGE_HANDLER;
275 : }
276 :
277 : /**
278 : * Recursive GroupJoinLeave implementation iterating through transport members.
279 : *
280 : * Listener is activated through the first transport from index N or above, which returns 'CanListenMulticast'
281 : *
282 : * @tparam N the index of the underlying transport to run GroupJoinLeave through.
283 : *
284 : * @param address where to send the message
285 : * @param join a boolean indicating if the transport should join or leave the group
286 : */
287 : template <size_t N, typename std::enable_if<(N < sizeof...(TransportTypes))>::type * = nullptr>
288 0 : CHIP_ERROR MulticastGroupJoinLeaveImpl(const Transport::PeerAddress & address, bool join)
289 : {
290 0 : Base * base = &std::get<N>(mTransports);
291 0 : if (base->CanListenMulticast())
292 : {
293 0 : return base->MulticastGroupJoinLeave(address, join);
294 : }
295 0 : return MulticastGroupJoinLeaveImpl<N + 1>(address, join);
296 : }
297 :
298 : /**
299 : * GroupJoinLeave when N is out of range. Always returns an error code.
300 : */
301 : template <size_t N, typename std::enable_if<(N >= sizeof...(TransportTypes))>::type * = nullptr>
302 0 : CHIP_ERROR MulticastGroupJoinLeaveImpl(const Transport::PeerAddress & address, bool join)
303 : {
304 0 : return CHIP_ERROR_NO_MESSAGE_HANDLER;
305 : }
306 :
307 : /**
308 : * Recursive init implementation iterating through transport members
309 : *
310 : * Given a set of arguments 'a1, a2, a3, ... aN' will call an Init method on the last N
311 : * transports.
312 : *
313 : * Method is expected to be called initially with exactly sizeof(TransportTypes) to initialize
314 : * all transports.
315 : *
316 : * @param arg the next initialize argument to pass to the transport Init method
317 : * @param rest tail arguments to be passed to the rest of transport Init methods.
318 : */
319 : template <typename InitArg, typename... Rest>
320 4 : CHIP_ERROR InitImpl(RawTransportDelegate * delegate, InitArg && arg, Rest &&... rest)
321 : {
322 4 : auto transport = &std::get<sizeof...(TransportTypes) - sizeof...(Rest) - 1>(mTransports);
323 :
324 4 : CHIP_ERROR err = transport->Init(std::forward<InitArg>(arg));
325 4 : if (err != CHIP_NO_ERROR)
326 : {
327 0 : return err;
328 : }
329 :
330 4 : transport->SetDelegate(delegate);
331 :
332 4 : return InitImpl(delegate, std::forward<Rest>(rest)...);
333 : }
334 :
335 : /**
336 : * Base case where initialization finishes.
337 : *
338 : * Provided to ensure that recursive InitImpl finishes compiling.
339 : */
340 1 : CHIP_ERROR InitImpl(RawTransportDelegate * delegate) { return CHIP_NO_ERROR; }
341 :
342 : std::tuple<TransportTypes...> mTransports;
343 :
344 : public:
345 : template <size_t i>
346 1 : auto GetImplAtIndex() -> decltype(std::get<i>(mTransports)) &
347 : {
348 1 : return std::get<i>(mTransports);
349 : }
350 :
351 : std::tuple<TransportTypes...> & GetTransports() { return mTransports; }
352 : };
353 :
354 : } // namespace Transport
355 : } // namespace chip
|