From e7665f0d794b9ffa4dea69b03bc42db759fd2600 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Fri, 17 Feb 2012 12:23:57 +0100 Subject: Initial import. --- src/Makefile.am | 23 +++ src/muniad.cc | 495 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 518 insertions(+) create mode 100644 src/Makefile.am create mode 100644 src/muniad.cc (limited to 'src') 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 +#include +#include +#include +#include +#include + +#include + +#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 +#include + +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 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 " + "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=

] [--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*/ -- cgit v1.2.3