2 * libwebsockets-test-server-extpoll - libwebsockets external poll loop sample
4 * This acts the same as libwebsockets-test-server but works with the poll
5 * loop taken out of libwebsockets and into this app. It's an example of how
6 * you can integrate libwebsockets polling into an app that already has its
9 * Copyright (C) 2010-2011 Andy Green <andy@warmcat.com>
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation:
14 * version 2.1 of the License.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
35 #include "../lib/libwebsockets.h"
39 * This demo server shows how to use libwebsockets for one or more
40 * websocket protocols in the same server
42 * It defines the following websocket protocols:
44 * dumb-increment-protocol: once the socket is opened, an incrementing
45 * ascii string is sent down it every 50ms.
46 * If you send "reset\n" on the websocket, then
47 * the incrementing number is reset to 0.
49 * lws-mirror-protocol: copies any received packet to every connection also
50 * using this protocol, including the sender
53 #define MAX_POLL_ELEMENTS 100
54 struct pollfd pollfds[100];
55 int count_pollfds = 0;
63 PROTOCOL_DUMB_INCREMENT,
71 #define LOCAL_RESOURCE_PATH DATADIR"/libwebsockets-test-server"
73 /* this protocol server (always the first one) just knows how to do HTTP */
75 static int callback_http(struct libwebsocket *wsi,
76 enum libwebsocket_callback_reasons reason, void *user,
82 case LWS_CALLBACK_HTTP:
83 fprintf(stderr, "serving HTTP URI %s\n", (char *)in);
85 if (in && strcmp(in, "/favicon.ico") == 0) {
86 if (libwebsockets_serve_http_file(wsi,
87 LOCAL_RESOURCE_PATH"/favicon.ico", "image/x-icon"))
88 fprintf(stderr, "Failed to send favicon\n");
92 /* send the script... when it runs it'll start websockets */
94 if (libwebsockets_serve_http_file(wsi,
95 LOCAL_RESOURCE_PATH"/test.html", "text/html"))
96 fprintf(stderr, "Failed to send HTTP file\n");
100 * callbacks for managing the external poll() array appear in
104 case LWS_CALLBACK_ADD_POLL_FD:
105 pollfds[count_pollfds].fd = (int)(long)user;
106 pollfds[count_pollfds].events = (int)len;
107 pollfds[count_pollfds++].revents = 0;
110 case LWS_CALLBACK_DEL_POLL_FD:
111 for (n = 0; n < count_pollfds; n++)
112 if (pollfds[n].fd == (int)(long)user)
113 while (n < count_pollfds) {
114 pollfds[n] = pollfds[n + 1];
120 case LWS_CALLBACK_SET_MODE_POLL_FD:
121 for (n = 0; n < count_pollfds; n++)
122 if (pollfds[n].fd == (int)(long)user)
123 pollfds[n].events |= (int)(long)len;
126 case LWS_CALLBACK_CLEAR_MODE_POLL_FD:
127 for (n = 0; n < count_pollfds; n++)
128 if (pollfds[n].fd == (int)(long)user)
129 pollfds[n].events &= ~(int)(long)len;
139 /* dumb_increment protocol */
142 * one of these is auto-created for each connection and a pointer to the
143 * appropriate instance is passed to the callback in the user parameter
145 * for this example protocol we use it to individualize the count for each
149 struct per_session_data__dumb_increment {
154 callback_dumb_increment(struct libwebsocket *wsi,
155 enum libwebsocket_callback_reasons reason,
156 void *user, void *in, size_t len)
159 unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 +
160 LWS_SEND_BUFFER_POST_PADDING];
161 unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
162 struct per_session_data__dumb_increment *pss = user;
166 case LWS_CALLBACK_ESTABLISHED:
171 * in this protocol, we just use the broadcast action as the chance to
172 * send our own connection-specific data and ignore the broadcast info
173 * that is available in the 'in' parameter
176 case LWS_CALLBACK_BROADCAST:
177 n = sprintf((char *)p, "%d", pss->number++);
178 n = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
180 fprintf(stderr, "ERROR writing to socket");
185 case LWS_CALLBACK_RECEIVE:
186 fprintf(stderr, "rx %d\n", (int)len);
189 if (strcmp(in, "reset\n") == 0)
201 /* lws-mirror_protocol */
203 #define MAX_MESSAGE_QUEUE 64
205 struct per_session_data__lws_mirror {
206 struct libwebsocket *wsi;
215 static struct a_message ringbuffer[MAX_MESSAGE_QUEUE];
216 static int ringbuffer_head;
220 callback_lws_mirror(struct libwebsocket *wsi,
221 enum libwebsocket_callback_reasons reason,
222 void *user, void *in, size_t len)
225 struct per_session_data__lws_mirror *pss = user;
229 case LWS_CALLBACK_ESTABLISHED:
230 pss->ringbuffer_tail = ringbuffer_head;
232 libwebsocket_callback_on_writable(wsi);
235 case LWS_CALLBACK_CLIENT_WRITEABLE:
237 if (pss->ringbuffer_tail != ringbuffer_head) {
239 n = libwebsocket_write(wsi, (unsigned char *)
240 ringbuffer[pss->ringbuffer_tail].payload +
241 LWS_SEND_BUFFER_PRE_PADDING,
242 ringbuffer[pss->ringbuffer_tail].len,
246 fprintf(stderr, "ERROR writing to socket");
250 if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
251 pss->ringbuffer_tail = 0;
253 pss->ringbuffer_tail++;
255 if (((ringbuffer_head - pss->ringbuffer_tail) %
256 MAX_MESSAGE_QUEUE) < (MAX_MESSAGE_QUEUE - 15))
257 libwebsocket_rx_flow_control(wsi, 1);
259 libwebsocket_callback_on_writable(wsi);
264 case LWS_CALLBACK_BROADCAST:
265 n = libwebsocket_write(wsi, in, len, LWS_WRITE_TEXT);
267 fprintf(stderr, "mirror write failed\n");
270 case LWS_CALLBACK_RECEIVE:
272 if (ringbuffer[ringbuffer_head].payload)
273 free(ringbuffer[ringbuffer_head].payload);
275 ringbuffer[ringbuffer_head].payload =
276 malloc(LWS_SEND_BUFFER_PRE_PADDING + len +
277 LWS_SEND_BUFFER_POST_PADDING);
278 ringbuffer[ringbuffer_head].len = len;
279 memcpy((char *)ringbuffer[ringbuffer_head].payload +
280 LWS_SEND_BUFFER_PRE_PADDING, in, len);
281 if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
286 if (((ringbuffer_head - pss->ringbuffer_tail) %
287 MAX_MESSAGE_QUEUE) > (MAX_MESSAGE_QUEUE - 10))
288 libwebsocket_rx_flow_control(wsi, 0);
290 libwebsocket_callback_on_writable_all_protocol(
291 libwebsockets_get_protocol(wsi));
302 /* list of supported protocols and callbacks */
304 static struct libwebsocket_protocols protocols[] = {
305 /* first protocol must always be HTTP handler */
308 .callback = callback_http,
310 [PROTOCOL_DUMB_INCREMENT] = {
311 .name = "dumb-increment-protocol",
312 .callback = callback_dumb_increment,
313 .per_session_data_size =
314 sizeof(struct per_session_data__dumb_increment),
316 [PROTOCOL_LWS_MIRROR] = {
317 .name = "lws-mirror-protocol",
318 .callback = callback_lws_mirror,
319 .per_session_data_size =
320 sizeof(struct per_session_data__lws_mirror),
322 [DEMO_PROTOCOL_COUNT] = { /* end of list */
327 static struct option options[] = {
328 { "help", no_argument, NULL, 'h' },
329 { "port", required_argument, NULL, 'p' },
330 { "ssl", no_argument, NULL, 's' },
331 { "killmask", no_argument, NULL, 'k' },
335 int main(int argc, char **argv)
338 const char *cert_path =
339 LOCAL_RESOURCE_PATH"/libwebsockets-test-server.pem";
340 const char *key_path =
341 LOCAL_RESOURCE_PATH"/libwebsockets-test-server.key.pem";
342 unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 1024 +
343 LWS_SEND_BUFFER_POST_PADDING];
346 struct libwebsocket_context *context;
348 unsigned int oldus = 0;
350 fprintf(stderr, "libwebsockets test server with external poll()\n"
351 "(C) Copyright 2010-2011 Andy Green <andy@warmcat.com> "
352 "licensed under LGPL2.1\n");
355 n = getopt_long(argc, argv, "khsp:", options, NULL);
363 opts = LWS_SERVER_OPTION_DEFEAT_CLIENT_MASK;
369 fprintf(stderr, "Usage: test-server "
370 "[--port=<p>] [--ssl]\n");
376 cert_path = key_path = NULL;
378 context = libwebsocket_create_context(port, protocols, cert_path,
379 key_path, -1, -1, opts);
380 if (context == NULL) {
381 fprintf(stderr, "libwebsocket init failed\n");
385 buf[LWS_SEND_BUFFER_PRE_PADDING] = 'x';
388 * This is an example of an existing application's explicit poll()
389 * loop that libwebsockets can integrate with.
396 * this represents an existing server's single poll action
397 * which also includes libwebsocket sockets
400 n = poll(pollfds, count_pollfds, 25);
405 for (n = 0; n < count_pollfds; n++)
406 if (pollfds[n].revents)
408 * returns immediately if the fd does not
409 * match anything under libwebsockets
412 libwebsocket_service_fd(context,
415 /* do our broadcast periodically */
417 gettimeofday(&tv, NULL);
420 * This broadcasts to all dumb-increment-protocol connections
423 * We're just sending a character 'x', in these examples the
424 * callbacks send their own per-connection content.
426 * You have to send something with nonzero length to get the
427 * callback actions delivered.
429 * We take care of pre-and-post padding allocation.
432 if (((unsigned int)tv.tv_usec - oldus) > 50000) {
433 libwebsockets_broadcast(
434 &protocols[PROTOCOL_DUMB_INCREMENT],
435 &buf[LWS_SEND_BUFFER_PRE_PADDING], 1);
441 libwebsocket_context_destroy(context);