Skip to content

Commit 09ded2b

Browse files
authored
Secure tunnel with Multiplexing (#78)
* refactor and update of Secure Tunnel and its API * Support for V2 WebSocket Protocol (Multiplexing)
1 parent 6e59b46 commit 09ded2b

15 files changed

+4676
-1695
lines changed

include/aws/iotdevice/iotdevice.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,21 @@ enum aws_iotdevice_error {
2121
AWS_ERROR_IOTDEVICE_DEFENDER_PUBLISH_FAILURE,
2222
AWS_ERROR_IOTDEVICE_DEFENDER_UNKNOWN_TASK_STATUS,
2323

24-
AWS_ERROR_IOTDEVICE_SECUTRE_TUNNELING_INVALID_STREAM,
25-
AWS_ERROR_IOTDEVICE_SECUTRE_TUNNELING_INCORRECT_MODE,
24+
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_STREAM,
25+
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INCORRECT_MODE,
26+
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_BAD_SERVICE_ID,
27+
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_OPTIONS_VALIDATION,
28+
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_STREAM_OPTIONS_VALIDATION,
29+
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_SECURE_TUNNEL_TERMINATED,
30+
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_WEBSOCKET_TIMEOUT,
31+
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PING_RESPONSE_TIMEOUT,
32+
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_DISCONNECTION,
33+
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_PROCESSING_FAILURE,
34+
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_OFFLINE_QUEUE_POLICY,
35+
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_UNEXPECTED_HANGUP,
36+
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_USER_REQUESTED_STOP,
37+
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_TERMINATED,
38+
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DECODE_FAILURE,
2639

2740
AWS_ERROR_END_IOTDEVICE_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_IOTDEVICE_PACKAGE_ID),
2841
};

include/aws/iotdevice/private/iotdevice_internals.h

Lines changed: 0 additions & 43 deletions
This file was deleted.

include/aws/iotdevice/private/secure_tunneling_impl.h

Lines changed: 229 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,60 +8,265 @@
88
#include <aws/iotdevice/secure_tunneling.h>
99

1010
#include <aws/common/condition_variable.h>
11+
#include <aws/common/hash_table.h>
1112
#include <aws/common/mutex.h>
13+
#include <aws/common/task_scheduler.h>
14+
#include <aws/http/proxy.h>
15+
#include <aws/http/websocket.h>
16+
#include <aws/io/socket.h>
1217
#include <aws/io/tls_channel_handler.h>
1318

19+
/**
20+
* The various states that the secure tunnel can be in. A secure tunnel has both a current state and a desired state.
21+
* Desired state is only allowed to be one of {STOPPED, CONNECTED, TERMINATED}. The secure tunnel transitions states
22+
* based on either
23+
* (1) changes in desired state, or
24+
* (2) external events.
25+
*
26+
* Most states are interruptible (in the sense of a change in desired state causing an immediate change in state) but
27+
* CONNECTING cannot be interrupted due to waiting for an asynchronous callback (that has no
28+
* cancel) to complete.
29+
*/
30+
enum aws_secure_tunnel_state {
31+
/*
32+
* The secure tunnel is not connected and not waiting for anything to happen.
33+
*
34+
* Next States:
35+
* CONNECTING - if the user invokes Connect() on the secure tunnel
36+
* TERMINATED - if the user releases the last ref count on the secure tunnel
37+
*/
38+
AWS_STS_STOPPED,
39+
40+
/*
41+
* The secure tunnel is attempting to connect to a remote endpoint and establish a WebSocket upgrade. This state is
42+
* not interruptible by any means other than WebSocket setup completion.
43+
*
44+
* Next States:
45+
* CONNECTED - if WebSocket handshake is successful and desired state is still CONNECTED
46+
* WEBSOCKET_SHUTDOWN - if the WebSocket completes setup with no error but desired state is not CONNECTED
47+
* PENDING_RECONNECT - if the WebSocket fails to complete setup and desired state is still CONNECTED
48+
* STOPPED - if the WebSocket fails to complete setup and desired state is not CONNECTED
49+
*/
50+
AWS_STS_CONNECTING,
51+
52+
/*
53+
* The secure tunnel is ready to perform user-requested operations.
54+
*
55+
* Next States:
56+
* WEBSOCKET_SHUTDOWN - desired state is no longer CONNECTED
57+
* PENDING_RECONNECT - unexpected WebSocket shutdown completion and desired state still CONNECTED
58+
* STOPPED - unexpected WebSocket shutdown completion and desired state no longer CONNECTED
59+
*/
60+
AWS_STS_CONNECTED,
61+
62+
/*
63+
* The secure tunnel is attempting to shut down a WebSocket connection cleanly by finishing the current operation
64+
* and then transmitting a STREAM RESET message to all open streams.
65+
*
66+
* Next States:
67+
* WEBSOCKET_SHUTDOWN - on sucessful (or unsuccessful) disconnection
68+
* PENDING_RECONNECT - unexpected WebSocket shutdown completion and desired state still CONNECTED
69+
* STOPPED - unexpected WebSocket shutdown completion and desired state no longer CONNECTED
70+
*/
71+
AWS_STS_CLEAN_DISCONNECT,
72+
73+
/*
74+
* The secure tunnel is waiting for the WebSocket to completely shut down. This state is not interruptible.
75+
*
76+
* Next States:
77+
* PENDING_RECONNECT - the WebSocket has shut down and desired state is still CONNECTED
78+
* STOPPED - the WebSocket has shut down and desired state is not CONNECTED
79+
*/
80+
AWS_STS_WEBSOCKET_SHUTDOWN,
81+
82+
/*
83+
* The secure tunnel is waiting for the reconnect timer to expire before attempting to connect again.
84+
*
85+
* Next States:
86+
* CONNECTING - the reconnect timer has expired and desired state is still CONNECTED
87+
* STOPPED - desired state is no longer CONNECTED
88+
*/
89+
AWS_STS_PENDING_RECONNECT,
90+
91+
/*
92+
* The secure tunnel is performing final shutdown and release of all resources. This state is only realized for
93+
* a non-observable instant of time (transition out of STOPPED).
94+
*/
95+
AWS_STS_TERMINATED
96+
};
97+
1498
struct data_tunnel_pair {
99+
struct aws_allocator *allocator;
15100
struct aws_byte_buf buf;
16101
struct aws_byte_cursor cur;
17102
const struct aws_secure_tunnel *secure_tunnel;
18103
bool length_prefix_written;
19104
};
20105

