diff options
Diffstat (limited to 'test-server/test-server-extpoll.c')
-rw-r--r-- | test-server/test-server-extpoll.c | 406 |
1 files changed, 406 insertions, 0 deletions
diff --git a/test-server/test-server-extpoll.c b/test-server/test-server-extpoll.c new file mode 100644 index 0000000..cccb50e --- /dev/null +++ b/test-server/test-server-extpoll.c @@ -0,0 +1,406 @@ +/* + * libwebsockets-test-server-extpoll - libwebsockets external poll loop sample + * + * This acts the same as libwebsockets-test-server but works with the poll + * loop taken out of libwebsockets and into this app. It's an example of how + * you can integrate libwebsockets polling into an app that already has its + * own poll loop. + * + * Copyright (C) 2010-2011 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <getopt.h> +#include <string.h> +#include <sys/time.h> +#include <poll.h> + +#include "../lib/libwebsockets.h" + + +/* + * This demo server shows how to use libwebsockets for one or more + * websocket protocols in the same server + * + * It defines the following websocket protocols: + * + * dumb-increment-protocol: once the socket is opened, an incrementing + * ascii string is sent down it every 50ms. + * If you send "reset\n" on the websocket, then + * the incrementing number is reset to 0. + * + * lws-mirror-protocol: copies any received packet to every connection also + * using this protocol, including the sender + */ + +#define MAX_POLL_ELEMENTS 100 +struct pollfd pollfds[100]; +int count_pollfds = 0; + + + +enum demo_protocols { + /* always first */ + PROTOCOL_HTTP = 0, + + PROTOCOL_DUMB_INCREMENT, + PROTOCOL_LWS_MIRROR, + + /* always last */ + DEMO_PROTOCOL_COUNT +}; + + +#define LOCAL_RESOURCE_PATH DATADIR"/libwebsockets-test-server" + +/* this protocol server (always the first one) just knows how to do HTTP */ + +static int callback_http(struct libwebsocket *wsi, + enum libwebsocket_callback_reasons reason, void *user, + void *in, size_t len) +{ + switch (reason) { + case LWS_CALLBACK_HTTP: + fprintf(stderr, "serving HTTP URI %s\n", (char *)in); + + if (in && strcmp(in, "/favicon.ico") == 0) { + if (libwebsockets_serve_http_file(wsi, + LOCAL_RESOURCE_PATH"/favicon.ico", "image/x-icon")) + fprintf(stderr, "Failed to send favicon\n"); + break; + } + + /* send the script... when it runs it'll start websockets */ + + if (libwebsockets_serve_http_file(wsi, + LOCAL_RESOURCE_PATH"/test.html", "text/html")) + fprintf(stderr, "Failed to send HTTP file\n"); + break; + + default: + break; + } + + return 0; +} + +/* dumb_increment protocol */ + +/* + * one of these is auto-created for each connection and a pointer to the + * appropriate instance is passed to the callback in the user parameter + * + * for this example protocol we use it to individualize the count for each + * connection. + */ + +struct per_session_data__dumb_increment { + int number; +}; + +static int +callback_dumb_increment(struct libwebsocket *wsi, + enum libwebsocket_callback_reasons reason, + void *user, void *in, size_t len) +{ + int n; + unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 + + LWS_SEND_BUFFER_POST_PADDING]; + unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING]; + struct per_session_data__dumb_increment *pss = user; + + switch (reason) { + + case LWS_CALLBACK_ESTABLISHED: + pss->number = 0; + break; + + /* + * in this protocol, we just use the broadcast action as the chance to + * send our own connection-specific data and ignore the broadcast info + * that is available in the 'in' parameter + */ + + case LWS_CALLBACK_BROADCAST: + n = sprintf((char *)p, "%d", pss->number++); + n = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT); + if (n < 0) { + fprintf(stderr, "ERROR writing to socket"); + return 1; + } + break; + + case LWS_CALLBACK_RECEIVE: + fprintf(stderr, "rx %d\n", (int)len); + if (len < 6) + break; + if (strcmp(in, "reset\n") == 0) + pss->number = 0; + break; + + default: + break; + } + + return 0; +} + + +/* lws-mirror_protocol */ + +#define MAX_MESSAGE_QUEUE 64 + +struct per_session_data__lws_mirror { + struct libwebsocket *wsi; + int ringbuffer_tail; +}; + +struct a_message { + void *payload; + size_t len; +}; + +static struct a_message ringbuffer[MAX_MESSAGE_QUEUE]; +static int ringbuffer_head; + + +static int +callback_lws_mirror(struct libwebsocket *wsi, + enum libwebsocket_callback_reasons reason, + void *user, void *in, size_t len) +{ + int n; + struct per_session_data__lws_mirror *pss = user; + + switch (reason) { + + case LWS_CALLBACK_ESTABLISHED: + pss->ringbuffer_tail = ringbuffer_head; + pss->wsi = wsi; + break; + + case LWS_CALLBACK_CLIENT_WRITEABLE: + if (pss->ringbuffer_tail != ringbuffer_head) { + + n = libwebsocket_write(wsi, (unsigned char *) + ringbuffer[pss->ringbuffer_tail].payload + + LWS_SEND_BUFFER_PRE_PADDING, + ringbuffer[pss->ringbuffer_tail].len, + LWS_WRITE_TEXT); + if (n < 0) { + fprintf(stderr, "ERROR writing to socket"); + exit(1); + } + + if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1)) + pss->ringbuffer_tail = 0; + else + pss->ringbuffer_tail++; + + if (((ringbuffer_head - pss->ringbuffer_tail) % + MAX_MESSAGE_QUEUE) < (MAX_MESSAGE_QUEUE - 15)) + libwebsocket_rx_flow_control(wsi, 1); + + libwebsocket_callback_on_writable(wsi); + + } + break; + + case LWS_CALLBACK_BROADCAST: + n = libwebsocket_write(wsi, in, len, LWS_WRITE_TEXT); + if (n < 0) + fprintf(stderr, "mirror write failed\n"); + break; + + case LWS_CALLBACK_RECEIVE: + + if (ringbuffer[ringbuffer_head].payload) + free(ringbuffer[ringbuffer_head].payload); + + ringbuffer[ringbuffer_head].payload = + malloc(LWS_SEND_BUFFER_PRE_PADDING + len + + LWS_SEND_BUFFER_POST_PADDING); + ringbuffer[ringbuffer_head].len = len; + memcpy((char *)ringbuffer[ringbuffer_head].payload + + LWS_SEND_BUFFER_PRE_PADDING, in, len); + if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1)) + ringbuffer_head = 0; + else + ringbuffer_head++; + + if (((ringbuffer_head - pss->ringbuffer_tail) % + MAX_MESSAGE_QUEUE) > (MAX_MESSAGE_QUEUE - 10)) + libwebsocket_rx_flow_control(wsi, 0); + + libwebsocket_callback_on_writable_all_protocol( + libwebsockets_get_protocol(wsi)); + break; + + default: + break; + } + + return 0; +} + + +/* list of supported protocols and callbacks */ + +static struct libwebsocket_protocols protocols[] = { + /* first protocol must always be HTTP handler */ + [PROTOCOL_HTTP] = { + .name = "http-only", + .callback = callback_http, + }, + [PROTOCOL_DUMB_INCREMENT] = { + .name = "dumb-increment-protocol", + .callback = callback_dumb_increment, + .per_session_data_size = + sizeof(struct per_session_data__dumb_increment), + }, + [PROTOCOL_LWS_MIRROR] = { + .name = "lws-mirror-protocol", + .callback = callback_lws_mirror, + .per_session_data_size = + sizeof(struct per_session_data__lws_mirror), + }, + [DEMO_PROTOCOL_COUNT] = { /* end of list */ + .callback = NULL + } +}; + +static struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "port", required_argument, NULL, 'p' }, + { "ssl", no_argument, NULL, 's' }, + { "killmask", no_argument, NULL, 'k' }, + { NULL, 0, 0, 0 } +}; + +int main(int argc, char **argv) +{ + int n = 0; + const char *cert_path = + LOCAL_RESOURCE_PATH"/libwebsockets-test-server.pem"; + const char *key_path = + LOCAL_RESOURCE_PATH"/libwebsockets-test-server.key.pem"; + unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 1024 + + LWS_SEND_BUFFER_POST_PADDING]; + int port = 7681; + int use_ssl = 0; + struct libwebsocket_context *context; + int opts = 0; + unsigned int oldus = 0; + + fprintf(stderr, "libwebsockets test server with external poll()\n" + "(C) Copyright 2010-2011 Andy Green <andy@warmcat.com> " + "licensed under LGPL2.1\n"); + + while (n >= 0) { + n = getopt_long(argc, argv, "khsp:", options, NULL); + if (n < 0) + continue; + switch (n) { + case 's': + use_ssl = 1; + break; + case 'k': + opts = LWS_SERVER_OPTION_DEFEAT_CLIENT_MASK; + break; + case 'p': + port = atoi(optarg); + break; + case 'h': + fprintf(stderr, "Usage: test-server " + "[--port=<p>] [--ssl]\n"); + exit(1); + } + } + + if (!use_ssl) + cert_path = key_path = NULL; + + context = libwebsocket_create_context(port, protocols, cert_path, + key_path, -1, -1, opts); + if (context == NULL) { + fprintf(stderr, "libwebsocket init failed\n"); + return -1; + } + + buf[LWS_SEND_BUFFER_PRE_PADDING] = 'x'; + + /* + * This is an example of an existing application's explicit poll() + * loop that libwebsockets can integrate with. + */ + + while (1) { + struct timeval tv; + + /* + * this represents an existing server's single poll action + * which also includes libwebsocket sockets + */ + + n = poll(pollfds, count_pollfds, 25); + if (n < 0) + goto done; + + if (n) + for (n = 0; n < count_pollfds; n++) + if (pollfds[n].revents) + /* + * returns immediately if the fd does not + * match anything under libwebsockets + * control + */ + libwebsocket_service_fd(context, + &pollfds[n]); + + /* do our broadcast periodically */ + + gettimeofday(&tv, NULL); + + /* + * This broadcasts to all dumb-increment-protocol connections + * at 20Hz. + * + * We're just sending a character 'x', in these examples the + * callbacks send their own per-connection content. + * + * You have to send something with nonzero length to get the + * callback actions delivered. + * + * We take care of pre-and-post padding allocation. + */ + + if (((unsigned int)tv.tv_usec - oldus) > 50000) { + libwebsockets_broadcast( + &protocols[PROTOCOL_DUMB_INCREMENT], + &buf[LWS_SEND_BUFFER_PRE_PADDING], 1); + oldus = tv.tv_usec; + } + } + +done: + libwebsocket_context_destroy(context); + + return 0; +} |