2 * libwebsockets-test-server - libwebsockets test implementation
4 * Copyright (C) 2010-2011 Andy Green <andy@warmcat.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation:
9 * version 2.1 of the License.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
29 #include "../lib/libwebsockets.h"
33 * This demo server shows how to use libwebsockets for one or more
34 * websocket protocols in the same server
36 * It defines the following websocket protocols:
38 * dumb-increment-protocol: once the socket is opened, an incrementing
39 * ascii string is sent down it every 50ms.
40 * If you send "reset\n" on the websocket, then
41 * the incrementing number is reset to 0.
43 * lws-mirror-protocol: copies any received packet to every connection also
44 * using this protocol, including the sender
51 PROTOCOL_DUMB_INCREMENT,
59 #define LOCAL_RESOURCE_PATH DATADIR"/libwebsockets-test-server"
61 /* this protocol server (always the first one) just knows how to do HTTP */
63 static int callback_http(struct libwebsocket *wsi,
64 enum libwebsocket_callback_reasons reason, void *user,
67 char client_name[128];
71 case LWS_CALLBACK_HTTP:
72 fprintf(stderr, "serving HTTP URI %s\n", (char *)in);
74 if (in && strcmp(in, "/favicon.ico") == 0) {
75 if (libwebsockets_serve_http_file(wsi,
76 LOCAL_RESOURCE_PATH"/favicon.ico", "image/x-icon"))
77 fprintf(stderr, "Failed to send favicon\n");
81 /* send the script... when it runs it'll start websockets */
83 if (libwebsockets_serve_http_file(wsi,
84 LOCAL_RESOURCE_PATH"/test.html", "text/html"))
85 fprintf(stderr, "Failed to send HTTP file\n");
89 * callback for confirming to continue with client IP appear in
90 * protocol 0 callback since no websocket protocol has been agreed
91 * yet. You can just ignore this if you won't filter on client IP
92 * since the default uhandled callback return is 0 meaning let the
93 * connection continue.
96 case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
98 libwebsockets_get_peer_addresses((int)(long)user, client_name,
99 sizeof(client_name), client_ip, sizeof(client_ip));
101 fprintf(stderr, "Received network connect from %s (%s)\n",
102 client_name, client_ip);
104 /* if we returned non-zero from here, we kill the connection */
114 /* dumb_increment protocol */
117 * one of these is auto-created for each connection and a pointer to the
118 * appropriate instance is passed to the callback in the user parameter
120 * for this example protocol we use it to individualize the count for each
124 struct per_session_data__dumb_increment {
129 callback_dumb_increment(struct libwebsocket *wsi,
130 enum libwebsocket_callback_reasons reason,
131 void *user, void *in, size_t len)
134 unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 +
135 LWS_SEND_BUFFER_POST_PADDING];
136 unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
137 struct per_session_data__dumb_increment *pss = user;
141 case LWS_CALLBACK_ESTABLISHED:
146 * in this protocol, we just use the broadcast action as the chance to
147 * send our own connection-specific data and ignore the broadcast info
148 * that is available in the 'in' parameter
151 case LWS_CALLBACK_BROADCAST:
152 n = sprintf((char *)p, "%d", pss->number++);
153 n = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
155 fprintf(stderr, "ERROR writing to socket");
160 case LWS_CALLBACK_RECEIVE:
161 fprintf(stderr, "rx %d\n", (int)len);
164 if (strcmp(in, "reset\n") == 0)
176 /* lws-mirror_protocol */
178 #define MAX_MESSAGE_QUEUE 64
180 struct per_session_data__lws_mirror {
181 struct libwebsocket *wsi;
190 static struct a_message ringbuffer[MAX_MESSAGE_QUEUE];
191 static int ringbuffer_head;
195 callback_lws_mirror(struct libwebsocket *wsi,
196 enum libwebsocket_callback_reasons reason,
197 void *user, void *in, size_t len)
200 struct per_session_data__lws_mirror *pss = user;
204 case LWS_CALLBACK_ESTABLISHED:
205 pss->ringbuffer_tail = ringbuffer_head;
209 case LWS_CALLBACK_CLIENT_WRITEABLE:
210 if (pss->ringbuffer_tail != ringbuffer_head) {
212 n = libwebsocket_write(wsi, (unsigned char *)
213 ringbuffer[pss->ringbuffer_tail].payload +
214 LWS_SEND_BUFFER_PRE_PADDING,
215 ringbuffer[pss->ringbuffer_tail].len,
218 fprintf(stderr, "ERROR writing to socket");
222 if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
223 pss->ringbuffer_tail = 0;
225 pss->ringbuffer_tail++;
227 if (((ringbuffer_head - pss->ringbuffer_tail) %
228 MAX_MESSAGE_QUEUE) < (MAX_MESSAGE_QUEUE - 15))
229 libwebsocket_rx_flow_control(wsi, 1);
231 libwebsocket_callback_on_writable(wsi);
236 case LWS_CALLBACK_BROADCAST:
237 n = libwebsocket_write(wsi, in, len, LWS_WRITE_TEXT);
239 fprintf(stderr, "mirror write failed\n");
242 case LWS_CALLBACK_RECEIVE:
244 if (ringbuffer[ringbuffer_head].payload)
245 free(ringbuffer[ringbuffer_head].payload);
247 ringbuffer[ringbuffer_head].payload =
248 malloc(LWS_SEND_BUFFER_PRE_PADDING + len +
249 LWS_SEND_BUFFER_POST_PADDING);
250 ringbuffer[ringbuffer_head].len = len;
251 memcpy((char *)ringbuffer[ringbuffer_head].payload +
252 LWS_SEND_BUFFER_PRE_PADDING, in, len);
253 if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
258 if (((ringbuffer_head - pss->ringbuffer_tail) %
259 MAX_MESSAGE_QUEUE) > (MAX_MESSAGE_QUEUE - 10))
260 libwebsocket_rx_flow_control(wsi, 0);
262 libwebsocket_callback_on_writable_all_protocol(
263 libwebsockets_get_protocol(wsi));
274 /* list of supported protocols and callbacks */
276 static struct libwebsocket_protocols protocols[] = {
277 /* first protocol must always be HTTP handler */
280 .callback = callback_http,
282 [PROTOCOL_DUMB_INCREMENT] = {
283 .name = "dumb-increment-protocol",
284 .callback = callback_dumb_increment,
285 .per_session_data_size =
286 sizeof(struct per_session_data__dumb_increment),
288 [PROTOCOL_LWS_MIRROR] = {
289 .name = "lws-mirror-protocol",
290 .callback = callback_lws_mirror,
291 .per_session_data_size =
292 sizeof(struct per_session_data__lws_mirror),
294 [DEMO_PROTOCOL_COUNT] = { /* end of list */
299 static struct option options[] = {
300 { "help", no_argument, NULL, 'h' },
301 { "port", required_argument, NULL, 'p' },
302 { "ssl", no_argument, NULL, 's' },
303 { "killmask", no_argument, NULL, 'k' },
307 int main(int argc, char **argv)
310 const char *cert_path =
311 LOCAL_RESOURCE_PATH"/libwebsockets-test-server.pem";
312 const char *key_path =
313 LOCAL_RESOURCE_PATH"/libwebsockets-test-server.key.pem";
314 unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 1024 +
315 LWS_SEND_BUFFER_POST_PADDING];
318 struct libwebsocket_context *context;
321 unsigned int oldus = 0;
324 fprintf(stderr, "libwebsockets test server\n"
325 "(C) Copyright 2010-2011 Andy Green <andy@warmcat.com> "
326 "licensed under LGPL2.1\n");
329 n = getopt_long(argc, argv, "khsp:", options, NULL);
337 opts = LWS_SERVER_OPTION_DEFEAT_CLIENT_MASK;
343 fprintf(stderr, "Usage: test-server "
344 "[--port=<p>] [--ssl]\n");
350 cert_path = key_path = NULL;
352 context = libwebsocket_create_context(port, protocols, cert_path,
353 key_path, -1, -1, opts);
354 if (context == NULL) {
355 fprintf(stderr, "libwebsocket init failed\n");
359 buf[LWS_SEND_BUFFER_PRE_PADDING] = 'x';
364 * This example shows how to work with no forked service loop
367 fprintf(stderr, " Using no-fork service loop\n");
372 gettimeofday(&tv, NULL);
375 * This broadcasts to all dumb-increment-protocol connections
378 * We're just sending a character 'x', in these examples the
379 * callbacks send their own per-connection content.
381 * You have to send something with nonzero length to get the
382 * callback actions delivered.
384 * We take care of pre-and-post padding allocation.
387 if (((unsigned int)tv.tv_usec - oldus) > 50000) {
388 libwebsockets_broadcast(
389 &protocols[PROTOCOL_DUMB_INCREMENT],
390 &buf[LWS_SEND_BUFFER_PRE_PADDING], 1);
395 * This example server does not fork or create a thread for
396 * websocket service, it all runs in this single loop. So,
397 * we have to give the websockets an opportunity to service
400 * If no socket is needing service, the call below returns
401 * immediately and quickly.
404 libwebsocket_service(context, 50);
410 * This example shows how to work with the forked websocket service loop
413 fprintf(stderr, " Using forked service loop\n");
416 * This forks the websocket service action into a subprocess so we
417 * don't have to take care about it.
420 n = libwebsockets_fork_service_loop(context);
422 fprintf(stderr, "Unable to fork service loop %d\n", n);
431 * This broadcasts to all dumb-increment-protocol connections
434 * We're just sending a character 'x', in these examples the
435 * callbacks send their own per-connection content.
437 * You have to send something with nonzero length to get the
438 * callback actions delivered.
440 * We take care of pre-and-post padding allocation.
443 libwebsockets_broadcast(&protocols[PROTOCOL_DUMB_INCREMENT],
444 &buf[LWS_SEND_BUFFER_PRE_PADDING], 1);
449 libwebsocket_context_destroy(context);