21-
struct aws_secure_tunnel_vtable {
22-
int (*connect)(struct aws_secure_tunnel *secure_tunnel);
23-
int (*send_data)(struct aws_secure_tunnel *secure_tunnel, const struct aws_byte_cursor *data);
24-
int (*send_stream_start)(struct aws_secure_tunnel *secure_tunnel);
25-
int (*send_stream_reset)(struct aws_secure_tunnel *secure_tunnel);
26-
int (*close)(struct aws_secure_tunnel *secure_tunnel);
106+
/*
107+
* Secure tunnel configuration
108+
*/
109+
struct aws_secure_tunnel_options_storage {
110+
struct aws_allocator *allocator;
111+
112+
/* backup */
113+
114+
struct aws_client_bootstrap *bootstrap;
115+
struct aws_socket_options socket_options;
116+
struct aws_http_proxy_options http_proxy_options;
117+
struct aws_http_proxy_config *http_proxy_config;
118+
struct aws_string *access_token;
119+
struct aws_string *client_token;
120+
121+
struct aws_string *endpoint_host;
122+
123+
/* Stream related info */
124+
int32_t stream_id;
125+
126+
struct aws_hash_table service_ids;
127+
128+
/* Callbacks */
129+
aws_secure_tunnel_message_received_fn *on_message_received;
130+
aws_secure_tunneling_on_connection_complete_fn *on_connection_complete;
131+
aws_secure_tunneling_on_connection_shutdown_fn *on_connection_shutdown;
132+
aws_secure_tunneling_on_stream_start_fn *on_stream_start;
133+
aws_secure_tunneling_on_stream_reset_fn *on_stream_reset;
134+
aws_secure_tunneling_on_session_reset_fn *on_session_reset;
135+
aws_secure_tunneling_on_stopped_fn *on_stopped;
136+
137+
aws_secure_tunneling_on_send_data_complete_fn *on_send_data_complete;
138+
aws_secure_tunneling_on_termination_complete_fn *on_termination_complete;
139+
void *secure_tunnel_on_termination_user_data;
140+
141+
void *user_data;
142+
enum aws_secure_tunneling_local_proxy_mode local_proxy_mode;
27143
};
28144

29-
struct aws_websocket_client_connection_options;
30-
struct aws_websocket_send_frame_options;
145+
struct aws_secure_tunnel_vtable {
146+
/* aws_high_res_clock_get_ticks */
147+
uint64_t (*get_current_time_fn)(void);
148+
149+
/* For test verification */
150+
int (*aws_websocket_client_connect_fn)(const struct aws_websocket_client_connection_options *options);
151+
152+
/* For test verification */
153+
int (*aws_websocket_send_frame_fn)(
154+
struct aws_websocket *websocket,
155+
const struct aws_websocket_send_frame_options *options);
31156

32-
struct aws_websocket_vtable {
33-
int (*client_connect)(const struct aws_websocket_client_connection_options *options);
34-
int (*send_frame)(struct aws_websocket *websocket, const struct aws_websocket_send_frame_options *options);
35-
void (*close)(struct aws_websocket *websocket, bool free_scarce_resources_immediately);
36-
void (*release)(struct aws_websocket *websocket);
157+
/* For test verification */
158+
void (*aws_websocket_release_fn)(struct aws_websocket *websocket);
159+
160+
/* For test verification */
161+
void (*aws_websocket_close_fn)(struct aws_websocket *websocket, bool free_scarce_resources_immediately);
162+
163+
void *vtable_user_data;
37164
};
38165

39166
struct aws_secure_tunnel {
40167
/* Static settings */
41-
struct aws_allocator *alloc;
42-
struct aws_secure_tunnel_options_storage *options_storage;
43-
struct aws_secure_tunnel_options *options;
168+
struct aws_allocator *allocator;
169+
struct aws_ref_count ref_count;
170+
171+
const struct aws_secure_tunnel_vtable *vtable;
172+
173+
/*
174+
* Secure tunnel configuration
175+
*/
176+
struct aws_secure_tunnel_options_storage *config;
177+
44178
struct aws_tls_ctx *tls_ctx;
45179
struct aws_tls_connection_options tls_con_opt;
46-
struct aws_secure_tunnel_vtable vtable;
47-
struct aws_websocket_vtable websocket_vtable;
48180

49-
struct aws_ref_count ref_count;
181+
/*
182+
* The recurrent task that runs all secure tunnel logic outside of external event callbacks. Bound to the secure
183+
* tunnel's event loop.
184+
*/
185+
struct aws_task service_task;
186+
187+
/*
188+
* Tracks when the secure tunnel's service task is next schedule to run. Is zero if the task is not scheduled to
189+
* run or we are in the middle of a service (so technically not scheduled too).
190+
*/
191+
uint64_t next_service_task_run_time;
192+
193+
/*
194+
* True if the secure tunnel's service task is running. Used to skip service task reevaluation due to state changes
195+
* while running the service task. Reevaluation will occur at the very end of the service.
196+
*/
197+
bool in_service;
198+
199+
/*
200+
* Event loop all the secure tunnel's tasks will be pinned to, ensuring serialization and
201+
* concurrency safety.
202+
*/
203+
struct aws_event_loop *loop;
204+
205+
/*
206+
* What state is the secure tunnel working towards?
207+
*/
208+
enum aws_secure_tunnel_state desired_state;
209+
210+
/*
211+
* What is the secure tunnel's current state?
212+
*/
213+
enum aws_secure_tunnel_state current_state;
50214

51-
/* Used only during initial websocket setup. Otherwise, should be NULL */
215+
/*
216+
* handshake_request exists between the transform completion timepoint and the websocket setup callback.
217+
*/
52218
struct aws_http_message *handshake_request;
53219

54220
/* Dynamic data */
55-
int32_t stream_id;
221+
56222
struct aws_websocket *websocket;
57223

58224
/* Stores what has been received but not processed */
59225
struct aws_byte_buf received_data;
60226

61-
/* The secure tunneling endpoint ELB drops idle connect after 1 minute. We need to send a ping periodically to keep
62-
* the connection */
227+
/*
228+
* When should the secure tunnel next attempt to reconnect? Only used by PENDING_RECONNECT state.
229+
*/
230+
uint64_t next_reconnect_time_ns;
231+
232+
/*
233+
* How many consecutive reconnect failures have we experienced?
234+
*/
235+
uint64_t reconnect_count;
63236

64-
struct ping_task_context *ping_task_context;
237+
struct aws_linked_list queued_operations;
238+
struct aws_secure_tunnel_operation *current_operation;
239+
240+
/*
241+
* Is there a WebSocket message in transit (to the socket) that has not invoked its write completion callback yet?
242+
* The secure tunnel implementation only allows one in-transit message at a time, and so if this is true, we don't
243+
* send additional ones/
244+
*/
245+
bool pending_write_completion;
246+
247+
/*
248+
* When should the next PINGREQ be sent?
249+
* The secure tunneling endpoint ELB drops idle connect after 1 minute. we need to send a ping periodically to keep
250+
* the connection alive.
251+
*/
252+
uint64_t next_ping_time;
65253
};
66254

255+
AWS_EXTERN_C_BEGIN
256+
257+
/*
258+
* Override the vtable used by the secure tunnel; useful for mocking certain scenarios.
259+
*/
260+
AWS_IOTDEVICE_API void aws_secure_tunnel_set_vtable(
261+
struct aws_secure_tunnel *secure_tunnel,
262+
const struct aws_secure_tunnel_vtable *vtable);
263+
264+
/*
265+
* Gets the default vtable used by the secure tunnel. In order to mock something, we start with the default and then
266+
* mutate it selectively to achieve the scenario we're interested in.
267+
*/
268+
AWS_IOTDEVICE_API const struct aws_secure_tunnel_vtable *aws_secure_tunnel_get_default_vtable(void);
269+
270+
AWS_EXTERN_C_END
271+
67272
#endif /* AWS_IOTDEVICE_SECURE_TUNNELING_IMPL_H */

0 commit comments

Comments
 (0)