From 2e5de82da2da28ada2103b6c537f6e0d205febce Mon Sep 17 00:00:00 2001 From: Andy Green Date: Sat, 12 Feb 2011 12:07:23 +0000 Subject: [PATCH] introduce-libwebsockets-test-server-extpoll.patch Signed-off-by: Andy Green --- test-server/Makefile.am | 5 +- test-server/Makefile.in | 38 +++- test-server/test-server-extpoll.c | 406 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 445 insertions(+), 4 deletions(-) create mode 100644 test-server/test-server-extpoll.c diff --git a/test-server/Makefile.am b/test-server/Makefile.am index c94d313..a28fe1d 100644 --- a/test-server/Makefile.am +++ b/test-server/Makefile.am @@ -1,12 +1,15 @@ -bin_PROGRAMS=libwebsockets-test-server libwebsockets-test-client +bin_PROGRAMS=libwebsockets-test-server libwebsockets-test-client libwebsockets-test-server-extpoll libwebsockets_test_server_SOURCES=test-server.c libwebsockets_test_server_LDADD=-L../lib -lwebsockets libwebsockets_test_client_SOURCES=test-client.c libwebsockets_test_client_LDADD=-L../lib -lwebsockets +libwebsockets_test_server_extpoll_SOURCES=test-server-extpoll.c +libwebsockets_test_server_extpoll_LDADD=-L../lib -lwebsockets libwebsockets_test_server_CFLAGS:= -Wall -Werror -std=gnu99 -pedantic -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\" libwebsockets_test_client_CFLAGS:= -Wall -Werror -std=gnu99 -pedantic -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\" +libwebsockets_test_server_extpoll_CFLAGS:= -Wall -Werror -std=gnu99 -pedantic -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\" if NOPING else diff --git a/test-server/Makefile.in b/test-server/Makefile.in index d6f0da6..2a9c7dc 100644 --- a/test-server/Makefile.in +++ b/test-server/Makefile.in @@ -35,7 +35,8 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ bin_PROGRAMS = libwebsockets-test-server$(EXEEXT) \ - libwebsockets-test-client$(EXEEXT) $(am__EXEEXT_1) + libwebsockets-test-client$(EXEEXT) \ + libwebsockets-test-server-extpoll$(EXEEXT) $(am__EXEEXT_1) @NOPING_FALSE@am__append_1 = libwebsockets-test-ping subdir = test-server DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in @@ -78,6 +79,14 @@ libwebsockets_test_server_LINK = $(LIBTOOL) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(libwebsockets_test_server_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ $(LDFLAGS) -o $@ +am_libwebsockets_test_server_extpoll_OBJECTS = libwebsockets_test_server_extpoll-test-server-extpoll.$(OBJEXT) +libwebsockets_test_server_extpoll_OBJECTS = \ + $(am_libwebsockets_test_server_extpoll_OBJECTS) +libwebsockets_test_server_extpoll_DEPENDENCIES = +libwebsockets_test_server_extpoll_LINK = $(LIBTOOL) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(libwebsockets_test_server_extpoll_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles @@ -93,10 +102,12 @@ LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ $(LDFLAGS) -o $@ SOURCES = $(libwebsockets_test_client_SOURCES) \ $(libwebsockets_test_ping_SOURCES) \ - $(libwebsockets_test_server_SOURCES) + $(libwebsockets_test_server_SOURCES) \ + $(libwebsockets_test_server_extpoll_SOURCES) DIST_SOURCES = $(libwebsockets_test_client_SOURCES) \ $(am__libwebsockets_test_ping_SOURCES_DIST) \ - $(libwebsockets_test_server_SOURCES) + $(libwebsockets_test_server_SOURCES) \ + $(libwebsockets_test_server_extpoll_SOURCES) ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) @@ -218,8 +229,11 @@ libwebsockets_test_server_SOURCES = test-server.c libwebsockets_test_server_LDADD = -L../lib -lwebsockets libwebsockets_test_client_SOURCES = test-client.c libwebsockets_test_client_LDADD = -L../lib -lwebsockets +libwebsockets_test_server_extpoll_SOURCES = test-server-extpoll.c +libwebsockets_test_server_extpoll_LDADD = -L../lib -lwebsockets libwebsockets_test_server_CFLAGS := -Wall -Werror -std=gnu99 -pedantic -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\" libwebsockets_test_client_CFLAGS := -Wall -Werror -std=gnu99 -pedantic -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\" +libwebsockets_test_server_extpoll_CFLAGS := -Wall -Werror -std=gnu99 -pedantic -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\" @NOPING_FALSE@libwebsockets_test_ping_SOURCES = test-ping.c @NOPING_FALSE@libwebsockets_test_ping_LDADD = -L../lib -lwebsockets @NOPING_FALSE@libwebsockets_test_ping_CFLAGS := -Wall -Werror -std=gnu99 -pedantic -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\" @@ -309,6 +323,9 @@ libwebsockets-test-ping$(EXEEXT): $(libwebsockets_test_ping_OBJECTS) $(libwebsoc libwebsockets-test-server$(EXEEXT): $(libwebsockets_test_server_OBJECTS) $(libwebsockets_test_server_DEPENDENCIES) @rm -f libwebsockets-test-server$(EXEEXT) $(libwebsockets_test_server_LINK) $(libwebsockets_test_server_OBJECTS) $(libwebsockets_test_server_LDADD) $(LIBS) +libwebsockets-test-server-extpoll$(EXEEXT): $(libwebsockets_test_server_extpoll_OBJECTS) $(libwebsockets_test_server_extpoll_DEPENDENCIES) + @rm -f libwebsockets-test-server-extpoll$(EXEEXT) + $(libwebsockets_test_server_extpoll_LINK) $(libwebsockets_test_server_extpoll_OBJECTS) $(libwebsockets_test_server_extpoll_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -319,6 +336,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_test_client-test-client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_test_ping-test-ping.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_test_server-test-server.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_test_server_extpoll-test-server-extpoll.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @@ -383,6 +401,20 @@ libwebsockets_test_server-test-server.obj: test-server.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_server_CFLAGS) $(CFLAGS) -c -o libwebsockets_test_server-test-server.obj `if test -f 'test-server.c'; then $(CYGPATH_W) 'test-server.c'; else $(CYGPATH_W) '$(srcdir)/test-server.c'; fi` +libwebsockets_test_server_extpoll-test-server-extpoll.o: test-server-extpoll.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_server_extpoll_CFLAGS) $(CFLAGS) -MT libwebsockets_test_server_extpoll-test-server-extpoll.o -MD -MP -MF $(DEPDIR)/libwebsockets_test_server_extpoll-test-server-extpoll.Tpo -c -o libwebsockets_test_server_extpoll-test-server-extpoll.o `test -f 'test-server-extpoll.c' || echo '$(srcdir)/'`test-server-extpoll.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libwebsockets_test_server_extpoll-test-server-extpoll.Tpo $(DEPDIR)/libwebsockets_test_server_extpoll-test-server-extpoll.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test-server-extpoll.c' object='libwebsockets_test_server_extpoll-test-server-extpoll.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_server_extpoll_CFLAGS) $(CFLAGS) -c -o libwebsockets_test_server_extpoll-test-server-extpoll.o `test -f 'test-server-extpoll.c' || echo '$(srcdir)/'`test-server-extpoll.c + +libwebsockets_test_server_extpoll-test-server-extpoll.obj: test-server-extpoll.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_server_extpoll_CFLAGS) $(CFLAGS) -MT libwebsockets_test_server_extpoll-test-server-extpoll.obj -MD -MP -MF $(DEPDIR)/libwebsockets_test_server_extpoll-test-server-extpoll.Tpo -c -o libwebsockets_test_server_extpoll-test-server-extpoll.obj `if test -f 'test-server-extpoll.c'; then $(CYGPATH_W) 'test-server-extpoll.c'; else $(CYGPATH_W) '$(srcdir)/test-server-extpoll.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libwebsockets_test_server_extpoll-test-server-extpoll.Tpo $(DEPDIR)/libwebsockets_test_server_extpoll-test-server-extpoll.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test-server-extpoll.c' object='libwebsockets_test_server_extpoll-test-server-extpoll.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_server_extpoll_CFLAGS) $(CFLAGS) -c -o libwebsockets_test_server_extpoll-test-server-extpoll.obj `if test -f 'test-server-extpoll.c'; then $(CYGPATH_W) 'test-server-extpoll.c'; else $(CYGPATH_W) '$(srcdir)/test-server-extpoll.c'; fi` + mostlyclean-libtool: -rm -f *.lo 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 + * + * 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 +#include +#include +#include +#include +#include +#include + +#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 " + "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=

] [--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; +} -- 2.7.4