• 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 #include "private.h"
27 
28 static struct lws_context_creation_info info;
29 static const struct ops *ops = NULL;
30 struct lws_context *context;
31 int lifetime = 5, reported;
32 
33 enum {
34 	TEST_STATE_CREATE_LWS_CONTEXT,
35 	TEST_STATE_DESTROY_LWS_CONTEXT,
36 	TEST_STATE_EXIT
37 };
38 
39 static int sequence = TEST_STATE_CREATE_LWS_CONTEXT;
40 
41 static const struct lws_http_mount mount = {
42 	/* .mount_next */		NULL,		/* linked-list "next" */
43 	/* .mountpoint */		"/",		/* mountpoint URL */
44 	/* .origin */			"./mount-origin", /* serve from dir */
45 	/* .def */			"index.html",	/* default filename */
46 	/* .protocol */			NULL,
47 	/* .cgienv */			NULL,
48 	/* .extra_mimetypes */		NULL,
49 	/* .interpret */		NULL,
50 	/* .cgi_timeout */		0,
51 	/* .cache_max_age */		0,
52 	/* .auth_mask */		0,
53 	/* .cache_reusable */		0,
54 	/* .cache_revalidate */		0,
55 	/* .cache_intermediaries */	0,
56 	/* .origin_protocol */		LWSMPRO_FILE,	/* files in a dir */
57 	/* .mountpoint_len */		1,		/* char count */
58 	/* .basic_auth_login_file */	NULL,
59 };
60 
61 void
signal_cb(int signum)62 signal_cb(int signum)
63 {
64 	lwsl_notice("Signal %d caught, exiting...\n", signum);
65 
66 	switch (signum) {
67 	case SIGTERM:
68 	case SIGINT:
69 		break;
70 	default:
71 		break;
72 	}
73 
74 	lws_context_destroy(context);
75 }
76 
77 static int
callback_http(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)78 callback_http(struct lws *wsi, enum lws_callback_reasons reason,
79 	      void *user, void *in, size_t len)
80 {
81 	switch (reason) {
82 
83 	case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
84 		lwsl_user("LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: resp %u\n",
85 				lws_http_client_http_response(wsi));
86 		break;
87 
88 	/* because we are protocols[0] ... */
89 	case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
90 		lwsl_err("CLIENT_CONNECTION_ERROR: %s\n",
91 			 in ? (char *)in : "(null)");
92 		break;
93 
94 	/* chunks of chunked content, with header removed */
95 	case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
96 		lwsl_user("RECEIVE_CLIENT_HTTP_READ: read %d\n", (int)len);
97 		lwsl_hexdump_info(in, len);
98 		return 0; /* don't passthru */
99 
100 	/* uninterpreted http content */
101 	case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
102 		{
103 			char buffer[1024 + LWS_PRE];
104 			char *px = buffer + LWS_PRE;
105 			int lenx = sizeof(buffer) - LWS_PRE;
106 
107 			if (lws_http_client_read(wsi, &px, &lenx) < 0)
108 				return -1;
109 		}
110 		return 0; /* don't passthru */
111 
112 	case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
113 		lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP %s\n",
114 			  lws_wsi_tag(wsi));
115 		break;
116 
117 	case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
118 		lwsl_info("%s: closed: %s\n", __func__, lws_wsi_tag(wsi));
119 		break;
120 
121 	default:
122 		break;
123 	}
124 
125 	return lws_callback_http_dummy(wsi, reason, user, in, len);
126 }
127 
128 static const struct lws_protocols protocols[] = {
129 	{ "httptest", callback_http, 0, 0, 0, NULL, 0},
130 	LWS_PROTOCOL_LIST_TERM
131 };
132 
133 static int
do_client_conn(void)134 do_client_conn(void)
135 {
136 	struct lws_client_connect_info i;
137 
138 	memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */
139 
140 	i.context		= context;
141 
142 	i.ssl_connection	= LCCSCF_USE_SSL;
143 	i.port			= 443;
144 	i.address		= "warmcat.com";
145 
146 	i.ssl_connection	|= LCCSCF_H2_QUIRK_OVERFLOWS_TXCR |
147 				   LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM;
148 	i.path			= "/";
149 	i.host			= i.address;
150 	i.origin		= i.address;
151 	i.method		= "GET";
152 	i.local_protocol_name	= protocols[0].name;
153 #if defined(LWS_WITH_SYS_FAULT_INJECTION)
154 	i.fi_wsi_name		= "user";
155 #endif
156 
157 	if (!lws_client_connect_via_info(&i)) {
158 		lwsl_err("Client creation failed\n");
159 
160 		return 1;
161 	}
162 
163 	return 0;
164 }
165 
166 
167 /* this is called at 1Hz using a foreign loop timer */
168 
169 void
foreign_timer_service(void * foreign_loop)170 foreign_timer_service(void *foreign_loop)
171 {
172 	void *foreign_loops[1];
173 
174 	lwsl_user("Foreign 1Hz timer\n");
175 
176 	if (sequence == TEST_STATE_EXIT && !context && !reported) {
177 		/*
178 		 * at this point the lws_context_destroy() we did earlier
179 		 * has completed and the entire context is wholly destroyed
180 		 */
181 		lwsl_user("lws_destroy_context() done, continuing for 5s\n");
182 		reported = 1;
183 	}
184 
185 	if (--lifetime)
186 		return;
187 
188 	switch (sequence++) {
189 	case TEST_STATE_CREATE_LWS_CONTEXT:
190 		/* this only has to exist for the duration of create context */
191 		foreign_loops[0] = foreign_loop;
192 		info.foreign_loops = foreign_loops;
193 
194 		context = lws_create_context(&info);
195 		if (!context) {
196 			lwsl_err("lws init failed\n");
197 			return;
198 		}
199 		lwsl_user("LWS Context created and will be active for 10s\n");
200 
201 		do_client_conn();
202 
203 		lifetime = 11;
204 		break;
205 
206 	case TEST_STATE_DESTROY_LWS_CONTEXT:
207 		/* cleanup the lws part */
208 		lwsl_user("Destroying lws context and continuing loop for 5s\n");
209 		lws_context_destroy(context);
210 		lifetime = 6;
211 		break;
212 
213 	case TEST_STATE_EXIT:
214 		lwsl_user("Deciding to exit foreign loop too\n");
215 		ops->stop();
216 		break;
217 	default:
218 		break;
219 	}
220 }
221 
main(int argc,const char ** argv)222 int main(int argc, const char **argv)
223 {
224 	const char *p;
225 	int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
226 			/* for LLL_ verbosity above NOTICE to be built into lws,
227 			 * lws must have been configured and built with
228 			 * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */
229 			/* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
230 			/* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
231 			/* | LLL_DEBUG */;
232 
233 	if ((p = lws_cmdline_option(argc, argv, "-d")))
234 		logs = atoi(p);
235 
236 	lws_set_log_level(logs, NULL);
237 	lwsl_user("LWS minimal http server eventlib + foreign loop |"
238 		  " visit http://localhost:7681\n");
239 
240 	/*
241 	 * We prepare the info here, but don't use it until later in the
242 	 * timer callback, to demonstrate the independence of the foreign loop
243 	 * and lws.
244 	 */
245 
246 	memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
247 	info.port = 7681;
248 	if ((p = lws_cmdline_option(argc, argv, "-p")))
249 		info.port = atoi(p);
250 	info.mounts = &mount;
251 	info.error_document_404 = "/404.html";
252 	info.pcontext = &context;
253 	info.protocols = protocols;
254 	info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT |
255 		LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE;
256 
257 	if (lws_cmdline_option(argc, argv, "-s")) {
258 		info.ssl_cert_filepath = "localhost-100y.cert";
259 		info.ssl_private_key_filepath = "localhost-100y.key";
260 	}
261 
262 	/*
263 	 * We configure lws to use the chosen event loop, and select the
264 	 * matching event-lib specific code for our demo operations
265 	 */
266 
267 #if defined(LWS_WITH_LIBUV)
268 	if (lws_cmdline_option(argc, argv, "--uv")) {
269 		info.options |= LWS_SERVER_OPTION_LIBUV;
270 		ops = &ops_libuv;
271 		lwsl_notice("%s: using libuv event loop\n", __func__);
272 	} else
273 #endif
274 #if defined(LWS_WITH_LIBEVENT)
275 		if (lws_cmdline_option(argc, argv, "--event")) {
276 			info.options |= LWS_SERVER_OPTION_LIBEVENT;
277 			ops = &ops_libevent;
278 			lwsl_notice("%s: using libevent loop\n", __func__);
279 		} else
280 #endif
281 #if defined(LWS_WITH_LIBEV)
282 			if (lws_cmdline_option(argc, argv, "--ev")) {
283 				info.options |= LWS_SERVER_OPTION_LIBEV;
284 				ops = &ops_libev;
285 				lwsl_notice("%s: using libev loop\n", __func__);
286 			} else
287 #endif
288 #if defined(LWS_WITH_GLIB)
289 				if (lws_cmdline_option(argc, argv, "--glib")) {
290 					info.options |= LWS_SERVER_OPTION_GLIB;
291 					ops = &ops_glib;
292 					lwsl_notice("%s: using glib loop\n", __func__);
293 				} else
294 #endif
295 #if defined(LWS_WITH_SDEVENT)
296 					if (lws_cmdline_option(argc, argv, "--sd")) {
297 						info.options |= LWS_SERVER_OPTION_SDEVENT;
298 						ops = &ops_sdevent;
299 						lwsl_notice("%s: using sd-event loop\n", __func__);
300 					} else
301 #endif
302 #if defined(LWS_WITH_ULOOP)
303 					if (lws_cmdline_option(argc, argv, "--uloop")) {
304 						info.options |= LWS_SERVER_OPTION_ULOOP;
305 						ops = &ops_uloop;
306 						lwsl_notice("%s: using uloop loop\n", __func__);
307 					} else
308 #endif
309 				{
310 				lwsl_err("This app only makes sense when used\n");
311 				lwsl_err(" with a foreign loop, --uv, --event, --glib, --ev or --sd\n");
312 
313 				return 1;
314 				}
315 
316 	lwsl_user("  This app creates a foreign event loop with a timer +\n");
317 	lwsl_user("  signalhandler, and performs a test in three phases:\n");
318 	lwsl_user("\n");
319 	lwsl_user("  1) 5s: Runs the loop with just the timer\n");
320 	lwsl_user("  2) 10s: create an lws context serving on localhost:7681\n");
321 	lwsl_user("     using the same foreign loop.  Destroy it after 10s.\n");
322 	lwsl_user("  3) 5s: Run the loop again with just the timer\n");
323 	lwsl_user("\n");
324 	lwsl_user("  Finally close only the timer and signalhandler and\n");
325 	lwsl_user("   exit the loop cleanly\n");
326 	lwsl_user("\n");
327 
328 	/* foreign loop specific startup and run */
329 
330 	ops->init_and_run();
331 
332 	lws_context_destroy(context);
333 
334 	/* foreign loop specific cleanup and exit */
335 
336 	ops->cleanup();
337 
338 	lwsl_user("%s: exiting...\n", __func__);
339 
340 	return 0;
341 }
342