• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * lws-minimal-http-server-eventlib-foreign
3  *
4  * Written in 2010-2020 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  * This demonstrates the most minimal http server you can make with lws that
10  * uses a libuv event loop created outside lws.  It shows how lws can
11  * participate in someone else's event loop and clean up after itself.
12  *
13  * You choose the event loop to work with at runtime, by giving the
14  * --uv, --event or --ev switch.  Lws has to have been configured to build the
15  * selected event lib support.
16  *
17  * To keep it simple, it serves stuff from the subdirectory
18  * "./mount-origin" of the directory it was started in.
19  * You can change that by changing mount.origin below.
20  */
21 
22 #include <libwebsockets.h>
23 #include <string.h>
24 #include <signal.h>
25 
26 struct lws_context_creation_info info;
27 static struct lws_context *context;
28 static int lifetime = 5, reported;
29 
30 static void foreign_timer_service(void *foreign_loop);
31 
32 enum {
33 	TEST_STATE_CREATE_LWS_CONTEXT,
34 	TEST_STATE_DESTROY_LWS_CONTEXT,
35 	TEST_STATE_EXIT
36 };
37 
38 static int sequence = TEST_STATE_CREATE_LWS_CONTEXT;
39 
40 static const struct lws_http_mount mount = {
41 	/* .mount_next */		NULL,		/* linked-list "next" */
42 	/* .mountpoint */		"/",		/* mountpoint URL */
43 	/* .origin */			"./mount-origin", /* serve from dir */
44 	/* .def */			"index.html",	/* default filename */
45 	/* .protocol */			NULL,
46 	/* .cgienv */			NULL,
47 	/* .extra_mimetypes */		NULL,
48 	/* .interpret */		NULL,
49 	/* .cgi_timeout */		0,
50 	/* .cache_max_age */		0,
51 	/* .auth_mask */		0,
52 	/* .cache_reusable */		0,
53 	/* .cache_revalidate */		0,
54 	/* .cache_intermediaries */	0,
55 	/* .origin_protocol */		LWSMPRO_FILE,	/* files in a dir */
56 	/* .mountpoint_len */		1,		/* char count */
57 	/* .basic_auth_login_file */	NULL,
58 };
59 
60 static void
signal_cb(int signum)61 signal_cb(int signum)
62 {
63 	lwsl_notice("Signal %d caught, exiting...\n", signum);
64 
65 	switch (signum) {
66 	case SIGTERM:
67 	case SIGINT:
68 		break;
69 	default:
70 		break;
71 	}
72 
73 	lws_context_destroy(context);
74 }
75 
76 /*
77  * The event-loop specific foreign loop code, one set for each event loop lib
78  *
79  * Only the code in this section is specific to the event library used.
80  */
81 
82 #if defined(LWS_WITH_LIBUV)
83 
84 static uv_loop_t loop_uv;
85 static uv_timer_t timer_outer_uv;
86 static uv_signal_t sighandler_uv;
87 
88 static void
timer_cb_uv(uv_timer_t * t)89 timer_cb_uv(uv_timer_t *t)
90 {
91 	foreign_timer_service(&loop_uv);
92 }
93 
94 static void
signal_cb_uv(uv_signal_t * watcher,int signum)95 signal_cb_uv(uv_signal_t *watcher, int signum)
96 {
97 	signal_cb(signum);
98 }
99 
100 static void
foreign_event_loop_init_and_run_libuv(void)101 foreign_event_loop_init_and_run_libuv(void)
102 {
103 	/* we create and start our "foreign loop" */
104 
105 #if (UV_VERSION_MAJOR > 0) // Travis...
106 	uv_loop_init(&loop_uv);
107 #endif
108 	uv_signal_init(&loop_uv, &sighandler_uv);
109 	uv_signal_start(&sighandler_uv, signal_cb_uv, SIGINT);
110 
111 	uv_timer_init(&loop_uv, &timer_outer_uv);
112 #if (UV_VERSION_MAJOR > 0) // Travis...
113 	uv_timer_start(&timer_outer_uv, timer_cb_uv, 0, 1000);
114 #else
115 	(void)timer_cb_uv;
116 #endif
117 
118 	uv_run(&loop_uv, UV_RUN_DEFAULT);
119 }
120 
121 static void
foreign_event_loop_stop_libuv(void)122 foreign_event_loop_stop_libuv(void)
123 {
124 	uv_stop(&loop_uv);
125 }
126 
127 static void
foreign_event_loop_cleanup_libuv(void)128 foreign_event_loop_cleanup_libuv(void)
129 {
130 	/* cleanup the foreign loop assets */
131 
132 	uv_timer_stop(&timer_outer_uv);
133 	uv_close((uv_handle_t*)&timer_outer_uv, NULL);
134 	uv_signal_stop(&sighandler_uv);
135 	uv_close((uv_handle_t *)&sighandler_uv, NULL);
136 
137 	uv_run(&loop_uv, UV_RUN_DEFAULT);
138 #if (UV_VERSION_MAJOR > 0) // Travis...
139 	uv_loop_close(&loop_uv);
140 #endif
141 }
142 
143 #endif
144 
145 #if defined(LWS_WITH_LIBEVENT)
146 
147 static struct event_base *loop_event;
148 static struct event *timer_outer_event;
149 static struct event *sighandler_event;
150 
151 static void
timer_cb_event(int fd,short event,void * arg)152 timer_cb_event(int fd, short event, void *arg)
153 {
154 	foreign_timer_service(loop_event);
155 }
156 
157 static void
signal_cb_event(int fd,short event,void * arg)158 signal_cb_event(int fd, short event, void *arg)
159 {
160 	signal_cb((int)(lws_intptr_t)arg);
161 }
162 
163 static void
foreign_event_loop_init_and_run_libevent(void)164 foreign_event_loop_init_and_run_libevent(void)
165 {
166 	struct timeval tv;
167 
168 	/* we create and start our "foreign loop" */
169 
170 	tv.tv_sec = 1;
171 	tv.tv_usec = 0;
172 
173 	loop_event = event_base_new();
174 
175 	sighandler_event = evsignal_new(loop_event, SIGINT, signal_cb_event,
176 					(void*)SIGINT);
177 
178 	timer_outer_event = event_new(loop_event, -1, EV_PERSIST,
179 				      timer_cb_event, NULL);
180 	//evtimer_new(loop_event, timer_cb_event, NULL);
181 	evtimer_add(timer_outer_event, &tv);
182 
183 	event_base_loop(loop_event, 0);
184 }
185 
186 static void
foreign_event_loop_stop_libevent(void)187 foreign_event_loop_stop_libevent(void)
188 {
189 	event_base_loopexit(loop_event, NULL);
190 }
191 
192 static void
foreign_event_loop_cleanup_libevent(void)193 foreign_event_loop_cleanup_libevent(void)
194 {
195 	/* cleanup the foreign loop assets */
196 
197 	evtimer_del(timer_outer_event);
198 	event_free(timer_outer_event);
199 	evsignal_del(sighandler_event);
200 	event_free(sighandler_event);
201 
202 	event_base_loop(loop_event, 0);
203 	event_base_free(loop_event);
204 }
205 
206 #endif
207 
208 #if defined(LWS_WITH_GLIB)
209 
210 #include <glib-2.0/glib.h>
211 #include <glib-unix.h>
212 
213 static GMainLoop *loop_glib;
214 static guint timer_outer_glib;
215 static guint sighandler_glib;
216 
217 static int
timer_cb_glib(void * p)218 timer_cb_glib(void *p)
219 {
220 	foreign_timer_service(loop_glib);
221 	return 1;
222 }
223 
224 static void
signal_cb_glib(void * p)225 signal_cb_glib(void *p)
226 {
227 	signal_cb(SIGINT);
228 }
229 
230 static void
foreign_event_loop_init_and_run_glib(void)231 foreign_event_loop_init_and_run_glib(void)
232 {
233 	/* we create and start our "foreign loop" */
234 
235 	loop_glib = g_main_loop_new(NULL, 0);
236 
237 	sighandler_glib = g_unix_signal_add(SIGINT,
238 					G_SOURCE_FUNC(signal_cb_glib), NULL);
239 
240 	timer_outer_glib = g_timeout_add(1000, timer_cb_glib, NULL);
241 
242 	g_main_loop_run(loop_glib);
243 }
244 
245 static void
foreign_event_loop_stop_glib(void)246 foreign_event_loop_stop_glib(void)
247 {
248 	g_main_loop_quit(loop_glib);
249 }
250 
251 static void
foreign_event_loop_cleanup_glib(void)252 foreign_event_loop_cleanup_glib(void)
253 {
254 	/* cleanup the foreign loop assets */
255 	g_source_remove(sighandler_glib);
256 	g_main_loop_unref(loop_glib);
257 }
258 
259 #endif
260 
261 #if defined(LWS_WITH_LIBEV)
262 
263 static struct ev_loop *loop_ev;
264 static struct ev_timer timer_outer_ev;
265 static struct ev_signal sighandler_ev;
266 
267 static void
timer_cb_ev(struct ev_loop * loop,struct ev_timer * watcher,int revents)268 timer_cb_ev(struct ev_loop *loop, struct ev_timer *watcher, int revents)
269 {
270 	foreign_timer_service(loop_ev);
271 }
272 
273 static void
signal_cb_ev(struct ev_loop * loop,struct ev_signal * watcher,int revents)274 signal_cb_ev(struct ev_loop *loop, struct ev_signal *watcher, int revents)
275 {
276 	signal_cb(watcher->signum);
277 }
278 
279 static void
foreign_event_loop_init_and_run_libev(void)280 foreign_event_loop_init_and_run_libev(void)
281 {
282 	/* we create and start our "foreign loop" */
283 
284 	loop_ev = ev_loop_new(0);
285 
286 	ev_signal_init(&sighandler_ev, signal_cb_ev, SIGINT);
287 	ev_signal_start(loop_ev, &sighandler_ev);
288 
289 	ev_timer_init(&timer_outer_ev, timer_cb_ev, 0, 1);
290 	ev_timer_start(loop_ev, &timer_outer_ev);
291 
292 	ev_run(loop_ev, 0);
293 }
294 
295 static void
foreign_event_loop_stop_libev(void)296 foreign_event_loop_stop_libev(void)
297 {
298 	ev_break(loop_ev, EVBREAK_ALL);
299 }
300 
301 static void
foreign_event_loop_cleanup_libev(void)302 foreign_event_loop_cleanup_libev(void)
303 {
304 	/* cleanup the foreign loop assets */
305 
306 	ev_timer_stop(loop_ev, &timer_outer_ev);
307 	ev_signal_stop(loop_ev, &sighandler_ev);
308 
309 	ev_run(loop_ev, UV_RUN_DEFAULT);
310 	ev_loop_destroy(loop_ev);
311 }
312 
313 #endif
314 
315 /* this is called at 1Hz using a foreign loop timer */
316 
317 static void
foreign_timer_service(void * foreign_loop)318 foreign_timer_service(void *foreign_loop)
319 {
320 	void *foreign_loops[1];
321 
322 	lwsl_user("Foreign 1Hz timer\n");
323 
324 	if (sequence == TEST_STATE_EXIT && !context && !reported) {
325 		/*
326 		 * at this point the lws_context_destroy() we did earlier
327 		 * has completed and the entire context is wholly destroyed
328 		 */
329 		lwsl_user("lws_destroy_context() done, continuing for 5s\n");
330 		reported = 1;
331 	}
332 
333 	if (--lifetime)
334 		return;
335 
336 	switch (sequence++) {
337 	case TEST_STATE_CREATE_LWS_CONTEXT:
338 		/* this only has to exist for the duration of create context */
339 		foreign_loops[0] = foreign_loop;
340 		info.foreign_loops = foreign_loops;
341 
342 		context = lws_create_context(&info);
343 		if (!context) {
344 			lwsl_err("lws init failed\n");
345 			return;
346 		}
347 		lwsl_user("LWS Context created and will be active for 10s\n");
348 		lifetime = 11;
349 		break;
350 
351 	case TEST_STATE_DESTROY_LWS_CONTEXT:
352 		/* cleanup the lws part */
353 		lwsl_user("Destroying lws context and continuing loop for 5s\n");
354 		lws_context_destroy(context);
355 		lifetime = 6;
356 		break;
357 
358 	case TEST_STATE_EXIT:
359 		lwsl_user("Deciding to exit foreign loop too\n");
360 #if defined(LWS_WITH_LIBUV)
361 		if (info.options & LWS_SERVER_OPTION_LIBUV)
362 			foreign_event_loop_stop_libuv();
363 #endif
364 #if defined(LWS_WITH_LIBEVENT)
365 		if (info.options & LWS_SERVER_OPTION_LIBEVENT)
366 			foreign_event_loop_stop_libevent();
367 #endif
368 #if defined(LWS_WITH_LIBEV)
369 		if (info.options & LWS_SERVER_OPTION_LIBEV)
370 			foreign_event_loop_stop_libev();
371 #endif
372 #if defined(LWS_WITH_GLIB)
373 		if (info.options & LWS_SERVER_OPTION_GLIB)
374 			foreign_event_loop_stop_glib();
375 #endif
376 		break;
377 	default:
378 		break;
379 	}
380 }
381 
main(int argc,const char ** argv)382 int main(int argc, const char **argv)
383 {
384 	const char *p;
385 	int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
386 			/* for LLL_ verbosity above NOTICE to be built into lws,
387 			 * lws must have been configured and built with
388 			 * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */
389 			/* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
390 			/* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
391 			/* | LLL_DEBUG */;
392 
393 	if ((p = lws_cmdline_option(argc, argv, "-d")))
394 		logs = atoi(p);
395 
396 	lws_set_log_level(logs, NULL);
397 	lwsl_user("LWS minimal http server eventlib + foreign loop |"
398 		  " visit http://localhost:7681\n");
399 
400 	/*
401 	 * We prepare the info here, but don't use it until later in the
402 	 * timer callback, to demonstrate the independence of the foreign loop
403 	 * and lws.
404 	 */
405 
406 	memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
407 	info.port = 7681;
408 	info.mounts = &mount;
409 	info.error_document_404 = "/404.html";
410 	info.pcontext = &context;
411 	info.options =
412 		LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE;
413 
414 	if (lws_cmdline_option(argc, argv, "-s")) {
415 		info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
416 		info.ssl_cert_filepath = "localhost-100y.cert";
417 		info.ssl_private_key_filepath = "localhost-100y.key";
418 	}
419 
420 	if (lws_cmdline_option(argc, argv, "--uv"))
421 		info.options |= LWS_SERVER_OPTION_LIBUV;
422 	else
423 		if (lws_cmdline_option(argc, argv, "--event"))
424 			info.options |= LWS_SERVER_OPTION_LIBEVENT;
425 		else
426 			if (lws_cmdline_option(argc, argv, "--ev"))
427 				info.options |= LWS_SERVER_OPTION_LIBEV;
428 			else
429 				if (lws_cmdline_option(argc, argv, "--glib"))
430 					info.options |= LWS_SERVER_OPTION_GLIB;
431 				else {
432 				lwsl_err("This app only makes sense when used\n");
433 				lwsl_err(" with a foreign loop, --uv, --event, --glib, or --ev\n");
434 
435 				return 1;
436 			}
437 
438 	lwsl_user("  This app creates a foreign event loop with a timer +\n");
439 	lwsl_user("  signalhandler, and performs a test in three phases:\n");
440 	lwsl_user("\n");
441 	lwsl_user("  1) 5s: Runs the loop with just the timer\n");
442 	lwsl_user("  2) 10s: create an lws context serving on localhost:7681\n");
443 	lwsl_user("     using the same foreign loop.  Destroy it after 10s.\n");
444 	lwsl_user("  3) 5s: Run the loop again with just the timer\n");
445 	lwsl_user("\n");
446 	lwsl_user("  Finally close only the timer and signalhandler and\n");
447 	lwsl_user("   exit the loop cleanly\n");
448 	lwsl_user("\n");
449 
450 	/* foreign loop specific startup and run */
451 
452 #if defined(LWS_WITH_LIBUV)
453 	if (info.options & LWS_SERVER_OPTION_LIBUV)
454 		foreign_event_loop_init_and_run_libuv();
455 #endif
456 #if defined(LWS_WITH_LIBEVENT)
457 	if (info.options & LWS_SERVER_OPTION_LIBEVENT)
458 		foreign_event_loop_init_and_run_libevent();
459 #endif
460 #if defined(LWS_WITH_LIBEV)
461 	if (info.options & LWS_SERVER_OPTION_LIBEV)
462 		foreign_event_loop_init_and_run_libev();
463 #endif
464 #if defined(LWS_WITH_GLIB)
465 	if (info.options & LWS_SERVER_OPTION_GLIB)
466 		foreign_event_loop_init_and_run_glib();
467 #endif
468 
469 	lws_context_destroy(context);
470 
471 	/* foreign loop specific cleanup and exit */
472 
473 #if defined(LWS_WITH_LIBUV)
474 	if (info.options & LWS_SERVER_OPTION_LIBUV)
475 		foreign_event_loop_cleanup_libuv();
476 #endif
477 #if defined(LWS_WITH_LIBEVENT)
478 	if (info.options & LWS_SERVER_OPTION_LIBEVENT)
479 		foreign_event_loop_cleanup_libevent();
480 #endif
481 #if defined(LWS_WITH_LIBEV)
482 	if (info.options & LWS_SERVER_OPTION_LIBEV)
483 		foreign_event_loop_cleanup_libev();
484 #endif
485 #if defined(LWS_WITH_GLIB)
486 	if (info.options & LWS_SERVER_OPTION_GLIB)
487 		foreign_event_loop_cleanup_glib();
488 #endif
489 
490 	lwsl_user("%s: exiting...\n", __func__);
491 
492 	return 0;
493 }
494