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