summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBent Bisballe Nyeng <deva@aasimon.org>2012-02-17 12:23:57 +0100
committerBent Bisballe Nyeng <deva@aasimon.org>2012-02-17 12:23:57 +0100
commite7665f0d794b9ffa4dea69b03bc42db759fd2600 (patch)
tree7f0e4c35ab74a84babe8dd5030075837ff0fec75
parentf1395d05a2feec5366b8dd1b81899a6fd217864e (diff)
Initial import.
-rw-r--r--Makefile.am3
-rwxr-xr-xautogen.sh89
-rw-r--r--configure.in65
-rw-r--r--src/Makefile.am23
-rw-r--r--src/muniad.cc495
5 files changed, 675 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..06d6b0e
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,3 @@
+AUTOMAKE_OPTIONS = gnu
+SUBDIRS = src tools
+DISTDIRS = src tools
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..6e23e5b
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,89 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+# This was lifted from the Gimp, and adapted slightly by
+# Raph Levien, slightly hacked for xine by Daniel Caujolle-Bert.
+
+DIE=0
+
+PROG=anoid
+
+# Check how echo works in this /bin/sh
+case `echo -n` in
+-n) _echo_n= _echo_c='\c';;
+*) _echo_n=-n _echo_c=;;
+esac
+
+
+(autoconf --version) < /dev/null > /dev/null 2>&1 || {
+ echo
+ echo "You must have autoconf installed to compile $PROG."
+ echo "Download the appropriate package for your distribution,"
+ echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/"
+ DIE=1
+}
+
+(libtool --version) < /dev/null > /dev/null 2>&1 || {
+ echo
+ echo "You must have libtool installed to compile $PROG."
+ echo "Get ftp://ftp.gnu.org/pub/gnu/libtool-1.4.tar.gz"
+ echo "(or a newer version if it is available)"
+ DIE=1
+}
+
+(automake --version) < /dev/null > /dev/null 2>&1 || {
+ echo
+ echo "You must have automake installed to compile $PROG."
+ echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz"
+ echo "(or a newer version if it is available)"
+ DIE=1
+}
+
+(aclocal --version) < /dev/null > /dev/null 2>&1 || {
+ echo
+ echo "**Error**: Missing aclocal. The version of automake"
+ echo "installed doesn't appear recent enough."
+ echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz"
+ echo "(or a newer version if it is available)"
+ DIE=1
+}
+
+if [ "$DIE" -eq 1 ]; then
+ exit 1
+fi
+
+# Create testfiles
+TESTDIRS="src"
+for d in $TESTDIRS
+do
+ if [ ! -f $d/Makefile.am.test ]
+ then
+ touch $d/Makefile.am.test
+ fi
+done
+
+aclocalinclude="$ACLOCAL_FLAGS"; \
+(echo $_echo_n " + Running aclocal: $_echo_c"; \
+ aclocal $aclocalinclude; \
+ echo "done.") && \
+(echo $_echo_n " + Running libtoolize: $_echo_c"; \
+ libtoolize --force --copy >/dev/null 2>&1; \
+ echo "done.") && \
+(echo $_echo_n " + Running autoheader: $_echo_c"; \
+ autoheader; \
+ echo "done.") && \
+(echo $_echo_n " + Running automake: $_echo_c"; \
+ automake --gnu --add-missing --copy; \
+ echo "done.") && \
+(echo $_echo_n " + Running autoconf: $_echo_c"; \
+ autoconf; \
+ echo "done.")
+
+rm -f config.cache
+
+for d in $TESTDIRS
+do
+ if [ -f $d/Makefile.am.test ]
+ then
+ rm $d/Makefile.am.test
+ fi
+done
diff --git a/configure.in b/configure.in
new file mode 100644
index 0000000..4a9e870
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,65 @@
+# Filename: configure.in
+
+AC_INIT(src/muniad.cc)
+
+VERSION="0.0.1"
+AM_INIT_AUTOMAKE( munia, $VERSION )
+
+dnl ======================
+dnl Compile with debug options
+dnl ======================
+AC_ARG_WITH(debug,
+ [ --with-debug build with debug support (default=no)],
+ [],
+ [with_debug=no])
+if test x$with_debug == xyes; then
+ AC_MSG_WARN([*** Building with debug support!])
+ AC_DEFINE_UNQUOTED(WITH_DEBUG, , [The project is configured to use debug output])
+ CXXFLAGS="$CXXFLAGS -D_FORTIFY_SOURCE=2 -fstack-protector -Wall -Werror -g -O0"
+fi
+
+dnl ======================
+dnl Init pkg-config
+dnl ======================
+PKG_PROG_PKG_CONFIG(0.23)
+
+AC_PROG_CXX
+
+AM_CONFIG_HEADER(config.h)
+AC_STDC_HEADERS
+
+dnl ======================
+dnl Check for getopt
+dnl ======================
+AC_HAVE_HEADERS(getopt.h)
+
+dnl ======================
+dnl Check for libwebsockets library
+dnl ======================
+tmp_CXXFLAGS="$CXXFLAGS"
+tmp_CPPFLAGS="$CPPFLAGS"
+tmp_CFLAGS="$CFLAGS"
+tmp_LDFLAGS="$LDFLAGS"
+tmp_LIBS="$LIBS"
+CXXFLAGS=""
+CPPFLAGS=""
+CFLAGS=""
+LDFLAGS=""
+LIBS=""
+AC_CHECK_HEADER(libwebsockets.h, , AC_MSG_ERROR([*** libwebsockets header file not found!]))
+AC_CHECK_LIB(websockets, libwebsocket_create_context, , AC_MSG_ERROR([*** libwebsockets library not found!]))
+LIBWEBSOCKETS_CFLAGS="$CXXFLAGS $CPPFLAGS $CFLAGS"
+LIBWEBSOCKETS_LIBS="$LDFLAGS $LIBS"
+CXXFLAGS="$tmp_CXXFLAGS"
+CPPFLAGS="$tmp_CPPFLAGS"
+CFLAGS="$tmp_CFLAGS"
+LDFLAGS="$tmp_LDFLAGS"
+LIBS="$tmp_LIBS"
+AC_SUBST(LIBWEBSOCKETS_CFLAGS)
+AC_SUBST(LIBWEBSOCKETS_LIBS)
+
+AC_OUTPUT(
+ Makefile
+ src/Makefile
+ tools/Makefile)
+
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..ec4ada4
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,23 @@
+SUBDIRS =
+
+bin_PROGRAMS = muniad
+
+muniad_LDADD = $(LIBWEBSOCKETS_LIBS)
+
+muniad_CXXFLAGS = $(LIBWEBSOCKETS_CFLAGS)
+
+muniad_SOURCES = \
+ muniad.cc
+
+EXTRA_DIST =
+
+################
+# Test Section #
+################
+
+TEST_SOURCE_DEPS = ${muniad_SOURCES} ${EXTRA_DIST}
+TEST_SCRIPT_DIR = $(top_srcdir)/tools
+
+include ${TEST_SCRIPT_DIR}/Makefile.am.test
+
+include Makefile.am.test \ No newline at end of file
diff --git a/src/muniad.cc b/src/muniad.cc
new file mode 100644
index 0000000..f5d4d6a
--- /dev/null
+++ b/src/muniad.cc
@@ -0,0 +1,495 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set et sw=2 ts=2: */
+/***************************************************************************
+ * muniad.cc
+ *
+ * Thu Feb 16 15:03:28 CET 2012
+ * Copyright 2012 Bent Bisballe Nyeng
+ * deva@aasimon.org
+ ****************************************************************************/
+
+/*
+ * This file is part of Munia.
+ *
+ * Munia is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Munia 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Munia; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <libwebsockets.h>
+
+#define LWS_NO_FORK
+
+static int close_testing;
+
+/*
+ * This demo server shows how to use libwebsockets for one or more
+ * websocket protocols in the same server
+ *
+ * It defines the following websocket protocols:
+ * lws-mirror-protocol: copies any received packet to every connection also
+ * using this protocol, including the sender
+ */
+
+enum demo_protocols {
+ /* always first */
+ PROTOCOL_HTTP = 0,
+ PROTOCOL_LWS_MIRROR,
+ /* always last */
+ DEMO_PROTOCOL_COUNT
+};
+
+#define LOCAL_RESOURCE_PATH "."
+
+/* this protocol server (always the first one) just knows how to do HTTP */
+
+static int callback_http(struct libwebsocket_context * context,
+ struct libwebsocket *wsi,
+ enum libwebsocket_callback_reasons reason, void *user,
+ void *in, size_t len)
+{
+ char client_name[128];
+ char client_ip[128];
+
+ switch (reason) {
+ case LWS_CALLBACK_HTTP:
+ fprintf(stderr, "serving HTTP URI %s\n", (char *)in);
+
+ if(in && strcmp((const char *)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;
+
+ /*
+ * callback for confirming to continue with client IP appear in
+ * protocol 0 callback since no websocket protocol has been agreed
+ * yet. You can just ignore this if you won't filter on client IP
+ * since the default uhandled callback return is 0 meaning let the
+ * connection continue.
+ */
+
+ case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
+ libwebsockets_get_peer_addresses((int)(long)user, client_name, sizeof(client_name), client_ip, sizeof(client_ip));
+ fprintf(stderr, "Received network connect from %s (%s)\n", client_name, client_ip);
+ /* if we returned non-zero from here, we kill the connection */
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+
+/*
+ * this is just an example of parsing handshake headers, you don't need this
+ * in your code unless you will filter allowing connections by the header
+ * content
+ */
+
+static void dump_handshake_info(struct lws_tokens *lwst)
+{
+ int n;
+ static const char *token_names[WSI_TOKEN_COUNT] = {
+ /*[WSI_TOKEN_GET_URI] =*/ "GET URI",
+ /*[WSI_TOKEN_HOST] =*/ "Host",
+ /*[WSI_TOKEN_CONNECTION] =*/ "Connection",
+ /*[WSI_TOKEN_KEY1] =*/ "key 1",
+ /*[WSI_TOKEN_KEY2] =*/ "key 2",
+ /*[WSI_TOKEN_PROTOCOL] =*/ "Protocol",
+ /*[WSI_TOKEN_UPGRADE] =*/ "Upgrade",
+ /*[WSI_TOKEN_ORIGIN] =*/ "Origin",
+ /*[WSI_TOKEN_DRAFT] =*/ "Draft",
+ /*[WSI_TOKEN_CHALLENGE] =*/ "Challenge",
+
+ /* new for 04 */
+ /*[WSI_TOKEN_KEY] =*/ "Key",
+ /*[WSI_TOKEN_VERSION] =*/ "Version",
+ /*[WSI_TOKEN_SWORIGIN] =*/ "Sworigin",
+
+ /* new for 05 */
+ /*[WSI_TOKEN_EXTENSIONS] =*/ "Extensions",
+
+ /* client receives these */
+ /*[WSI_TOKEN_ACCEPT] =*/ "Accept",
+ /*[WSI_TOKEN_NONCE] =*/ "Nonce",
+ /*[WSI_TOKEN_HTTP] =*/ "Http",
+ /*[WSI_TOKEN_MUXURL] =*/ "MuxURL",
+ };
+
+ for (n = 0; n < WSI_TOKEN_COUNT; n++) {
+ if (lwst[n].token == NULL) continue;
+ fprintf(stderr, " %s = %s\n", token_names[n], lwst[n].token);
+ }
+}
+
+
+/* lws-mirror_protocol */
+
+#define MAX_MESSAGE_QUEUE 1024
+
+struct per_session_data__lws_mirror {
+ struct libwebsocket *wsi;
+ int ringbuffer_tail;
+};
+
+struct a_message {
+ void *payload;
+ size_t len;
+};
+
+#include <string>
+#include <map>
+
+typedef int boxid_t;
+class Box {
+public:
+ Box(std::string data) {}
+ Box() {}
+
+ boxid_t id;
+ std::string title;
+ std::string description;
+ int x, y;
+};
+
+std::map<boxid_t, Box> boxes;
+
+/*
+Protocol:
+
+Server -> client:
+ update [id] [title] [description];
+ move [id] [x] [y];
+ add [title] [description];
+
+Client -> server:
+ update [id] [title] [description];
+ move [id] [x] [y];
+ add [id] [title] [description] [x] [y];
+
+title and description are " encapsulated utf-8 string with " escaped with a backslash.
+x and y are integers as strings
+id are an integer as a string
+ */
+
+static struct a_message ringbuffer[MAX_MESSAGE_QUEUE];
+static int ringbuffer_head;
+
+static int callback_lws_mirror(struct libwebsocket_context * context,
+ struct libwebsocket *wsi,
+ enum libwebsocket_callback_reasons reason,
+ void *user, void *in, size_t len)
+{
+ int n;
+ struct per_session_data__lws_mirror *pss = (struct per_session_data__lws_mirror *)user;
+
+ switch (reason) {
+
+ case LWS_CALLBACK_ESTABLISHED:
+ {
+ fprintf(stderr, "callback_lws_mirror: LWS_CALLBACK_ESTABLISHED\n");
+ pss->ringbuffer_tail = ringbuffer_head;
+ pss->wsi = wsi;
+ }
+ break;
+
+ case LWS_CALLBACK_SERVER_WRITEABLE:
+ {
+ printf("LWS_CALLBACK_SERVER_WRITEABLE\n");
+ if(close_testing) break;
+
+ 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(context, wsi);
+ }
+ }
+ break;
+
+ case LWS_CALLBACK_BROADCAST:
+ printf("LWS_CALLBACK_BROADCAST\n");
+ n = libwebsocket_write(wsi, (unsigned char*)in, len, LWS_WRITE_TEXT);
+ if (n < 0) fprintf(stderr, "mirror write failed\n");
+ break;
+
+ case LWS_CALLBACK_RECEIVE:
+ {
+ printf("LWS_CALLBACK_RECEIVE\n");
+
+ printf("%s\n", (char*)in);
+ std::string data;
+ data.append((char*)in, len);
+
+ std::string cmd = data.substr(0, data.find(' '));
+ printf("Cmd: %s\n", cmd.c_str());
+
+ if(cmd == "add") {
+ printf("Add\n");
+ } else if(cmd == "move") {
+ printf("Move\n");
+ } else if(cmd == "update") {
+ printf("Update\n");
+ } else { // unknown command
+
+ }
+
+ struct a_message &msg = ringbuffer[ringbuffer_head];
+ if(msg.payload) {
+ free(msg.payload);
+ msg.payload = NULL;
+ }
+
+ msg.payload = malloc(LWS_SEND_BUFFER_PRE_PADDING + len + LWS_SEND_BUFFER_POST_PADDING);
+ msg.len = len;
+ memcpy((char *)msg.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;
+ /*
+ * this just demonstrates how to use the protocol filter. If you won't
+ * study and reject connections based on header content, you don't need
+ * to handle this callback
+ */
+
+ case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
+ printf("LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION\n");
+ dump_handshake_info((struct lws_tokens *)(long)user);
+ /* you could return non-zero here and kill the connection */
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+
+/* list of supported protocols and callbacks */
+
+static struct libwebsocket_protocols protocols[] = {
+ /* first protocol must always be HTTP handler */
+
+ {
+ "http-only", /* name */
+ callback_http, /* callback */
+ 0 /* per_session_data_size */
+ },
+ {
+ "lws-mirror-protocol",
+ callback_lws_mirror,
+ sizeof(struct per_session_data__lws_mirror)
+ },
+ {
+ NULL, NULL, 0 /* End of list */
+ }
+};
+
+static struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "port", required_argument, NULL, 'p' },
+ { "ssl", no_argument, NULL, 's' },
+ { "killmask", no_argument, NULL, 'k' },
+ { "interface", required_argument, NULL, 'i' },
+ { "closetest", no_argument, NULL, 'c' },
+ { 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;
+ char interface_name[128] = "";
+ const char * interface = NULL;
+#ifdef LWS_NO_FORK
+ unsigned int oldus = 0;
+#endif
+
+ fprintf(stderr, "libwebsockets test server\n"
+ "(C) Copyright 2010-2011 Andy Green <andy@warmcat.com> "
+ "licensed under LGPL2.1\n");
+
+ while (n >= 0) {
+ n = getopt_long(argc, argv, "ci: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 'i':
+ strncpy(interface_name, optarg, sizeof interface_name);
+ interface_name[(sizeof interface_name) - 1] = '\0';
+ interface = interface_name;
+ break;
+ case 'c':
+ close_testing = 1;
+ fprintf(stderr, " Close testing mode -- closes on "
+ "client after 50 dumb increments"
+ "and suppresses lws_mirror spam\n");
+ 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, interface, protocols,
+ libwebsocket_internal_extensions,
+ 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';
+
+#ifdef LWS_NO_FORK
+
+ /*
+ * This example shows how to work with no forked service loop
+ */
+
+ fprintf(stderr, " Using no-fork service loop\n");
+
+ while (1) {
+ /*
+ * This example server does not fork or create a thread for
+ * websocket service, it all runs in this single loop. So,
+ * we have to give the websockets an opportunity to service
+ * "manually".
+ *
+ * If no socket is needing service, the call below returns
+ * immediately and quickly.
+ */
+
+ libwebsocket_service(context, 50);
+ }
+
+#else
+
+ /*
+ * This example shows how to work with the forked websocket service loop
+ */
+
+ fprintf(stderr, " Using forked service loop\n");
+
+ /*
+ * This forks the websocket service action into a subprocess so we
+ * don't have to take care about it.
+ */
+
+ n = libwebsockets_fork_service_loop(context);
+ if (n < 0) {
+ fprintf(stderr, "Unable to fork service loop %d\n", n);
+ return 1;
+ }
+
+ while (1) {
+
+ usleep(50000);
+
+ /*
+ * 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.
+ */
+
+ libwebsockets_broadcast(&protocols[PROTOCOL_DUMB_INCREMENT],
+ &buf[LWS_SEND_BUFFER_PRE_PADDING], 1);
+ }
+
+#endif
+
+ libwebsocket_context_destroy(context);
+
+ return 0;
+}
+
+#ifdef TEST_MUNIAD
+//Additional dependency files
+//deps:
+//Required cflags (autoconf vars may be used)
+//cflags:
+//Required link options (autoconf vars may be used)
+//libs:
+#include "test.h"
+
+TEST_BEGIN;
+
+// TODO: Put some testcode here (see test.h for usable macros).
+TEST_TRUE(false, "No tests yet!");
+
+TEST_END;
+
+#endif/*TEST_MUNIAD*/