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