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