summaryrefslogtreecommitdiffstats
path: root/contrib/src/evws
diff options
context:
space:
mode:
authorStefan Radomski <radomski@tk.informatik.tu-darmstadt.de>2013-11-16 01:10:51 (GMT)
committerStefan Radomski <radomski@tk.informatik.tu-darmstadt.de>2013-11-16 01:10:51 (GMT)
commit22ddb37efb48c484e022fef5516491ad69608397 (patch)
tree4604b39065d8f2ce8cddb6a0a324dc8df2d79ebe /contrib/src/evws
parent5b29128e4db73021b585dd62e301685a7a6b77b9 (diff)
downloaduscxml-22ddb37efb48c484e022fef5516491ad69608397.zip
uscxml-22ddb37efb48c484e022fef5516491ad69608397.tar.gz
uscxml-22ddb37efb48c484e022fef5516491ad69608397.tar.bz2
Trying to get rid of subproject
Diffstat (limited to 'contrib/src/evws')
-rw-r--r--contrib/src/evws/evws.c490
-rw-r--r--contrib/src/evws/evws.h197
2 files changed, 687 insertions, 0 deletions
diff --git a/contrib/src/evws/evws.c b/contrib/src/evws/evws.c
new file mode 100644
index 0000000..35adaa1
--- /dev/null
+++ b/contrib/src/evws/evws.c
@@ -0,0 +1,490 @@
+/**
+ * @file
+ * @author Pawel Zubrycki <paw.zubr@gmail.com>
+ * @author 2013 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
+ * @copyright Simplified BSD
+ *
+ * @cond
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the FreeBSD license as published by the FreeBSD
+ * project.
+ *
+ * This program 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.
+ *
+ * You should have received a copy of the FreeBSD license along with this
+ * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
+ * @endcond
+ */
+
+#include "evws.h"
+#include <stdlib.h>
+#include <string.h>
+#include <event2/buffer.h>
+#include <event2/bufferevent.h>
+#include "uscxml/util/SHA1.h"
+#include "uscxml/util/Base64.h"
+
+static int evws_parse_first_line(struct evws_connection *conn, char *line);
+static int evws_parse_header_line(char *line, char **skey, char **svalue);
+
+// Callbacks
+static void cb_accept(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *address, int socklen, void *ctx);
+static void cb_read_handshake(struct bufferevent *bev, void *arg);
+static void cb_read_frame(struct bufferevent *bev, void *arg);
+
+#define WS_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+
+struct evws *evws_new(struct event_base *base) {
+ struct evws *ret_obj = (struct evws*)calloc(1, sizeof(struct evws));
+ ret_obj->base = base;
+ ret_obj->listener = NULL;
+
+ TAILQ_INIT(&ret_obj->connections);
+ TAILQ_INIT(&ret_obj->callbacks);
+
+ return ret_obj;
+}
+
+void evws_free(struct evws *ptr) {
+ // Tu wiecej czyszczenia
+ free(ptr);
+}
+
+evutil_socket_t evws_bind_socket(struct evws * ws, unsigned short port) {
+ struct sockaddr_in sin;
+
+ // Creating serverside socket
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(0);
+ sin.sin_port = htons(port);
+
+ if(!(ws->listener = evconnlistener_new_bind(ws->base, cb_accept, ws, LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE|LEV_OPT_THREADSAFE, -1, (struct sockaddr*)&sin, sizeof(sin)))) {
+ return 0;
+ }
+ return evconnlistener_get_fd(ws->listener);
+}
+
+int evws_set_cb(struct evws * ws, const char * uri, cb_frame_type message_cb, cb_type connect_cb, void * arg) {
+ struct evws_cb *ws_cb;
+
+ TAILQ_FOREACH(ws_cb, &ws->callbacks, next) {
+ if (strcmp(ws_cb->uri, uri) == 0)
+ return (-1);
+ }
+
+ if((ws_cb = (struct evws_cb*)calloc(1, sizeof(struct evws_cb))) == NULL) {
+ return (-2);
+ }
+
+ ws_cb->uri = (char*)strdup(uri);
+ ws_cb->msg_cb = message_cb;
+ ws_cb->conn_cb = connect_cb;
+ ws_cb->cb_arg = arg;
+
+ TAILQ_INSERT_TAIL(&ws->callbacks, ws_cb, next);
+
+ return (0);
+}
+
+cb_frame_type evws_set_gencb(struct evws *ws, cb_frame_type cb, void * arg) {
+ cb_frame_type old_cb = ws->gencb;
+ ws->gencb = cb;
+ ws->gencb_arg = arg;
+ return old_cb;
+}
+
+// Broadcast data to all buffers associated with pattern
+void evws_broadcast(struct evws *ws, const char *uri, enum evws_opcode opcode, const char *data, uint64_t length) {
+ struct evws_connection *ws_connection;
+ TAILQ_FOREACH(ws_connection, &ws->connections, next) {
+ if (strcmp(ws_connection->uri, uri) == 0)
+ evws_send_data(ws_connection, opcode, data, length);
+ }
+}
+
+// Error callback
+static void cb_error(struct bufferevent *bev, short what, void *ctx) {
+ struct evws_connection *conn = ctx;
+ TAILQ_REMOVE(&(conn->ws->connections), conn, next);
+ evws_connection_free(conn);
+}
+
+//Callback to accept
+static void cb_accept(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *address, int socklen, void *arg) {
+ struct evws *ws = arg;
+ struct evws_connection *ws_conn = evws_connection_new(ws, fd);
+
+ bufferevent_setcb(ws_conn->bufev, cb_read_handshake, NULL, cb_error, ws_conn);
+ bufferevent_enable(ws_conn->bufev, EV_READ);
+}
+
+int evws_parse_first_line(struct evws_connection *conn, char *line) {
+ char *method;
+ char *uri;
+ char *version;
+
+ /* Parse the request line */
+ method = strsep(&line, " ");
+ if (line == NULL)
+ return (-1);
+ uri = strsep(&line, " ");
+ if (line == NULL)
+ return (-1);
+ version = strsep(&line, " ");
+ if (line != NULL)
+ return (-1);
+
+ if ((conn->uri = strdup(uri)) == NULL) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+// Callback to read handshake
+void cb_read_handshake(struct bufferevent *bev, void *arg) {
+ struct evws_connection *ws_conn = arg;
+ char *line, *skey, *svalue;
+ struct evbuffer *buffer = bufferevent_get_input(bev);
+ size_t line_length;
+ char *key = NULL;
+ char *host = NULL;
+ char *origin = NULL;
+
+ switch(ws_conn->state) {
+ case 0:
+ line = evbuffer_readln(buffer, &line_length, EVBUFFER_EOL_CRLF);
+ evws_parse_first_line(ws_conn, line);
+ ws_conn->state = EVWS_FIRSTLINE_READ;
+ free(line);
+ case EVWS_FIRSTLINE_READ:
+ while ((line = evbuffer_readln(buffer, &line_length, EVBUFFER_EOL_CRLF)) != NULL) {
+ if (*line == '\0') { /* Last header - Done */
+ free(line);
+ ws_conn->state = EVWS_HEADER_READ;
+ break;
+ }
+ evws_parse_header_line(line, &skey, &svalue);
+ if(strcmp(skey, "Sec-WebSocket-Key") == 0) {
+ key = strdup(svalue);
+ } else if(strcmp(skey, "Host") == 0) {
+ host = strdup(svalue);
+ } else if(strcmp(skey, "Origin") == 0) {
+ origin = strdup(svalue);
+ }
+ struct evws_header *header = evws_header_new(skey, svalue);
+ TAILQ_INSERT_TAIL(&ws_conn->headers, header, next);
+ free(line);
+ }
+ default:
+ break;
+ };
+
+ // -- SHA1
+
+ SHA1Context sha1;
+ SHA1Reset(&sha1);
+ SHA1Input(&sha1, (const unsigned char*)key, 24);
+ SHA1Input(&sha1, (const unsigned char*)WS_GUID, 36);
+ SHA1Result(&sha1);
+
+ char chksumSha1[21];
+ int i;
+ for (i = 0; i < 5; i++) {
+ chksumSha1[i * 4 + 0] = (sha1.Message_Digest[i] >> 24) & 0xff;
+ chksumSha1[i * 4 + 1] = (sha1.Message_Digest[i] >> 16) & 0xff;
+ chksumSha1[i * 4 + 2] = (sha1.Message_Digest[i] >> 8) & 0xff;
+ chksumSha1[i * 4 + 3] = (sha1.Message_Digest[i] >> 0) & 0xff;
+ }
+// printf("%s\n", chksumSha1);
+
+ // -- BASE64
+
+ int md5End = 0;
+ char chksumBase64[200];
+ base64_encodestate* base64Ctx = malloc(sizeof(base64_encodestate));
+ base64_init_encodestate(base64Ctx);
+ md5End += base64_encode_block(chksumSha1, 20, chksumBase64, base64Ctx);
+ md5End += base64_encode_blockend(&chksumBase64[md5End], base64Ctx);
+ // blockend writes unneccessary \n
+ chksumBase64[md5End - 1] = 0;
+
+ free(base64Ctx);
+ free(key);
+ free(host);
+ free(origin);
+ evbuffer_add_printf(bufferevent_get_output(ws_conn->bufev),
+ "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
+ "Upgrade: WebSocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Accept: %s\r\n"
+ "Server: uSCXML\r\n"
+ "Access-Control-Allow-Origin: *\r\n"
+ "Access-Control-Allow-Credentials: true\r\n"
+ "Access-Control-Allow-Headers: content-type\r\n"
+ "Access-Control-Allow-Headers: authorization\r\n"
+ "Access-Control-Allow-Headers: x-websocket-extensions\r\n"
+ "Access-Control-Allow-Headers: x-websocket-version\r\n"
+ "Access-Control-Allow-Headers: x-websocket-protocol\r\n"
+ "\r\n",
+ chksumBase64
+ );
+ bufferevent_setcb(ws_conn->bufev, cb_read_frame, NULL, cb_error, ws_conn);
+
+ TAILQ_INSERT_TAIL(&(ws_conn->ws->connections), ws_conn, next);
+ {
+ struct evws_cb *ws_cb;
+ TAILQ_FOREACH(ws_cb, &ws_conn->ws->callbacks, next) {
+ if (strcmp(ws_cb->uri, ws_conn->uri) == 0) {
+ if(ws_cb->conn_cb != NULL)
+ ws_cb->conn_cb(ws_conn, NULL, 0, ws_cb->cb_arg);
+ return;
+ }
+ }
+ }
+}
+
+int evws_parse_header_line(char *line, char **skey, char **svalue)
+{
+ *svalue = line;
+ *skey = strsep(svalue, ":");
+ if (*svalue == NULL)
+ return -1;
+
+ *svalue += strspn(*svalue, " ");
+
+ return (0);
+}
+
+// Callback to read sent data
+void cb_read_frame(struct bufferevent *bev, void *arg)
+{
+ struct evws_connection *conn = arg;
+ struct evws *ws = conn->ws;
+ char readbuf[1024];
+ int size = 0;
+ struct evbuffer *buffer = bufferevent_get_input(bev);
+ while ((size = evbuffer_remove(buffer, readbuf, sizeof(readbuf))) > 0) {
+ char* dataPtr = readbuf;
+
+NEXT_FRAME:
+ if (conn->frame == NULL) {
+ // data starts with the first byte of a new frame
+ conn->frame = evws_frame_new();
+ conn->frame->fin = (dataPtr[0] >> 7) & 1;
+ conn->frame->rsv = (dataPtr[0] >> 4) & 7;
+ conn->frame->opcode = dataPtr[0] & 0xfu;
+ conn->frame->state = EVWS_OPCODE_READ;
+ dataPtr++;
+ }
+
+ if (size - (dataPtr - readbuf) == 0)
+ continue;
+
+ if (conn->frame->state == EVWS_OPCODE_READ) { // we already read the first byte
+ conn->frame->has_mask = (dataPtr[0] >> 7) & 1;
+ if (conn->frame->has_mask)
+ conn->frame->mask_bytes = 4;
+ conn->frame->size = dataPtr[0] & 0x7fu;
+ dataPtr++;
+
+ if(conn->frame->size == 126) {
+ conn->frame->payload_bytes = 2;
+ conn->frame->size = 0;
+ } else if(conn->frame->size == 127) {
+ conn->frame->payload_bytes = 8;
+ conn->frame->size = 0;
+ } else {
+ conn->frame->payload_bytes = 0;
+ }
+ conn->frame->state = EVWS_PAYLOAD_LENGTH_LENGTH_READ;
+ }
+
+ if (size - (dataPtr - readbuf) == 0)
+ continue;
+
+ if (conn->frame->state == EVWS_PAYLOAD_LENGTH_LENGTH_READ) { // we already read the first byte
+
+ while(conn->frame->payload_bytes > 0 && // need to add more bytes to the payload length
+ (size - (dataPtr - readbuf) > 0)) { // and bytes still available
+ // length is in MSB network order - shift to left and add
+ conn->frame->size = (conn->frame->size << 8) + dataPtr[0];
+ conn->frame->payload_bytes--;
+ dataPtr++;
+ }
+ if (conn->frame->payload_bytes == 0) {
+ conn->frame->state = EVWS_PAYLOAD_LENGTH_READ;
+ if (!conn->frame->has_mask)
+ conn->frame->state = EVWS_MASKING_KEY_READ;
+ }
+ conn->frame->data = (char*)malloc(conn->frame->size);
+ }
+
+ if (size - (dataPtr - readbuf) == 0)
+ continue;
+
+ if (conn->frame->state == EVWS_PAYLOAD_LENGTH_READ && // we already read the complete payload
+ conn->frame->has_mask) {
+ while (conn->frame->mask_bytes > 0 && // bytes for the frame mask
+ (size - (dataPtr - readbuf) > 0)) { // still some available
+ conn->frame->mask[4 - conn->frame->mask_bytes] = dataPtr[0];
+ conn->frame->mask_bytes--;
+ dataPtr++;
+ if (conn->frame->mask_bytes == 0)
+ conn->frame->state = EVWS_MASKING_KEY_READ;
+ }
+ }
+
+ if (size - (dataPtr - readbuf) == 0)
+ continue;
+
+ if (conn->frame->state == EVWS_MASKING_KEY_READ) { // we read all of the header
+ size_t remaining = size - (dataPtr - readbuf);
+ remaining = (remaining > conn->frame->size - conn->frame->payload_read ? conn->frame->size - conn->frame->payload_read : remaining);
+
+ if (conn->frame->has_mask) {
+ int i;
+ for (i = 0; i < remaining; i++) {
+ dataPtr[i] = dataPtr[i] ^ conn->frame->mask[conn->frame->mask_index];
+ conn->frame->mask_index++;
+ conn->frame->mask_index = conn->frame->mask_index % 4;
+ }
+ }
+ memcpy(conn->frame->data + conn->frame->payload_read, dataPtr, remaining);
+ conn->frame->payload_read += remaining;
+ dataPtr += remaining;
+ }
+
+ if (conn->frame->payload_read == conn->frame->size) {
+ // done reading this frame - invoke callbacks
+ struct evws_cb *ws_cb;
+ TAILQ_FOREACH(ws_cb, &ws->callbacks, next) {
+ if (strcmp(ws_cb->uri, conn->uri) == 0) {
+ if(ws_cb->msg_cb != NULL)
+ ws_cb->msg_cb(conn, conn->frame, ws_cb->cb_arg);
+ continue;
+ }
+ }
+ ws->gencb(conn, conn->frame, ws->gencb_arg);
+
+ evws_frame_free(conn->frame);
+ conn->frame = NULL;
+ if (size - (dataPtr - readbuf) != 0) // there is more data in this packet
+ goto NEXT_FRAME;
+ }
+ }
+}
+
+void evws_send_data(struct evws_connection *conn, enum evws_opcode opcode, const char *data, uint64_t length) {
+ char *sendbuf = malloc(length + 10); // payload + header without masking key
+ if(sendbuf == NULL)
+ return;
+
+ char* writePtr = sendbuf;
+
+ writePtr[0] = (1 << 7); // set fin header and zero out RSV
+ writePtr[0] += opcode; // set opcode
+ writePtr++;
+ writePtr[0] = (0 << 7); // we don't mask replies
+
+ if (length < 126) {
+ writePtr[0] += (uint8_t)length;
+ writePtr++;
+ } else if(length < (1 << 16)) {
+ writePtr[0] = 126;
+ writePtr[1] = (((uint16_t)length) >> 8) & 0xff;
+ writePtr[2] = ((uint16_t)length) & 0xff;
+ writePtr += 3;
+ } else if(length < (1ull << 63)) {
+ writePtr[0] = 126;
+ // integer division and bitmask ought to be endian agnostic
+ writePtr[1] = (length / 0x0100000000000000) & 0xff;
+ writePtr[2] = (length / 0x0001000000000000) & 0xff;
+ writePtr[3] = (length / 0x0000010000000000) & 0xff;
+ writePtr[4] = (length / 0x0000000100000000) & 0xff;
+ writePtr[5] = (length / 0x0000000001000000) & 0xff;
+ writePtr[6] = (length / 0x0000000000010000) & 0xff;
+ writePtr[7] = (length / 0x0000000000000100) & 0xff;
+ writePtr[8] = (length / 0x0000000000000001) & 0xff;
+ writePtr += 9;
+ }
+
+ memcpy(writePtr, data, length);
+ writePtr += length;
+
+ struct evbuffer *buffer = bufferevent_get_output(conn->bufev);
+ evbuffer_add(buffer, sendbuf, writePtr - sendbuf);
+ free(sendbuf);
+}
+
+
+// --- new and free pairs ---
+
+struct evws_connection* evws_connection_new(struct evws *ws, evutil_socket_t fd) {
+ struct evws_connection* conn = calloc(1, sizeof(struct evws_connection));
+ conn->ws = ws;
+ conn->fd = fd;
+ conn->uri = NULL;
+
+ conn->bufev = bufferevent_socket_new(ws->base, fd, BEV_OPT_CLOSE_ON_FREE);
+ conn->state = 0;
+ conn->frame = NULL;
+ TAILQ_INIT(&conn->headers);
+ return conn;
+}
+
+void evws_connection_free(struct evws_connection *conn) {
+ struct evws_header *header;
+ bufferevent_free(conn->bufev);
+ if(conn->uri != NULL)
+ free(conn->uri);
+
+ TAILQ_FOREACH(header, &conn->headers, next) {
+ evws_header_free(header);
+ }
+ free(conn);
+}
+
+struct evws_frame *evws_frame_new() {
+ struct evws_frame *frame = calloc(1, sizeof(struct evws_frame));
+ return frame;
+}
+
+void evws_frame_free(struct evws_frame *frame) {
+ if (frame->data)
+ free(frame->data);
+ free(frame);
+}
+
+struct evws_header *evws_header_new(char *key, char *value)
+{
+ struct evws_header *head = calloc(1, sizeof(struct evws_header));
+ head->key = strdup(key);
+ head->value = strdup(value);
+ return head;
+}
+
+void evws_header_free(struct evws_header *header) {
+ if(header->key != NULL)
+ free(header->key);
+ // @Note: segfault when freeing value, some strange value
+ if(header->value != NULL)
+ free(header->value);
+ free(header);
+}
+
+char *evws_find_header(const struct wsheadersq *q, const char *key) {
+ struct evws_header *hdr;
+ char * ret = NULL;
+ TAILQ_FOREACH(hdr, q, next) {
+ if(strcmp(hdr->key, key) == 0) {
+ ret = hdr->value;
+ break;
+ }
+ }
+ return ret;
+}
+
diff --git a/contrib/src/evws/evws.h b/contrib/src/evws/evws.h
new file mode 100644
index 0000000..7967a64
--- /dev/null
+++ b/contrib/src/evws/evws.h
@@ -0,0 +1,197 @@
+/**
+ * @file
+ * @author Pawel Zubrycki <paw.zubr@gmail.com>
+ * @author 2013 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
+ * @copyright Simplified BSD
+ *
+ * @cond
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the FreeBSD license as published by the FreeBSD
+ * project.
+ *
+ * This program 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.
+ *
+ * You should have received a copy of the FreeBSD license along with this
+ * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
+ * @endcond
+ */
+
+#ifndef EVWS_H
+#define EVWS_H
+
+#include <event2/event.h>
+#include <event2/bufferevent.h>
+#include <event2/listener.h>
+#include <sys/queue.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum evws_opcode {
+ EVWS_CONTINUATION_FRAME = 0x0,
+ EVWS_TEXT_FRAME = 0x1,
+ EVWS_BINARY_FRAME = 0x2,
+ EVWS_CONNECTION_CLOSE = 0x8,
+ EVWS_PING = 0x9,
+ EVWS_PONG = 0xa
+};
+
+enum evws_conn_state {
+ EVWS_NULL = 0x0,
+ EVWS_FIRSTLINE_READ = 0x1,
+ EVWS_HEADER_READ = 0x2,
+ EVWS_CONNECTED = 0x3,
+ EVWS_CLOSED = 0x4,
+ EVWS_READING_FRAME = 0x5,
+};
+
+enum evws_frame_state {
+ EVWS_OPCODE_READ = 0x0,
+ EVWS_PAYLOAD_LENGTH_LENGTH_READ = 0x1,
+ EVWS_PAYLOAD_LENGTH_READ = 0x2,
+ EVWS_MASKING_KEY_READ = 0x3,
+ EVWS_EXTENSION_READ = 0x4,
+};
+
+struct evws_header {
+ TAILQ_ENTRY(evws_header) next;
+ char *key;
+ char *value;
+};
+
+struct evws_frame {
+ /**
+ * Indicates that this is the final fragment in a message. The first
+ * fragment MAY also be the final fragment.
+ */
+ uint8_t fin;
+
+ /**
+ * MUST be 0 unless an extension is negotiated that defines meanings
+ * for non-zero values. If a nonzero value is received and none of
+ * the negotiated extensions defines the meaning of such a nonzero
+ * value, the receiving endpoint MUST _Fail the WebSocket
+ * Connection_.
+ */
+ uint8_t rsv;
+
+ /**
+ * %x0 denotes a continuation frame
+ * %x1 denotes a text frame
+ * %x2 denotes a binary frame
+ * %x3-7 are reserved for further non-control frames
+ * %x8 denotes a connection close
+ * %x9 denotes a ping
+ * %xA denotes a pong
+ * %xB-F are reserved for further control frames
+ */
+ enum evws_opcode opcode;
+
+ /**
+ * Defines whether the "Payload data" is masked.
+ */
+ uint8_t has_mask;
+
+ /**
+ * The length of the "Payload data", in bytes: if 0-125, that is the
+ * payload length. If 126, the following 2 bytes interpreted as a
+ * 16-bit unsigned integer are the payload length. If 127, the
+ * following 8 bytes interpreted as a 64-bit unsigned integer (the
+ * most significant bit MUST be 0) are the payload length.
+ */
+ uint64_t size;
+ uint64_t payload_read;
+ uint8_t payload_bytes;
+
+ /**
+ * The content or payload of the frame
+ */
+ char* data;
+
+ /**
+ * All frames sent from the client to the server are masked by a
+ * 32-bit value that is contained within the frame. This field is
+ * present if the mask bit is set to 1 and is absent if the mask bit
+ * is set to 0.
+ */
+ uint8_t mask[4];
+ uint8_t mask_bytes;
+ uint8_t mask_index;
+
+ uint8_t state;
+};
+
+struct evws_connection
+{
+ TAILQ_ENTRY(evws_connection) next;
+ struct evws *ws;
+ struct evws_frame *frame;
+ char *uri;
+ struct bufferevent *bufev;
+ int state;
+ int fd;
+
+ // headers
+ TAILQ_HEAD(wsheadersq, evws_header) headers;
+};
+
+typedef void (*cb_type)(struct evws_connection *, const char *, size_t, void *);
+typedef void (*cb_frame_type)(struct evws_connection *, struct evws_frame *, void *);
+
+struct evws_cb
+{
+ TAILQ_ENTRY(evws_cb) next;
+ char * uri;
+ cb_frame_type msg_cb;
+ cb_type conn_cb;
+ void * cb_arg;
+};
+
+struct evws_read_buf
+{
+ TAILQ_ENTRY(evws_read_buf) next;
+ char* data;
+};
+
+TAILQ_HEAD(evwsconq, evws_connection);
+
+struct evws
+{
+ struct evconnlistener *listener;
+ TAILQ_HEAD(wscbq, evws_cb) callbacks;
+ struct evwsconq connections;
+
+ // generic callback
+ cb_frame_type gencb;
+ void * gencb_arg;
+ struct event_base *base;
+};
+
+struct evws_connection *evws_connection_new(struct evws *ws, evutil_socket_t fd);
+void evws_connection_free(struct evws_connection *conn);
+
+struct evws_frame *evws_frame_new();
+void evws_frame_free(struct evws_frame *frame);
+
+struct evws *evws_new(struct event_base *base);
+void evws_free(struct evws *ptr);
+
+struct evws_header *evws_header_new(char *key, char *value);
+void evws_header_free(struct evws_header *header);
+
+char *evws_find_header(const struct wsheadersq *q, const char *key);
+evutil_socket_t evws_bind_socket(struct evws * ws, unsigned short port);
+int evws_set_cb(struct evws * ws, const char * pattern, cb_frame_type message_cb, cb_type connect_cb, void * arg);
+cb_frame_type evws_set_gencb(struct evws *ws, cb_frame_type cb, void * arg);
+void evws_broadcast(struct evws *ws, const char *uri, enum evws_opcode opcode, const char *data, uint64_t length);
+void evws_send_data(struct evws_connection *conn, enum evws_opcode opcode, const char *data, uint64_t length);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif