summaryrefslogtreecommitdiff
path: root/src/muniad.cc
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 /src/muniad.cc
parentf1395d05a2feec5366b8dd1b81899a6fd217864e (diff)
Initial import.
Diffstat (limited to 'src/muniad.cc')
-rw-r--r--src/muniad.cc495
1 files changed, 495 insertions, 0 deletions
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*/