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