introduce-libwebsockets-test-server-extpoll.patch
[profile/ivi/libwebsockets.git] / test-server / test-server-extpoll.c
1 /*
2  * libwebsockets-test-server-extpoll - libwebsockets external poll loop sample
3  *
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
7  * own poll loop.
8  *
9  * Copyright (C) 2010-2011 Andy Green <andy@warmcat.com>
10  *
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.
15  *
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.
20  *
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,
24  *  MA  02110-1301  USA
25  */
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <getopt.h>
31 #include <string.h>
32 #include <sys/time.h>
33 #include <poll.h>
34
35 #include "../lib/libwebsockets.h"
36
37
38 /*
39  * This demo server shows how to use libwebsockets for one or more
40  * websocket protocols in the same server
41  *
42  * It defines the following websocket protocols:
43  *
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.
48  *
49  *  lws-mirror-protocol: copies any received packet to every connection also
50  *                              using this protocol, including the sender
51  */
52
53 #define MAX_POLL_ELEMENTS 100
54 struct pollfd pollfds[100];
55 int count_pollfds = 0;
56
57  
58
59 enum demo_protocols {
60         /* always first */
61         PROTOCOL_HTTP = 0,
62
63         PROTOCOL_DUMB_INCREMENT,
64         PROTOCOL_LWS_MIRROR,
65
66         /* always last */
67         DEMO_PROTOCOL_COUNT
68 };
69
70
71 #define LOCAL_RESOURCE_PATH DATADIR"/libwebsockets-test-server"
72
73 /* this protocol server (always the first one) just knows how to do HTTP */
74
75 static int callback_http(struct libwebsocket *wsi,
76                 enum libwebsocket_callback_reasons reason, void *user,
77                                                            void *in, size_t len)
78 {
79         switch (reason) {
80         case LWS_CALLBACK_HTTP:
81                 fprintf(stderr, "serving HTTP URI %s\n", (char *)in);
82
83                 if (in && strcmp(in, "/favicon.ico") == 0) {
84                         if (libwebsockets_serve_http_file(wsi,
85                              LOCAL_RESOURCE_PATH"/favicon.ico", "image/x-icon"))
86                                 fprintf(stderr, "Failed to send favicon\n");
87                         break;
88                 }
89
90                 /* send the script... when it runs it'll start websockets */
91
92                 if (libwebsockets_serve_http_file(wsi,
93                                   LOCAL_RESOURCE_PATH"/test.html", "text/html"))
94                         fprintf(stderr, "Failed to send HTTP file\n");
95                 break;
96
97         default:
98                 break;
99         }
100
101         return 0;
102 }
103
104 /* dumb_increment protocol */
105
106 /*
107  * one of these is auto-created for each connection and a pointer to the
108  * appropriate instance is passed to the callback in the user parameter
109  *
110  * for this example protocol we use it to individualize the count for each
111  * connection.
112  */
113
114 struct per_session_data__dumb_increment {
115         int number;
116 };
117
118 static int
119 callback_dumb_increment(struct libwebsocket *wsi,
120                         enum libwebsocket_callback_reasons reason,
121                                                void *user, void *in, size_t len)
122 {
123         int n;
124         unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 +
125                                                   LWS_SEND_BUFFER_POST_PADDING];
126         unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
127         struct per_session_data__dumb_increment *pss = user;
128
129         switch (reason) {
130
131         case LWS_CALLBACK_ESTABLISHED:
132                 pss->number = 0;
133                 break;
134
135         /*
136          * in this protocol, we just use the broadcast action as the chance to
137          * send our own connection-specific data and ignore the broadcast info
138          * that is available in the 'in' parameter
139          */
140
141         case LWS_CALLBACK_BROADCAST:
142                 n = sprintf((char *)p, "%d", pss->number++);
143                 n = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
144                 if (n < 0) {
145                         fprintf(stderr, "ERROR writing to socket");
146                         return 1;
147                 }
148                 break;
149
150         case LWS_CALLBACK_RECEIVE:
151                 fprintf(stderr, "rx %d\n", (int)len);
152                 if (len < 6)
153                         break;
154                 if (strcmp(in, "reset\n") == 0)
155                         pss->number = 0;
156                 break;
157
158         default:
159                 break;
160         }
161
162         return 0;
163 }
164
165
166 /* lws-mirror_protocol */
167
168 #define MAX_MESSAGE_QUEUE 64
169
170 struct per_session_data__lws_mirror {
171         struct libwebsocket *wsi;
172         int ringbuffer_tail;
173 };
174
175 struct a_message {
176         void *payload;
177         size_t len;
178 };
179
180 static struct a_message ringbuffer[MAX_MESSAGE_QUEUE];
181 static int ringbuffer_head;
182
183
184 static int
185 callback_lws_mirror(struct libwebsocket *wsi,
186                         enum libwebsocket_callback_reasons reason,
187                                                void *user, void *in, size_t len)
188 {
189         int n;
190         struct per_session_data__lws_mirror *pss = user;
191
192         switch (reason) {
193
194         case LWS_CALLBACK_ESTABLISHED:
195                 pss->ringbuffer_tail = ringbuffer_head;
196                 pss->wsi = wsi;
197                 break;
198
199         case LWS_CALLBACK_CLIENT_WRITEABLE:
200                 if (pss->ringbuffer_tail != ringbuffer_head) {
201
202                         n = libwebsocket_write(wsi, (unsigned char *)
203                                    ringbuffer[pss->ringbuffer_tail].payload +
204                                    LWS_SEND_BUFFER_PRE_PADDING,
205                                    ringbuffer[pss->ringbuffer_tail].len,
206                                                                 LWS_WRITE_TEXT);
207                         if (n < 0) {
208                                 fprintf(stderr, "ERROR writing to socket");
209                                 exit(1);
210                         }
211
212                         if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
213                                 pss->ringbuffer_tail = 0;
214                         else
215                                 pss->ringbuffer_tail++;
216
217                         if (((ringbuffer_head - pss->ringbuffer_tail) %
218                                   MAX_MESSAGE_QUEUE) < (MAX_MESSAGE_QUEUE - 15))
219                                 libwebsocket_rx_flow_control(wsi, 1);
220
221                         libwebsocket_callback_on_writable(wsi);
222
223                 }
224                 break;
225
226         case LWS_CALLBACK_BROADCAST:
227                 n = libwebsocket_write(wsi, in, len, LWS_WRITE_TEXT);
228                 if (n < 0)
229                         fprintf(stderr, "mirror write failed\n");
230                 break;
231
232         case LWS_CALLBACK_RECEIVE:
233
234                 if (ringbuffer[ringbuffer_head].payload)
235                         free(ringbuffer[ringbuffer_head].payload);
236
237                 ringbuffer[ringbuffer_head].payload =
238                                 malloc(LWS_SEND_BUFFER_PRE_PADDING + len +
239                                                   LWS_SEND_BUFFER_POST_PADDING);
240                 ringbuffer[ringbuffer_head].len = len;
241                 memcpy((char *)ringbuffer[ringbuffer_head].payload +
242                                           LWS_SEND_BUFFER_PRE_PADDING, in, len);
243                 if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
244                         ringbuffer_head = 0;
245                 else
246                         ringbuffer_head++;
247
248                 if (((ringbuffer_head - pss->ringbuffer_tail) %
249                                   MAX_MESSAGE_QUEUE) > (MAX_MESSAGE_QUEUE - 10))
250                         libwebsocket_rx_flow_control(wsi, 0);
251
252                 libwebsocket_callback_on_writable_all_protocol(
253                                                libwebsockets_get_protocol(wsi));
254                 break;
255
256         default:
257                 break;
258         }
259
260         return 0;
261 }
262
263
264 /* list of supported protocols and callbacks */
265
266 static struct libwebsocket_protocols protocols[] = {
267         /* first protocol must always be HTTP handler */
268         [PROTOCOL_HTTP] = {
269                 .name = "http-only",
270                 .callback = callback_http,
271         },
272         [PROTOCOL_DUMB_INCREMENT] = {
273                 .name = "dumb-increment-protocol",
274                 .callback = callback_dumb_increment,
275                 .per_session_data_size =
276                                 sizeof(struct per_session_data__dumb_increment),
277         },
278         [PROTOCOL_LWS_MIRROR] = {
279                 .name = "lws-mirror-protocol",
280                 .callback = callback_lws_mirror,
281                 .per_session_data_size =
282                                 sizeof(struct per_session_data__lws_mirror),
283         },
284         [DEMO_PROTOCOL_COUNT] = {  /* end of list */
285                 .callback = NULL
286         }
287 };
288
289 static struct option options[] = {
290         { "help",       no_argument,            NULL, 'h' },
291         { "port",       required_argument,      NULL, 'p' },
292         { "ssl",        no_argument,            NULL, 's' },
293         { "killmask",   no_argument,            NULL, 'k' },
294         { NULL, 0, 0, 0 }
295 };
296
297 int main(int argc, char **argv)
298 {
299         int n = 0;
300         const char *cert_path =
301                             LOCAL_RESOURCE_PATH"/libwebsockets-test-server.pem";
302         const char *key_path =
303                         LOCAL_RESOURCE_PATH"/libwebsockets-test-server.key.pem";
304         unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 1024 +
305                                                   LWS_SEND_BUFFER_POST_PADDING];
306         int port = 7681;
307         int use_ssl = 0;
308         struct libwebsocket_context *context;
309         int opts = 0;
310         unsigned int oldus = 0;
311
312         fprintf(stderr, "libwebsockets test server with external poll()\n"
313                         "(C) Copyright 2010-2011 Andy Green <andy@warmcat.com> "
314                                                     "licensed under LGPL2.1\n");
315
316         while (n >= 0) {
317                 n = getopt_long(argc, argv, "khsp:", options, NULL);
318                 if (n < 0)
319                         continue;
320                 switch (n) {
321                 case 's':
322                         use_ssl = 1;
323                         break;
324                 case 'k':
325                         opts = LWS_SERVER_OPTION_DEFEAT_CLIENT_MASK;
326                         break;
327                 case 'p':
328                         port = atoi(optarg);
329                         break;
330                 case 'h':
331                         fprintf(stderr, "Usage: test-server "
332                                              "[--port=<p>] [--ssl]\n");
333                         exit(1);
334                 }
335         }
336
337         if (!use_ssl)
338                 cert_path = key_path = NULL;
339
340         context = libwebsocket_create_context(port, protocols, cert_path,
341                                                 key_path, -1, -1, opts);
342         if (context == NULL) {
343                 fprintf(stderr, "libwebsocket init failed\n");
344                 return -1;
345         }
346
347         buf[LWS_SEND_BUFFER_PRE_PADDING] = 'x';
348
349         /*
350          * This is an example of an existing application's explicit poll()
351          * loop that libwebsockets can integrate with.
352          */
353
354         while (1) {
355                 struct timeval tv;
356
357                 /*
358                  * this represents an existing server's single poll action
359                  * which also includes libwebsocket sockets
360                  */
361
362                 n = poll(pollfds, count_pollfds, 25);
363                 if (n < 0)
364                         goto done;
365
366                 if (n)
367                         for (n = 0; n < count_pollfds; n++)
368                                 if (pollfds[n].revents)
369                                         /*
370                                         * returns immediately if the fd does not
371                                         * match anything under libwebsockets
372                                         * control
373                                         */
374                                         libwebsocket_service_fd(context,
375                                                                    &pollfds[n]);
376
377                 /* do our broadcast periodically */
378
379                 gettimeofday(&tv, NULL);
380
381                 /*
382                  * This broadcasts to all dumb-increment-protocol connections
383                  * at 20Hz.
384                  *
385                  * We're just sending a character 'x', in these examples the
386                  * callbacks send their own per-connection content.
387                  *
388                  * You have to send something with nonzero length to get the
389                  * callback actions delivered.
390                  *
391                  * We take care of pre-and-post padding allocation.
392                  */
393
394                 if (((unsigned int)tv.tv_usec - oldus) > 50000) {
395                         libwebsockets_broadcast(
396                                         &protocols[PROTOCOL_DUMB_INCREMENT],
397                                         &buf[LWS_SEND_BUFFER_PRE_PADDING], 1);
398                         oldus = tv.tv_usec;
399                 }
400         }
401
402 done:
403         libwebsocket_context_destroy(context);
404
405         return 0;
406 }