• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * lws-minimal-secure-streams-threads
3  *
4  * Written in 2010-2021 by Andy Green <andy@warmcat.com>
5  *
6  * This file is made available under the Creative Commons CC0 1.0
7  * Universal Public Domain Dedication.
8  *
9  *
10  * This demonstrates how other threads can wake the lws event loop and ask it
11  * to do things via lws_cancel_service(), notifying Secure Streams using the
12  * LWSSSCS_EVENT_WAIT_CANCELLED state callback.
13  *
14  * Because of what we're testing, we don't actually connect the SS just create
15  * it and wait for the states we are testing for to come at 10Hz.
16  *
17  * We run the test for 3s and check we got an appropriate amount of wakes
18  * to call it a success.
19  *
20  * You can use the same pattern to have any amount of shared data protected by
21  * the mutex, containing whatever the other threads want the lws event loop
22  * thread to do for them.
23  */
24 
25 #include <libwebsockets.h>
26 #include <string.h>
27 #include <signal.h>
28 
29 #include <pthread.h>
30 
31 /*
32  * Define this to cause an ss api access from a foreign thread, it will
33  * assert.  This is for testing lws, don't do this in your code.
34  */
35 // #define DO_ILLEGAL_API_THREAD
36 
37 static int interrupted, bad = 1, finished;
38 static lws_sorted_usec_list_t sul_timeout;
39 static struct lws_context *context;
40 static pthread_t pthread_spam;
41 static int wakes, started_thread;
42 
43 #if defined(DO_ILLEGAL_API_THREAD)
44 static struct lws_ss_handle *ss; /* only needed for DO_ILLEGAL_API_THREAD */
45 #endif
46 
47 /* the data shared between the spam thread and the lws event loop */
48 
49 static pthread_mutex_t lock_shared;
50 static int shared_counter;
51 
52 
53 #if !defined(LWS_SS_USE_SSPC)
54 static const char * const default_ss_policy =
55 	"{"
56 		"\"schema-version\":1,"
57 		"\"s\": ["
58 			"{"
59 				"\"mintest\": {"
60 					"\"endpoint\": \"connectivitycheck.android.com\","
61 					"\"http_url\": \"generate_204\","
62 					"\"port\": 80,"
63 					"\"protocol\": \"h1\","
64 					"\"http_method\": \"GET\","
65 					"\"opportunistic\": true,"
66 					"\"http_expect\": 204,"
67 					"\"http_fail_redirect\": true"
68 				"}"
69 			"}"
70 		"]"
71 	"}"
72 ;
73 
74 #endif
75 
76 typedef struct myss {
77 	struct lws_ss_handle 		*ss;
78 	void				*opaque_data;
79 	/* ... application specific state ... */
80 } myss_t;
81 
82 static void *
thread_spam(void * d)83 thread_spam(void *d)
84 {
85 
86 	do {
87 		pthread_mutex_lock(&lock_shared); /* --------- shared lock { */
88 
89 		/*
90 		 * prepare the shared data area to indicate whatever it is that
91 		 * we want doing on the main event loop.  In this case, we just
92 		 * bump a counter, but it can be any amount of data prepared,
93 		 * eg, whole info struct for a connection we want.
94 		 */
95 
96 		shared_counter++;
97 
98 		lwsl_notice("%s: cancelling wait from spam thread: %d\n",
99 				__func__, shared_counter);
100 		lws_cancel_service(context);
101 
102 #if defined(DO_ILLEGAL_API_THREAD)
103 		/*
104 		 * ILLEGAL...
105 		 * We cannot call any other lws api from a foreign thread
106 		 */
107 
108 		if (ss)
109 			lws_ss_request_tx(ss);
110 #endif
111 
112 		pthread_mutex_unlock(&lock_shared); /* } shared lock ------- */
113 
114 		usleep(100000); /* wait 100ms and signal main thread again */
115 
116 	} while (!finished);
117 
118 	pthread_exit(NULL);
119 
120 	return NULL;
121 }
122 
123 
124 static lws_ss_state_return_t
myss_state(void * userobj,void * h_src,lws_ss_constate_t state,lws_ss_tx_ordinal_t ack)125 myss_state(void *userobj, void *h_src, lws_ss_constate_t state,
126 	   lws_ss_tx_ordinal_t ack)
127 {
128 	// myss_t *m = (myss_t *)userobj;
129 	void *retval;
130 
131 	switch (state) {
132 	case LWSSSCS_CREATING:
133 		if (pthread_create(&pthread_spam, NULL, thread_spam, NULL)) {
134 			lwsl_err("thread creation failed\n");
135 			return LWSSSSRET_DESTROY_ME;
136 		}
137 		started_thread = 1;
138 		break;
139 	case LWSSSCS_DESTROYING:
140 		finished = 1;
141 		if (started_thread)
142 			pthread_join(pthread_spam, &retval);
143 		break;
144 
145 	case LWSSSCS_EVENT_WAIT_CANCELLED:
146 		pthread_mutex_lock(&lock_shared); /* --------- shared lock { */
147 		lwsl_notice("%s: LWSSSCS_EVENT_WAIT_CANCELLED: %d, shared: %d\n",
148 			    __func__, ++wakes, shared_counter);
149 		pthread_mutex_unlock(&lock_shared); /* } shared lock ------- */
150 		break;
151 
152 	default:
153 		break;
154 	}
155 
156 	return LWSSSSRET_OK;
157 }
158 
159 static const lws_ss_info_t ssi_lws_threads = {
160 	.handle_offset		  = offsetof(myss_t, ss),
161 	.opaque_user_data_offset  = offsetof(myss_t, opaque_data),
162 	/* we don't actually do any rx or tx in this test */
163 	.state			  = myss_state,
164 	.user_alloc		  = sizeof(myss_t),
165 	.streamtype		  = "mintest",
166 	.manual_initial_tx_credit = 0,
167 };
168 
169 static void
sul_timeout_cb(lws_sorted_usec_list_t * sul)170 sul_timeout_cb(lws_sorted_usec_list_t *sul)
171 {
172 	lwsl_notice("%s: test finishing\n", __func__);
173 	interrupted = 1;
174 }
175 
176 
177 static void
sigint_handler(int sig)178 sigint_handler(int sig)
179 {
180 	interrupted = 1;
181 }
182 
183 static int
system_notify_cb(lws_state_manager_t * mgr,lws_state_notify_link_t * link,int current,int target)184 system_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *link,
185 		   int current, int target)
186 {
187 	if (current != LWS_SYSTATE_OPERATIONAL || target != LWS_SYSTATE_OPERATIONAL)
188 		return 0;
189 
190 	/* the test SS.. not going to connect it, just see if the cancel_service
191 	 * messages are coming
192 	 */
193 
194 	if (lws_ss_create(context, 0, &ssi_lws_threads, NULL,
195 #if defined(DO_ILLEGAL_API_THREAD)
196 			&ss,
197 #else
198 			NULL,
199 #endif
200 			NULL, NULL)) {
201 		lwsl_err("%s: failed to create secure stream\n",
202 			 __func__);
203 
204 		return -1;
205 	}
206 
207 	/* set up the test timeout */
208 
209 	lws_sul_schedule(context, 0, &sul_timeout, sul_timeout_cb,
210 			 3 * LWS_US_PER_SEC);
211 
212 	return 0;
213 }
214 
main(int argc,const char ** argv)215 int main(int argc, const char **argv)
216 {
217 	lws_state_notify_link_t notifier = { { NULL, NULL, NULL},
218 					     system_notify_cb, "app" };
219 	lws_state_notify_link_t *na[] = { &notifier, NULL };
220 	struct lws_context_creation_info info;
221 
222 	signal(SIGINT, sigint_handler);
223 
224 	memset(&info, 0, sizeof info);
225 
226 	lws_cmdline_option_handle_builtin(argc, argv, &info);
227 
228 	lwsl_user("LWS Secure Streams threads test client [-d<verb>]\n");
229 
230 	info.fd_limit_per_thread	= 1 + 6 + 1;
231 	info.port			= CONTEXT_PORT_NO_LISTEN;
232 #if !defined(LWS_SS_USE_SSPC)
233 	info.pss_policies_json		= default_ss_policy;
234 #else
235 	info.protocols			= lws_sspc_protocols;
236 	{
237 		const char *p;
238 
239 		/* connect to ssproxy via UDS by default, else via
240 		 * tcp connection to this port */
241 		if ((p = lws_cmdline_option(argc, argv, "-p")))
242 			info.ss_proxy_port = (uint16_t)atoi(p);
243 
244 		/* UDS "proxy.ss.lws" in abstract namespace, else this socket
245 		 * path; when -p given this can specify the network interface
246 		 * to bind to */
247 		if ((p = lws_cmdline_option(argc, argv, "-i")))
248 			info.ss_proxy_bind = p;
249 
250 		/* if -p given, -a specifies the proxy address to connect to */
251 		if ((p = lws_cmdline_option(argc, argv, "-a")))
252 			info.ss_proxy_address = p;
253 	}
254 #endif
255 	info.options			= LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
256 					  LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
257 	info.register_notifier_list = na;
258 
259 	/* create the context */
260 
261 	context = lws_create_context(&info);
262 	if (!context) {
263 		lwsl_err("lws init failed\n");
264 		return 1;
265 	}
266 
267 #if defined(LWS_SS_USE_SSPC)
268 	if (!lws_create_vhost(context, &info)) {
269 		lwsl_err("%s: failed to create default vhost\n", __func__);
270 		goto bail;
271 	}
272 #endif
273 
274 	/* the event loop */
275 
276 	while (lws_service(context, 0) >= 0 && !interrupted)
277 		;
278 
279 	/* compare what happened with what we expect */
280 
281 	if (wakes > 10)
282 		/* OSX can do the usleep thread slower than 100ms */
283 		bad = 0;
284 
285 	lwsl_notice("wakes %d\n", wakes);
286 
287 #if defined(LWS_SS_USE_SSPC)
288 bail:
289 #endif
290 	lws_sul_cancel(&sul_timeout);
291 	lws_context_destroy(context);
292 
293 	lwsl_user("Completed: %s\n", bad ? "failed" : "OK");
294 
295 	return bad;
296 }
297