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,
80 char client_name[128];
84 case LWS_CALLBACK_HTTP:
85 fprintf(stderr, "serving HTTP URI %s\n", (char *)in);
87 if (in && strcmp(in, "/favicon.ico") == 0) {
88 if (libwebsockets_serve_http_file(wsi,
89 LOCAL_RESOURCE_PATH"/favicon.ico", "image/x-icon"))
90 fprintf(stderr, "Failed to send favicon\n");
94 /* send the script... when it runs it'll start websockets */
96 if (libwebsockets_serve_http_file(wsi,
97 LOCAL_RESOURCE_PATH"/test.html", "text/html"))
98 fprintf(stderr, "Failed to send HTTP file\n");
102 * callbacks for managing the external poll() array appear in
103 * protocol 0 callback
106 case LWS_CALLBACK_ADD_POLL_FD:
107 pollfds[count_pollfds].fd = (int)(long)user;
108 pollfds[count_pollfds].events = (int)len;
109 pollfds[count_pollfds++].revents = 0;
112 case LWS_CALLBACK_DEL_POLL_FD:
113 for (n = 0; n < count_pollfds; n++)
114 if (pollfds[n].fd == (int)(long)user)
115 while (n < count_pollfds) {
116 pollfds[n] = pollfds[n + 1];
122 case LWS_CALLBACK_SET_MODE_POLL_FD:
123 for (n = 0; n < count_pollfds; n++)
124 if (pollfds[n].fd == (int)(long)user)
125 pollfds[n].events |= (int)(long)len;
128 case LWS_CALLBACK_CLEAR_MODE_POLL_FD:
129 for (n = 0; n < count_pollfds; n++)
130 if (pollfds[n].fd == (int)(long)user)
131 pollfds[n].events &= ~(int)(long)len;
141 /* dumb_increment protocol */
144 * one of these is auto-created for each connection and a pointer to the
145 * appropriate instance is passed to the callback in the user parameter
147 * for this example protocol we use it to individualize the count for each
151 struct per_session_data__dumb_increment {
156 callback_dumb_increment(struct libwebsocket *wsi,
157 enum libwebsocket_callback_reasons reason,
158 void *user, void *in, size_t len)
161 unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 +
162 LWS_SEND_BUFFER_POST_PADDING];
163 unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
164 struct per_session_data__dumb_increment *pss = user;
168 case LWS_CALLBACK_ESTABLISHED:
173 * in this protocol, we just use the broadcast action as the chance to
174 * send our own connection-specific data and ignore the broadcast info
175 * that is available in the 'in' parameter
178 case LWS_CALLBACK_BROADCAST:
179 n = sprintf((char *)p, "%d", pss->number++);
180 n = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
182 fprintf(stderr, "ERROR writing to socket");
187 case LWS_CALLBACK_RECEIVE:
188 fprintf(stderr, "rx %d\n", (int)len);
191 if (strcmp(in, "reset\n") == 0)
203 /* lws-mirror_protocol */
205 #define MAX_MESSAGE_QUEUE 64
207 struct per_session_data__lws_mirror {
208 struct libwebsocket *wsi;
217 static struct a_message ringbuffer[MAX_MESSAGE_QUEUE];
218 static int ringbuffer_head;
222 callback_lws_mirror(struct libwebsocket *wsi,
223 enum libwebsocket_callback_reasons reason,
224 void *user, void *in, size_t len)
227 struct per_session_data__lws_mirror *pss = user;
231 case LWS_CALLBACK_ESTABLISHED:
232 pss->ringbuffer_tail = ringbuffer_head;
234 libwebsocket_callback_on_writable(wsi);
237 case LWS_CALLBACK_CLIENT_WRITEABLE:
239 if (pss->ringbuffer_tail != ringbuffer_head) {
241 n = libwebsocket_write(wsi, (unsigned char *)
242 ringbuffer[pss->ringbuffer_tail].payload +
243 LWS_SEND_BUFFER_PRE_PADDING,
244 ringbuffer[pss->ringbuffer_tail].len,
248 fprintf(stderr, "ERROR writing to socket");
252 if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
253 pss->ringbuffer_tail = 0;
255 pss->ringbuffer_tail++;
257 if (((ringbuffer_head - pss->ringbuffer_tail) %
258 MAX_MESSAGE_QUEUE) < (MAX_MESSAGE_QUEUE - 15))
259 libwebsocket_rx_flow_control(wsi, 1);
261 libwebsocket_callback_on_writable(wsi);
266 case LWS_CALLBACK_BROADCAST:
267 n = libwebsocket_write(wsi, in, len, LWS_WRITE_TEXT);
269 fprintf(stderr, "mirror write failed\n");
272 case LWS_CALLBACK_RECEIVE:
274 if (ringbuffer[ringbuffer_head].payload)
275 free(ringbuffer[ringbuffer_head].payload);
277 ringbuffer[ringbuffer_head].payload =
278 malloc(LWS_SEND_BUFFER_PRE_PADDING + len +
279 LWS_SEND_BUFFER_POST_PADDING);
280 ringbuffer[ringbuffer_head].len = len;
281 memcpy((char *)ringbuffer[ringbuffer_head].payload +
282 LWS_SEND_BUFFER_PRE_PADDING, in, len);
283 if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
288 if (((ringbuffer_head - pss->ringbuffer_tail) %
289 MAX_MESSAGE_QUEUE) > (MAX_MESSAGE_QUEUE - 10))
290 libwebsocket_rx_flow_control(wsi, 0);
292 libwebsocket_callback_on_writable_all_protocol(
293 libwebsockets_get_protocol(wsi));
304 /* list of supported protocols and callbacks */
306 static struct libwebsocket_protocols protocols[] = {
307 /* first protocol must always be HTTP handler */
310 .callback = callback_http,
312 [PROTOCOL_DUMB_INCREMENT] = {
313 .name = "dumb-increment-protocol",
314 .callback = callback_dumb_increment,
315 .per_session_data_size =
316 sizeof(struct per_session_data__dumb_increment),
318 [PROTOCOL_LWS_MIRROR] = {
319 .name = "lws-mirror-protocol",
320 .callback = callback_lws_mirror,
321 .per_session_data_size =
322 sizeof(struct per_session_data__lws_mirror),
324 [DEMO_PROTOCOL_COUNT] = { /* end of list */
329 static struct option options[] = {
330 { "help", no_argument, NULL, 'h' },
331 { "port", required_argument, NULL, 'p' },
332 { "ssl", no_argument, NULL, 's' },
333 { "killmask", no_argument, NULL, 'k' },
337 int main(int argc, char **argv)
340 const char *cert_path =
341 LOCAL_RESOURCE_PATH"/libwebsockets-test-server.pem";
342 const char *key_path =
343 LOCAL_RESOURCE_PATH"/libwebsockets-test-server.key.pem";
344 unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 1024 +
345 LWS_SEND_BUFFER_POST_PADDING];
348 struct libwebsocket_context *context;
350 unsigned int oldus = 0;
352 fprintf(stderr, "libwebsockets test server with external poll()\n"
353 "(C) Copyright 2010-2011 Andy Green <andy@warmcat.com> "
354 "licensed under LGPL2.1\n");
357 n = getopt_long(argc, argv, "khsp:", options, NULL);
365 opts = LWS_SERVER_OPTION_DEFEAT_CLIENT_MASK;
371 fprintf(stderr, "Usage: test-server "
372 "[--port=<p>] [--ssl]\n");
378 cert_path = key_path = NULL;
380 context = libwebsocket_create_context(port, protocols, cert_path,
381 key_path, -1, -1, opts);
382 if (context == NULL) {
383 fprintf(stderr, "libwebsocket init failed\n");
387 buf[LWS_SEND_BUFFER_PRE_PADDING] = 'x';
390 * This is an example of an existing application's explicit poll()
391 * loop that libwebsockets can integrate with.
398 * this represents an existing server's single poll action
399 * which also includes libwebsocket sockets
402 n = poll(pollfds, count_pollfds, 25);
407 for (n = 0; n < count_pollfds; n++)
408 if (pollfds[n].revents)
410 * returns immediately if the fd does not
411 * match anything under libwebsockets
414 libwebsocket_service_fd(context,
417 /* do our broadcast periodically */
419 gettimeofday(&tv, NULL);
422 * This broadcasts to all dumb-increment-protocol connections
425 * We're just sending a character 'x', in these examples the
426 * callbacks send their own per-connection content.
428 * You have to send something with nonzero length to get the
429 * callback actions delivered.
431 * We take care of pre-and-post padding allocation.
434 if (((unsigned int)tv.tv_usec - oldus) > 50000) {
435 libwebsockets_broadcast(
436 &protocols[PROTOCOL_DUMB_INCREMENT],
437 &buf[LWS_SEND_BUFFER_PRE_PADDING], 1);
443 libwebsocket_context_destroy(context);