1*1c60b9acSAndroid Build Coastguard Worker /*
2*1c60b9acSAndroid Build Coastguard Worker  * lws-minimal-http-client-attach
3*1c60b9acSAndroid Build Coastguard Worker  *
4*1c60b9acSAndroid Build Coastguard Worker  * Written in 2010-2019 by Andy Green <[email protected]>
5*1c60b9acSAndroid Build Coastguard Worker  *
6*1c60b9acSAndroid Build Coastguard Worker  * This file is made available under the Creative Commons CC0 1.0
7*1c60b9acSAndroid Build Coastguard Worker  * Universal Public Domain Dedication.
8*1c60b9acSAndroid Build Coastguard Worker  *
9*1c60b9acSAndroid Build Coastguard Worker  * This demonstrates how to use the lws_system (*attach) api to allow a
10*1c60b9acSAndroid Build Coastguard Worker  * different thread to arrange to join an existing lws event loop safely.  The
11*1c60b9acSAndroid Build Coastguard Worker  * attached stuff does an http client GET from the lws event loop, even though
12*1c60b9acSAndroid Build Coastguard Worker  * it was originally requested from a different thread than the lws event loop.
13*1c60b9acSAndroid Build Coastguard Worker  */
14*1c60b9acSAndroid Build Coastguard Worker 
15*1c60b9acSAndroid Build Coastguard Worker #include <libwebsockets.h>
16*1c60b9acSAndroid Build Coastguard Worker #include <string.h>
17*1c60b9acSAndroid Build Coastguard Worker #include <signal.h>
18*1c60b9acSAndroid Build Coastguard Worker #if defined(WIN32)
19*1c60b9acSAndroid Build Coastguard Worker #define HAVE_STRUCT_TIMESPEC
20*1c60b9acSAndroid Build Coastguard Worker #if defined(pid_t)
21*1c60b9acSAndroid Build Coastguard Worker #undef pid_t
22*1c60b9acSAndroid Build Coastguard Worker #endif
23*1c60b9acSAndroid Build Coastguard Worker #endif
24*1c60b9acSAndroid Build Coastguard Worker #include <pthread.h>
25*1c60b9acSAndroid Build Coastguard Worker 
26*1c60b9acSAndroid Build Coastguard Worker static struct lws_context *context;
27*1c60b9acSAndroid Build Coastguard Worker static pthread_t lws_thread;
28*1c60b9acSAndroid Build Coastguard Worker static pthread_mutex_t lock;
29*1c60b9acSAndroid Build Coastguard Worker static int interrupted, bad = 1, status;
30*1c60b9acSAndroid Build Coastguard Worker 
31*1c60b9acSAndroid Build Coastguard Worker static int
callback_http(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)32*1c60b9acSAndroid Build Coastguard Worker callback_http(struct lws *wsi, enum lws_callback_reasons reason,
33*1c60b9acSAndroid Build Coastguard Worker 	      void *user, void *in, size_t len)
34*1c60b9acSAndroid Build Coastguard Worker {
35*1c60b9acSAndroid Build Coastguard Worker 	switch (reason) {
36*1c60b9acSAndroid Build Coastguard Worker 
37*1c60b9acSAndroid Build Coastguard Worker 	/* because we are protocols[0] ... */
38*1c60b9acSAndroid Build Coastguard Worker 	case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
39*1c60b9acSAndroid Build Coastguard Worker 		lwsl_err("CLIENT_CONNECTION_ERROR: %s\n",
40*1c60b9acSAndroid Build Coastguard Worker 			 in ? (char *)in : "(null)");
41*1c60b9acSAndroid Build Coastguard Worker 		interrupted = 1;
42*1c60b9acSAndroid Build Coastguard Worker 		break;
43*1c60b9acSAndroid Build Coastguard Worker 
44*1c60b9acSAndroid Build Coastguard Worker 	case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
45*1c60b9acSAndroid Build Coastguard Worker 		{
46*1c60b9acSAndroid Build Coastguard Worker 			char buf[128];
47*1c60b9acSAndroid Build Coastguard Worker 
48*1c60b9acSAndroid Build Coastguard Worker 			lws_get_peer_simple(wsi, buf, sizeof(buf));
49*1c60b9acSAndroid Build Coastguard Worker 			status = (int)lws_http_client_http_response(wsi);
50*1c60b9acSAndroid Build Coastguard Worker 
51*1c60b9acSAndroid Build Coastguard Worker 			lwsl_user("Connected to %s, http response: %d\n",
52*1c60b9acSAndroid Build Coastguard Worker 					buf, status);
53*1c60b9acSAndroid Build Coastguard Worker 		}
54*1c60b9acSAndroid Build Coastguard Worker 		break;
55*1c60b9acSAndroid Build Coastguard Worker 
56*1c60b9acSAndroid Build Coastguard Worker 	/* chunks of chunked content, with header removed */
57*1c60b9acSAndroid Build Coastguard Worker 	case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
58*1c60b9acSAndroid Build Coastguard Worker 		lwsl_user("RECEIVE_CLIENT_HTTP_READ: read %d\n", (int)len);
59*1c60b9acSAndroid Build Coastguard Worker 
60*1c60b9acSAndroid Build Coastguard Worker #if 0  /* enable to dump the html */
61*1c60b9acSAndroid Build Coastguard Worker 		{
62*1c60b9acSAndroid Build Coastguard Worker 			const char *p = in;
63*1c60b9acSAndroid Build Coastguard Worker 
64*1c60b9acSAndroid Build Coastguard Worker 			while (len--)
65*1c60b9acSAndroid Build Coastguard Worker 				if (*p < 0x7f)
66*1c60b9acSAndroid Build Coastguard Worker 					putchar(*p++);
67*1c60b9acSAndroid Build Coastguard Worker 				else
68*1c60b9acSAndroid Build Coastguard Worker 					putchar('.');
69*1c60b9acSAndroid Build Coastguard Worker 		}
70*1c60b9acSAndroid Build Coastguard Worker #endif
71*1c60b9acSAndroid Build Coastguard Worker 		return 0; /* don't passthru */
72*1c60b9acSAndroid Build Coastguard Worker 
73*1c60b9acSAndroid Build Coastguard Worker 	/* uninterpreted http content */
74*1c60b9acSAndroid Build Coastguard Worker 	case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
75*1c60b9acSAndroid Build Coastguard Worker 		{
76*1c60b9acSAndroid Build Coastguard Worker 			char buffer[1024 + LWS_PRE];
77*1c60b9acSAndroid Build Coastguard Worker 			char *px = buffer + LWS_PRE;
78*1c60b9acSAndroid Build Coastguard Worker 			int lenx = sizeof(buffer) - LWS_PRE;
79*1c60b9acSAndroid Build Coastguard Worker 
80*1c60b9acSAndroid Build Coastguard Worker 			if (lws_http_client_read(wsi, &px, &lenx) < 0)
81*1c60b9acSAndroid Build Coastguard Worker 				return -1;
82*1c60b9acSAndroid Build Coastguard Worker 		}
83*1c60b9acSAndroid Build Coastguard Worker 		return 0; /* don't passthru */
84*1c60b9acSAndroid Build Coastguard Worker 
85*1c60b9acSAndroid Build Coastguard Worker 	case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
86*1c60b9acSAndroid Build Coastguard Worker 		lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n");
87*1c60b9acSAndroid Build Coastguard Worker 		interrupted = 1;
88*1c60b9acSAndroid Build Coastguard Worker 		bad = status != 200;
89*1c60b9acSAndroid Build Coastguard Worker 		lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */
90*1c60b9acSAndroid Build Coastguard Worker 		break;
91*1c60b9acSAndroid Build Coastguard Worker 
92*1c60b9acSAndroid Build Coastguard Worker 	case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
93*1c60b9acSAndroid Build Coastguard Worker 		interrupted = 1;
94*1c60b9acSAndroid Build Coastguard Worker 		bad = status != 200;
95*1c60b9acSAndroid Build Coastguard Worker 		lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */
96*1c60b9acSAndroid Build Coastguard Worker 		break;
97*1c60b9acSAndroid Build Coastguard Worker 
98*1c60b9acSAndroid Build Coastguard Worker 	default:
99*1c60b9acSAndroid Build Coastguard Worker 		break;
100*1c60b9acSAndroid Build Coastguard Worker 	}
101*1c60b9acSAndroid Build Coastguard Worker 
102*1c60b9acSAndroid Build Coastguard Worker 	return lws_callback_http_dummy(wsi, reason, user, in, len);
103*1c60b9acSAndroid Build Coastguard Worker }
104*1c60b9acSAndroid Build Coastguard Worker 
105*1c60b9acSAndroid Build Coastguard Worker static const struct lws_protocols protocols[] = {
106*1c60b9acSAndroid Build Coastguard Worker 	{
107*1c60b9acSAndroid Build Coastguard Worker 		"http",
108*1c60b9acSAndroid Build Coastguard Worker 		callback_http,
109*1c60b9acSAndroid Build Coastguard Worker 		0, 0, 0, NULL, 0
110*1c60b9acSAndroid Build Coastguard Worker 	},
111*1c60b9acSAndroid Build Coastguard Worker 	LWS_PROTOCOL_LIST_TERM
112*1c60b9acSAndroid Build Coastguard Worker };
113*1c60b9acSAndroid Build Coastguard Worker 
sigint_handler(int sig)114*1c60b9acSAndroid Build Coastguard Worker void sigint_handler(int sig)
115*1c60b9acSAndroid Build Coastguard Worker {
116*1c60b9acSAndroid Build Coastguard Worker 	interrupted = 1;
117*1c60b9acSAndroid Build Coastguard Worker }
118*1c60b9acSAndroid Build Coastguard Worker 
119*1c60b9acSAndroid Build Coastguard Worker static void
attach_callback(struct lws_context * context,int tsi,void * opaque)120*1c60b9acSAndroid Build Coastguard Worker attach_callback(struct lws_context *context, int tsi, void *opaque)
121*1c60b9acSAndroid Build Coastguard Worker {
122*1c60b9acSAndroid Build Coastguard Worker 	struct lws_client_connect_info i;
123*1c60b9acSAndroid Build Coastguard Worker 
124*1c60b9acSAndroid Build Coastguard Worker 	/*
125*1c60b9acSAndroid Build Coastguard Worker 	 * Even though it was asked for from a different thread, we are called
126*1c60b9acSAndroid Build Coastguard Worker 	 * back by lws from the lws event loop thread context
127*1c60b9acSAndroid Build Coastguard Worker 	 *
128*1c60b9acSAndroid Build Coastguard Worker 	 * We can set up our operations on the lws event loop and return so
129*1c60b9acSAndroid Build Coastguard Worker 	 * they can happen asynchronously
130*1c60b9acSAndroid Build Coastguard Worker 	 */
131*1c60b9acSAndroid Build Coastguard Worker 
132*1c60b9acSAndroid Build Coastguard Worker 	memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */
133*1c60b9acSAndroid Build Coastguard Worker 	i.context = context;
134*1c60b9acSAndroid Build Coastguard Worker 	i.ssl_connection = LCCSCF_USE_SSL;
135*1c60b9acSAndroid Build Coastguard Worker 	i.ssl_connection |= LCCSCF_H2_QUIRK_OVERFLOWS_TXCR |
136*1c60b9acSAndroid Build Coastguard Worker 			    LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM;
137*1c60b9acSAndroid Build Coastguard Worker 	i.port = 443;
138*1c60b9acSAndroid Build Coastguard Worker 	i.address = "warmcat.com";
139*1c60b9acSAndroid Build Coastguard Worker 	i.path = "/";
140*1c60b9acSAndroid Build Coastguard Worker 	i.host = i.address;
141*1c60b9acSAndroid Build Coastguard Worker 	i.origin = i.address;
142*1c60b9acSAndroid Build Coastguard Worker 	i.method = "GET";
143*1c60b9acSAndroid Build Coastguard Worker 
144*1c60b9acSAndroid Build Coastguard Worker 	i.protocol = protocols[0].name;
145*1c60b9acSAndroid Build Coastguard Worker 
146*1c60b9acSAndroid Build Coastguard Worker 	lws_client_connect_via_info(&i);
147*1c60b9acSAndroid Build Coastguard Worker }
148*1c60b9acSAndroid Build Coastguard Worker 
149*1c60b9acSAndroid Build Coastguard Worker 
150*1c60b9acSAndroid Build Coastguard Worker static int
lws_attach_with_pthreads_locking(struct lws_context * context,int tsi,lws_attach_cb_t cb,lws_system_states_t state,void * opaque,struct lws_attach_item ** get)151*1c60b9acSAndroid Build Coastguard Worker lws_attach_with_pthreads_locking(struct lws_context *context, int tsi,
152*1c60b9acSAndroid Build Coastguard Worker 				 lws_attach_cb_t cb, lws_system_states_t state,
153*1c60b9acSAndroid Build Coastguard Worker 				 void *opaque, struct lws_attach_item **get)
154*1c60b9acSAndroid Build Coastguard Worker {
155*1c60b9acSAndroid Build Coastguard Worker 	int n;
156*1c60b9acSAndroid Build Coastguard Worker 
157*1c60b9acSAndroid Build Coastguard Worker 	pthread_mutex_lock(&lock);
158*1c60b9acSAndroid Build Coastguard Worker 	/*
159*1c60b9acSAndroid Build Coastguard Worker 	 * We just provide system-specific locking around the lws non-threadsafe
160*1c60b9acSAndroid Build Coastguard Worker 	 * helper that adds and removes things from the pt list
161*1c60b9acSAndroid Build Coastguard Worker 	 */
162*1c60b9acSAndroid Build Coastguard Worker 	n = __lws_system_attach(context, tsi, cb, state, opaque, get);
163*1c60b9acSAndroid Build Coastguard Worker 	pthread_mutex_unlock(&lock);
164*1c60b9acSAndroid Build Coastguard Worker 
165*1c60b9acSAndroid Build Coastguard Worker 	return n;
166*1c60b9acSAndroid Build Coastguard Worker }
167*1c60b9acSAndroid Build Coastguard Worker 
168*1c60b9acSAndroid Build Coastguard Worker 
169*1c60b9acSAndroid Build Coastguard Worker lws_system_ops_t ops = {
170*1c60b9acSAndroid Build Coastguard Worker 	.attach = lws_attach_with_pthreads_locking
171*1c60b9acSAndroid Build Coastguard Worker };
172*1c60b9acSAndroid Build Coastguard Worker 
173*1c60b9acSAndroid Build Coastguard Worker /*
174*1c60b9acSAndroid Build Coastguard Worker  * We made this into a different thread to model it being run from completely
175*1c60b9acSAndroid Build Coastguard Worker  * different codebase that's all linked together
176*1c60b9acSAndroid Build Coastguard Worker  */
177*1c60b9acSAndroid Build Coastguard Worker 
178*1c60b9acSAndroid Build Coastguard Worker static void *
lws_create(void * d)179*1c60b9acSAndroid Build Coastguard Worker lws_create(void *d)
180*1c60b9acSAndroid Build Coastguard Worker {
181*1c60b9acSAndroid Build Coastguard Worker 	struct lws_context_creation_info info;
182*1c60b9acSAndroid Build Coastguard Worker 
183*1c60b9acSAndroid Build Coastguard Worker        lwsl_user("%s: tid %p\n", __func__, (void *)(intptr_t)pthread_self());
184*1c60b9acSAndroid Build Coastguard Worker 
185*1c60b9acSAndroid Build Coastguard Worker 	memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
186*1c60b9acSAndroid Build Coastguard Worker 	info.port = CONTEXT_PORT_NO_LISTEN;
187*1c60b9acSAndroid Build Coastguard Worker 	info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
188*1c60b9acSAndroid Build Coastguard Worker 	info.system_ops = &ops;
189*1c60b9acSAndroid Build Coastguard Worker 	info.protocols = protocols;
190*1c60b9acSAndroid Build Coastguard Worker 
191*1c60b9acSAndroid Build Coastguard Worker 	context = lws_create_context(&info);
192*1c60b9acSAndroid Build Coastguard Worker 	if (!context) {
193*1c60b9acSAndroid Build Coastguard Worker 		lwsl_err("lws init failed\n");
194*1c60b9acSAndroid Build Coastguard Worker 		goto bail;
195*1c60b9acSAndroid Build Coastguard Worker 	}
196*1c60b9acSAndroid Build Coastguard Worker 
197*1c60b9acSAndroid Build Coastguard Worker 	/* start the event loop */
198*1c60b9acSAndroid Build Coastguard Worker 
199*1c60b9acSAndroid Build Coastguard Worker 	while (!interrupted)
200*1c60b9acSAndroid Build Coastguard Worker 		if (lws_service(context, 0))
201*1c60b9acSAndroid Build Coastguard Worker 			interrupted = 1;
202*1c60b9acSAndroid Build Coastguard Worker 
203*1c60b9acSAndroid Build Coastguard Worker 	lws_context_destroy(context);
204*1c60b9acSAndroid Build Coastguard Worker 
205*1c60b9acSAndroid Build Coastguard Worker bail:
206*1c60b9acSAndroid Build Coastguard Worker 	pthread_exit(NULL);
207*1c60b9acSAndroid Build Coastguard Worker 
208*1c60b9acSAndroid Build Coastguard Worker 	return NULL;
209*1c60b9acSAndroid Build Coastguard Worker }
210*1c60b9acSAndroid Build Coastguard Worker 
main(int argc,const char ** argv)211*1c60b9acSAndroid Build Coastguard Worker int main(int argc, const char **argv)
212*1c60b9acSAndroid Build Coastguard Worker {
213*1c60b9acSAndroid Build Coastguard Worker 	int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
214*1c60b9acSAndroid Build Coastguard Worker 	const char *p;
215*1c60b9acSAndroid Build Coastguard Worker 	void *retval;
216*1c60b9acSAndroid Build Coastguard Worker 
217*1c60b9acSAndroid Build Coastguard Worker 	signal(SIGINT, sigint_handler);
218*1c60b9acSAndroid Build Coastguard Worker 
219*1c60b9acSAndroid Build Coastguard Worker 	if ((p = lws_cmdline_option(argc, argv, "-d")))
220*1c60b9acSAndroid Build Coastguard Worker 		logs = atoi(p);
221*1c60b9acSAndroid Build Coastguard Worker 
222*1c60b9acSAndroid Build Coastguard Worker 	lws_set_log_level(logs, NULL);
223*1c60b9acSAndroid Build Coastguard Worker 	lwsl_user("LWS minimal http client attach\n");
224*1c60b9acSAndroid Build Coastguard Worker 
225*1c60b9acSAndroid Build Coastguard Worker 	pthread_mutex_init(&lock, NULL);
226*1c60b9acSAndroid Build Coastguard Worker 
227*1c60b9acSAndroid Build Coastguard Worker 	/*
228*1c60b9acSAndroid Build Coastguard Worker 	 * The idea of the example is we're going to split the lws context and
229*1c60b9acSAndroid Build Coastguard Worker 	 * event loop off to be created from its own thread... this is like it
230*1c60b9acSAndroid Build Coastguard Worker 	 * was actually started by some completely different code...
231*1c60b9acSAndroid Build Coastguard Worker 	 */
232*1c60b9acSAndroid Build Coastguard Worker 
233*1c60b9acSAndroid Build Coastguard Worker 	if (pthread_create(&lws_thread, NULL, lws_create, NULL)) {
234*1c60b9acSAndroid Build Coastguard Worker 		lwsl_err("thread creation failed\n");
235*1c60b9acSAndroid Build Coastguard Worker 		goto bail1;
236*1c60b9acSAndroid Build Coastguard Worker 	}
237*1c60b9acSAndroid Build Coastguard Worker 
238*1c60b9acSAndroid Build Coastguard Worker 	/*
239*1c60b9acSAndroid Build Coastguard Worker 	 * Now on the original / different thread representing a different
240*1c60b9acSAndroid Build Coastguard Worker 	 * codebase that wants to join this existing event loop, we'll ask to
241*1c60b9acSAndroid Build Coastguard Worker 	 * get a callback from the event loop context when the event loop
242*1c60b9acSAndroid Build Coastguard Worker 	 * thread is operational.  We have to wait around a bit because we
243*1c60b9acSAndroid Build Coastguard Worker 	 * may run before the lws context was created.
244*1c60b9acSAndroid Build Coastguard Worker 	 */
245*1c60b9acSAndroid Build Coastguard Worker 
246*1c60b9acSAndroid Build Coastguard Worker 	while (!context && n++ < 30)
247*1c60b9acSAndroid Build Coastguard Worker 		usleep(10000);
248*1c60b9acSAndroid Build Coastguard Worker 
249*1c60b9acSAndroid Build Coastguard Worker 	if (!context) {
250*1c60b9acSAndroid Build Coastguard Worker 		lwsl_err("%s: context didn't start\n", __func__);
251*1c60b9acSAndroid Build Coastguard Worker 		goto bail;
252*1c60b9acSAndroid Build Coastguard Worker 	}
253*1c60b9acSAndroid Build Coastguard Worker 
254*1c60b9acSAndroid Build Coastguard Worker 	/*
255*1c60b9acSAndroid Build Coastguard Worker 	 * From our different, non event loop thread, ask for our attach
256*1c60b9acSAndroid Build Coastguard Worker 	 * callback to get called when lws system state is OPERATIONAL
257*1c60b9acSAndroid Build Coastguard Worker 	 */
258*1c60b9acSAndroid Build Coastguard Worker 
259*1c60b9acSAndroid Build Coastguard Worker 	lws_system_get_ops(context)->attach(context, 0, attach_callback,
260*1c60b9acSAndroid Build Coastguard Worker 					    LWS_SYSTATE_OPERATIONAL,
261*1c60b9acSAndroid Build Coastguard Worker 					    NULL, NULL);
262*1c60b9acSAndroid Build Coastguard Worker 
263*1c60b9acSAndroid Build Coastguard Worker 	/*
264*1c60b9acSAndroid Build Coastguard Worker 	 * That's all we wanted to do with our thread.  Just wait for the lws
265*1c60b9acSAndroid Build Coastguard Worker 	 * thread to exit as well.
266*1c60b9acSAndroid Build Coastguard Worker 	 */
267*1c60b9acSAndroid Build Coastguard Worker 
268*1c60b9acSAndroid Build Coastguard Worker bail:
269*1c60b9acSAndroid Build Coastguard Worker 	pthread_join(lws_thread, &retval);
270*1c60b9acSAndroid Build Coastguard Worker bail1:
271*1c60b9acSAndroid Build Coastguard Worker 	pthread_mutex_destroy(&lock);
272*1c60b9acSAndroid Build Coastguard Worker 
273*1c60b9acSAndroid Build Coastguard Worker 	lwsl_user("%s: finished\n", __func__);
274*1c60b9acSAndroid Build Coastguard Worker 
275*1c60b9acSAndroid Build Coastguard Worker 	return 0;
276*1c60b9acSAndroid Build Coastguard Worker }
277