1/*
2 * lws-minimal-ws-server
3 *
4 * Written in 2010-2019 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 a minimal ws server that can cooperate with
10 * other threads cleanly.  Two other threads are started, which fill
11 * a ringbuffer with strings at 10Hz.
12 *
13 * The actual work and thread spawning etc are done in the protocol
14 * implementation in protocol_lws_minimal.c.
15 *
16 * To keep it simple, it serves stuff in the subdirectory "./mount-origin" of
17 * the directory it was started in.
18 * You can change that by changing mount.origin.
19 */
20
21#include <libwebsockets.h>
22#include <string.h>
23#include <signal.h>
24#if defined(WIN32)
25#define HAVE_STRUCT_TIMESPEC
26#if defined(pid_t)
27#undef pid_t
28#endif
29#endif
30#include <pthread.h>
31
32#define LWS_PLUGIN_STATIC
33#include "protocol_lws_minimal.c"
34
35#define COUNT_THREADS 2
36
37static struct lws_protocols protocols[] = {
38	{ "http", lws_callback_http_dummy, 0, 0, 0, NULL, 0 },
39	LWS_PLUGIN_PROTOCOL_MINIMAL,
40	LWS_PROTOCOL_LIST_TERM
41};
42
43static struct lws_context *context;
44static int interrupted, started;
45static pthread_t pthread_service[COUNT_THREADS];
46
47static const struct lws_http_mount mount = {
48	/* .mount_next */		NULL,		/* linked-list "next" */
49	/* .mountpoint */		"/",		/* mountpoint URL */
50	/* .origin */			"./mount-origin", /* serve from dir */
51	/* .def */			"index.html",	/* default filename */
52	/* .protocol */			NULL,
53	/* .cgienv */			NULL,
54	/* .extra_mimetypes */		NULL,
55	/* .interpret */		NULL,
56	/* .cgi_timeout */		0,
57	/* .cache_max_age */		0,
58	/* .auth_mask */		0,
59	/* .cache_reusable */		0,
60	/* .cache_revalidate */		0,
61	/* .cache_intermediaries */	0,
62	/* .origin_protocol */		LWSMPRO_FILE,	/* files in a dir */
63	/* .mountpoint_len */		1,		/* char count */
64	/* .basic_auth_login_file */	NULL,
65};
66
67/*
68 * This demonstrates how to pass a pointer into a specific protocol handler
69 * running on a specific vhost.  In this case, it's our default vhost and
70 * we pass the pvo named "config" with the value a const char * "myconfig".
71 *
72 * This is the preferred way to pass configuration into a specific vhost +
73 * protocol instance.
74 */
75
76static const struct lws_protocol_vhost_options pvo_ops = {
77	NULL,
78	NULL,
79	"config",		/* pvo name */
80	(void *)"myconfig"	/* pvo value */
81};
82
83static const struct lws_protocol_vhost_options pvo = {
84	NULL,		/* "next" pvo linked-list */
85	&pvo_ops,	/* "child" pvo linked-list */
86	"lws-minimal",	/* protocol name we belong to on this vhost */
87	""		/* ignored */
88};
89
90void *thread_service(void *threadid)
91{
92	while (lws_service_tsi(context, 1000,
93			       (int)(lws_intptr_t)threadid) >= 0 &&
94	       !interrupted)
95		;
96
97	pthread_exit(NULL);
98
99	return NULL;
100}
101
102static int
103system_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *link,
104		   int current, int target)
105{
106	struct lws_context *context = mgr->parent;
107	void *retval;
108
109	if (current != target)
110		return 0;
111
112	switch (current) {
113	case LWS_SYSTATE_OPERATIONAL:
114		lwsl_notice("  Service threads: %d\n",
115			    lws_get_count_threads(context));
116
117		/* start all the service threads */
118
119		for (started = 1; started < lws_get_count_threads(context);
120		     started++)
121			if (pthread_create(&pthread_service[started], NULL,
122					   thread_service,
123					   (void *)(lws_intptr_t)started))
124				lwsl_err("Failed to start service thread\n");
125		break;
126	case LWS_SYSTATE_CONTEXT_DESTROYING:
127		/* wait for all the service threads to exit */
128
129		while ((--started) >= 1)
130			pthread_join(pthread_service[started], &retval);
131
132		break;
133	}
134
135	return 0;
136}
137
138lws_state_notify_link_t notifier = { { NULL, NULL, NULL },
139				     system_notify_cb, "app" };
140lws_state_notify_link_t *na[] = { &notifier, NULL };
141
142void sigint_handler(int sig)
143{
144	interrupted = 1;
145	lws_cancel_service(context);
146}
147
148int main(int argc, const char **argv)
149{
150	int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
151	struct lws_context_creation_info info;
152	const char *p;
153	int n = 0;
154
155	signal(SIGINT, sigint_handler);
156
157	if ((p = lws_cmdline_option(argc, argv, "-d")))
158		logs = atoi(p);
159
160	lws_set_log_level(logs, NULL);
161	lwsl_user("LWS minimal ws server + threads + smp | visit http://localhost:7681\n");
162
163	memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
164	info.port = 7681;
165	info.mounts = &mount;
166	info.protocols = protocols;
167	info.pvo = &pvo; /* per-vhost options */
168	info.count_threads = COUNT_THREADS;
169	info.register_notifier_list = na;
170	info.options =
171		LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE;
172
173	context = lws_create_context(&info);
174	if (!context) {
175		lwsl_err("lws init failed\n");
176		return 1;
177	}
178
179	while (n >= 0 && !interrupted)
180		n = lws_service(context, 0);
181
182	lws_context_destroy(context);
183
184	return 0;
185}
186