summaryrefslogtreecommitdiff
path: root/deps
diff options
context:
space:
mode:
authorTatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>2012-03-21 00:46:16 +0900
committerTatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>2012-03-21 00:46:16 +0900
commit86ecf36abbf121221a344738bf1b292ebd0fb82c (patch)
treeb9c7e86e11ebaed129d12449b35cd9aa8ef66109 /deps
parent41c77ab852378d3e9e0b579441a8a3e2cc6cdf91 (diff)
Added wslay WebSocket library.
I made following modifications to the original library: The doc directory was removed. Made shared library disabled by configure. Removed doc from SUBDIRS in Makefile.am.
Diffstat (limited to 'deps')
-rw-r--r--deps/wslay/.gitignore29
-rw-r--r--deps/wslay/AUTHORS1
-rw-r--r--deps/wslay/COPYING22
-rw-r--r--deps/wslay/ChangeLog0
-rw-r--r--deps/wslay/Makefile.am30
-rw-r--r--deps/wslay/NEWS41
-rw-r--r--deps/wslay/README1
-rw-r--r--deps/wslay/README.rst32
-rw-r--r--deps/wslay/configure.ac104
-rw-r--r--deps/wslay/examples/.gitignore3
-rw-r--r--deps/wslay/examples/echoserv.cc606
-rw-r--r--deps/wslay/examples/fork-echoserv.c426
-rw-r--r--deps/wslay/examples/testclient.cc524
-rw-r--r--deps/wslay/lib/Makefile.am42
-rw-r--r--deps/wslay/lib/includes/Makefile.am23
-rw-r--r--deps/wslay/lib/includes/wslay/wslay.h736
-rw-r--r--deps/wslay/lib/includes/wslay/wslayver.h.in31
-rw-r--r--deps/wslay/lib/libwslay.pc.in33
-rw-r--r--deps/wslay/lib/wslay_event.c984
-rw-r--r--deps/wslay/lib/wslay_event.h140
-rw-r--r--deps/wslay/lib/wslay_frame.c342
-rw-r--r--deps/wslay/lib/wslay_frame.h76
-rw-r--r--deps/wslay/lib/wslay_net.c62
-rw-r--r--deps/wslay/lib/wslay_net.h63
-rw-r--r--deps/wslay/lib/wslay_queue.c116
-rw-r--r--deps/wslay/lib/wslay_queue.h53
-rw-r--r--deps/wslay/lib/wslay_stack.c85
-rw-r--r--deps/wslay/lib/wslay_stack.h50
-rw-r--r--deps/wslay/m4/README1
-rw-r--r--deps/wslay/tests/Makefile.am44
-rw-r--r--deps/wslay/tests/main.c126
-rw-r--r--deps/wslay/tests/wslay_event_test.c491
-rw-r--r--deps/wslay/tests/wslay_event_test.h41
-rw-r--r--deps/wslay/tests/wslay_frame_test.c560
-rw-r--r--deps/wslay/tests/wslay_frame_test.h46
-rw-r--r--deps/wslay/tests/wslay_queue_test.c59
-rw-r--r--deps/wslay/tests/wslay_queue_test.h30
-rw-r--r--deps/wslay/tests/wslay_session_test.c27
-rw-r--r--deps/wslay/tests/wslay_session_test.h28
-rw-r--r--deps/wslay/tests/wslay_stack_test.c48
-rw-r--r--deps/wslay/tests/wslay_stack_test.h30
41 files changed, 6186 insertions, 0 deletions
diff --git a/deps/wslay/.gitignore b/deps/wslay/.gitignore
new file mode 100644
index 00000000..50ec1376
--- /dev/null
+++ b/deps/wslay/.gitignore
@@ -0,0 +1,29 @@
+*~
+*.o
+*.lo
+*.la
+depcomp
+*.m4
+Makefile
+Makefile.in
+libtool
+missing
+autom4te.cache/
+config.guess
+config.h
+config.h.in
+config.log
+config.status
+config.sub
+configure
+install-sh
+.deps/
+.libs
+lib/includes/wslay/wslayver.h
+lib/libwslay.pc
+ltmain.sh
+stamp-h1
+.deps/
+INSTALL
+.DS_STORE
+tests/main
diff --git a/deps/wslay/AUTHORS b/deps/wslay/AUTHORS
new file mode 100644
index 00000000..95bc9542
--- /dev/null
+++ b/deps/wslay/AUTHORS
@@ -0,0 +1 @@
+Tatsuhiro Tsujikawa <t-tujikawa at users dot sourceforge dot net>
diff --git a/deps/wslay/COPYING b/deps/wslay/COPYING
new file mode 100644
index 00000000..0550e0dc
--- /dev/null
+++ b/deps/wslay/COPYING
@@ -0,0 +1,22 @@
+The MIT License
+
+Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/deps/wslay/ChangeLog b/deps/wslay/ChangeLog
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/deps/wslay/ChangeLog
diff --git a/deps/wslay/Makefile.am b/deps/wslay/Makefile.am
new file mode 100644
index 00000000..8bb6f149
--- /dev/null
+++ b/deps/wslay/Makefile.am
@@ -0,0 +1,30 @@
+# Wslay - The WebSocket Library
+
+# Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+SUBDIRS = lib tests
+
+ACLOCAL_AMFLAGS = -I m4
+
+EXTRA_DIST = examples/Makefile \
+ examples/fork-echoserv.c \
+ examples/echoserv.cc \
+ examples/testclient.cc
diff --git a/deps/wslay/NEWS b/deps/wslay/NEWS
new file mode 100644
index 00000000..37b73272
--- /dev/null
+++ b/deps/wslay/NEWS
@@ -0,0 +1,41 @@
+wslay 0.1.1
+===========
+
+Release Note
+------------
+
+This release fixes the example programs and tutorial. It does not
+change library code at all, so the library version has not been
+changed.
+
+Changes
+-------
+
+* Fixed example source code and tutorial.
+
+
+
+wslay 0.1.0
+===========
+
+Release Note
+------------
+
+This is the initial release of wslay WebSocket C library.
+
+Wslay is a WebSocket library written in C. It implements the protocol
+version 13 described in RFC 6455. This library offers 2 levels of API:
+event-based API and frame-based low-level API. For event-based API, it
+is suitable for non-blocking reactor pattern style. You can set
+callbacks in various events. For frame-based API, you can send
+WebSocket frame directly. Wslay only supports data transfer part of
+WebSocket protocol and does not perform opening handshake in HTTP.
+
+Wslay does not perform any I/O operations for its own. Instead, it
+offers callbacks for them. This makes Wslay independent on any I/O
+frameworks, SSL, sockets, etc. This makes Wslay protable across
+various platforms and the application authors can choose freely IO
+frameworks.
+
+Visit http://wslay.sourceforge.net/ for the API reference and
+tutorial.
diff --git a/deps/wslay/README b/deps/wslay/README
new file mode 100644
index 00000000..5ccc0ea3
--- /dev/null
+++ b/deps/wslay/README
@@ -0,0 +1 @@
+See README.rst
diff --git a/deps/wslay/README.rst b/deps/wslay/README.rst
new file mode 100644
index 00000000..47a4424a
--- /dev/null
+++ b/deps/wslay/README.rst
@@ -0,0 +1,32 @@
+Wslay - The WebSocket library
+=============================
+
+Project Web: http://wslay.sourceforge.net/
+
+Wslay is a WebSocket library written in C.
+It implements the protocol version 13 described in
+`RFC 6455 <http://tools.ietf.org/html/rfc6455>`_.
+This library offers 2 levels of API:
+event-based API and frame-based low-level API. For event-based API, it
+is suitable for non-blocking reactor pattern style. You can set
+callbacks in various events. For frame-based API, you can send
+WebSocket frame directly. Wslay only supports data transfer part of
+WebSocket protocol and does not perform opening handshake in HTTP.
+
+Wslay supports:
+
+* Text/Binary messages.
+* Automatic ping reply.
+* Callback interface.
+* External event loop.
+
+Wslay does not perform any I/O operations for its own. Instead, it
+offers callbacks for them. This makes Wslay independent on any I/O
+frameworks, SSL, sockets, etc. This makes Wslay protable across
+various platforms and the application authors can choose freely I/O
+frameworks.
+
+See Autobahn test reports:
+`server <http://wslay.sourceforge.net/autobahn/reports/servers/index.html>`_
+and
+`client <http://wslay.sourceforge.net/autobahn/reports/clients/index.html>`_.
diff --git a/deps/wslay/configure.ac b/deps/wslay/configure.ac
new file mode 100644
index 00000000..fae2f84e
--- /dev/null
+++ b/deps/wslay/configure.ac
@@ -0,0 +1,104 @@
+dnl Wslay - The WebSocket Library
+
+dnl Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+
+dnl Permission is hereby granted, free of charge, to any person obtaining
+dnl a copy of this software and associated documentation files (the
+dnl "Software"), to deal in the Software without restriction, including
+dnl without limitation the rights to use, copy, modify, merge, publish,
+dnl distribute, sublicense, and/or sell copies of the Software, and to
+dnl permit persons to whom the Software is furnished to do so, subject to
+dnl the following conditions:
+
+dnl The above copyright notice and this permission notice shall be
+dnl included in all copies or substantial portions of the Software.
+
+dnl THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+dnl EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+dnl MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+dnl NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+dnl LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+dnl OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+dnl WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+AC_PREREQ(2.61)
+LT_PREREQ([2.2.6])
+AC_INIT([wslay], [0.1.1], [t-tujikawa@users.sourceforge.net])
+LT_INIT([disable-shared])
+AC_CONFIG_AUX_DIR([.])
+dnl See versioning rule:
+dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
+AC_SUBST(LT_CURRENT, 0)
+AC_SUBST(LT_REVISION, 0)
+AC_SUBST(LT_AGE, 0)
+
+AC_CONFIG_MACRO_DIR([m4])
+
+AM_INIT_AUTOMAKE()
+AC_CONFIG_HEADERS([config.h])
+
+dnl Checks for programs
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_MAKE_SET
+
+AC_PATH_PROG([SPHINX_BUILD], [sphinx-build])
+AC_SUBST([SPHINX_BUILD])
+AM_CONDITIONAL([HAVE_SPHINX_BUILD], [ test "x$SPHINX_BUILD" != "x" ])
+
+# Checks for libraries.
+AC_CHECK_LIB([cunit], [CU_initialize_registry],
+ [have_cunit=yes], [have_cunit=no])
+AM_CONDITIONAL([HAVE_CUNIT], [ test "x${have_cunit}" = "xyes" ])
+
+# Checks for header files.
+AC_CHECK_HEADERS([ \
+ arpa/inet.h \
+ netinet/in.h \
+ stddef.h \
+ stdint.h \
+ stdlib.h \
+ string.h \
+ unistd.h \
+])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_TYPE_SIZE_T
+AC_TYPE_SSIZE_T
+AC_TYPE_UINT8_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT64_T
+AC_CHECK_TYPES([ptrdiff_t])
+AC_C_BIGENDIAN
+
+# Checks for library functions.
+AC_FUNC_MALLOC
+AC_CHECK_FUNCS([ \
+ memmove \
+ memset \
+ ntohl \
+ ntohs \
+ htons
+])
+
+AC_CONFIG_FILES([
+ Makefile
+ lib/Makefile
+ lib/libwslay.pc
+ lib/includes/Makefile
+ lib/includes/wslay/wslayver.h
+ tests/Makefile
+])
+AC_OUTPUT
+
+AC_MSG_NOTICE([summary of build options:
+
+ version: ${VERSION} shared $LT_CURRENT:$LT_REVISION:$LT_AGE
+ Host type: ${host}
+ Install prefix: ${prefix}
+ C compiler: ${CC}
+ CFlags: ${CFLAGS}
+ Library types: Shared=${enable_shared}, Static=${enable_static}
+ CUnit: ${have_cunit}
+])
diff --git a/deps/wslay/examples/.gitignore b/deps/wslay/examples/.gitignore
new file mode 100644
index 00000000..0cd7270c
--- /dev/null
+++ b/deps/wslay/examples/.gitignore
@@ -0,0 +1,3 @@
+fork-echoserv
+echoserv
+testclient
diff --git a/deps/wslay/examples/echoserv.cc b/deps/wslay/examples/echoserv.cc
new file mode 100644
index 00000000..193c2d1b
--- /dev/null
+++ b/deps/wslay/examples/echoserv.cc
@@ -0,0 +1,606 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+// WebSocket Echo Server
+// This is suitable for Autobahn server test.
+// g++ -Wall -O2 -g -o echoserv echoserv.cc -L../lib/.libs -I../lib/includes -lwslay -lnettle
+// $ export LD_LIBRARY_PATH=../lib/.libs
+// $ ./a.out 9000
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/epoll.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <signal.h>
+
+#include <cassert>
+#include <cstdio>
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include <string>
+#include <iostream>
+#include <string>
+#include <set>
+#include <iomanip>
+#include <fstream>
+
+#include <nettle/base64.h>
+#include <nettle/sha.h>
+#include <wslay/wslay.h>
+
+int create_listen_socket(const char *service)
+{
+ struct addrinfo hints;
+ int sfd = -1;
+ int r;
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
+ struct addrinfo *res;
+ r = getaddrinfo(0, service, &hints, &res);
+ if(r != 0) {
+ std::cerr << "getaddrinfo: " << gai_strerror(r) << std::endl;
+ return -1;
+ }
+ for(struct addrinfo *rp = res; rp; rp = rp->ai_next) {
+ sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+ if(sfd == -1) {
+ continue;
+ }
+ int val = 1;
+ if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &val,
+ static_cast<socklen_t>(sizeof(val))) == -1) {
+ continue;
+ }
+ if(bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) {
+ break;
+ }
+ close(sfd);
+ }
+ freeaddrinfo(res);
+ if(listen(sfd, 16) == -1) {
+ perror("listen");
+ close(sfd);
+ return -1;
+ }
+ return sfd;
+}
+
+int make_non_block(int fd)
+{
+ int flags, r;
+ while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR);
+ if(flags == -1) {
+ return -1;
+ }
+ while((r = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR);
+ if(r == -1) {
+ return -1;
+ }
+ return 0;
+}
+
+std::string sha1(const std::string& src)
+{
+ sha1_ctx ctx;
+ sha1_init(&ctx);
+ sha1_update(&ctx, src.size(), reinterpret_cast<const uint8_t*>(src.c_str()));
+ uint8_t temp[SHA1_DIGEST_SIZE];
+ sha1_digest(&ctx, SHA1_DIGEST_SIZE, temp);
+ std::string res(&temp[0], &temp[SHA1_DIGEST_SIZE]);
+ return res;
+}
+
+std::string base64(const std::string& src)
+{
+ base64_encode_ctx ctx;
+ base64_encode_init(&ctx);
+ int dstlen = BASE64_ENCODE_RAW_LENGTH(src.size());
+ uint8_t *dst = new uint8_t[dstlen];
+ base64_encode_raw(dst, src.size(), reinterpret_cast<const uint8_t*>(src.c_str()));
+ std::string res(&dst[0], &dst[dstlen]);
+ delete [] dst;
+ return res;
+}
+
+std::string create_acceptkey(const std::string& clientkey)
+{
+ std::string s = clientkey+"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+ return base64(sha1(s));
+}
+
+class EventHandler {
+public:
+ virtual ~EventHandler() {}
+ virtual int on_read_event() = 0;
+ virtual int on_write_event() = 0;
+ virtual bool want_read() = 0;
+ virtual bool want_write() = 0;
+ virtual int fd() const = 0;
+ virtual bool finish() = 0;
+ virtual EventHandler* next() = 0;
+};
+
+ssize_t send_callback(wslay_event_context_ptr ctx,
+ const uint8_t *data, size_t len, int flags,
+ void *user_data);
+ssize_t recv_callback(wslay_event_context_ptr ctx, uint8_t *data, size_t len,
+ int flags, void *user_data);
+void on_msg_recv_callback(wslay_event_context_ptr ctx,
+ const struct wslay_event_on_msg_recv_arg *arg,
+ void *user_data);
+
+class EchoWebSocketHandler : public EventHandler {
+public:
+ EchoWebSocketHandler(int fd)
+ : fd_(fd)
+ {
+ struct wslay_event_callbacks callbacks = {
+ recv_callback,
+ send_callback,
+ NULL, /* genmask_callback */
+ NULL, /* on_frame_recv_start_callback */
+ NULL, /* on_frame_recv_callback */
+ NULL, /* on_frame_recv_end_callback */
+ on_msg_recv_callback
+ };
+ wslay_event_context_server_init(&ctx_, &callbacks, this);
+ }
+ virtual ~EchoWebSocketHandler()
+ {
+ wslay_event_context_free(ctx_);
+ shutdown(fd_, SHUT_WR);
+ close(fd_);
+ }
+ virtual int on_read_event()
+ {
+ if(wslay_event_recv(ctx_) == 0) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+ virtual int on_write_event()
+ {
+ if(wslay_event_send(ctx_) == 0) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+ ssize_t send_data(const uint8_t *data, size_t len, int flags)
+ {
+ ssize_t r;
+ int sflags = 0;
+#ifdef MSG_MORE
+ if(flags & WSLAY_MSG_MORE) {
+ sflags |= MSG_MORE;
+ }
+#endif // MSG_MORE
+ while((r = send(fd_, data, len, sflags)) == -1 && errno == EINTR);
+ return r;
+ }
+ ssize_t recv_data(uint8_t *data, size_t len, int flags)
+ {
+ ssize_t r;
+ while((r = recv(fd_, data, len, 0)) == -1 && errno == EINTR);
+ return r;
+ }
+ virtual bool want_read()
+ {
+ return wslay_event_want_read(ctx_);
+ }
+ virtual bool want_write()
+ {
+ return wslay_event_want_write(ctx_);
+ }
+ virtual int fd() const
+ {
+ return fd_;
+ }
+ virtual bool finish()
+ {
+ return !want_read() && !want_write();
+ }
+ virtual EventHandler* next()
+ {
+ return 0;
+ }
+private:
+ int fd_;
+ wslay_event_context_ptr ctx_;
+};
+
+ssize_t send_callback(wslay_event_context_ptr ctx,
+ const uint8_t *data, size_t len, int flags,
+ void *user_data)
+{
+ EchoWebSocketHandler *sv = (EchoWebSocketHandler*)user_data;
+ ssize_t r = sv->send_data(data, len, flags);
+ if(r == -1) {
+ if(errno == EAGAIN || errno == EWOULDBLOCK) {
+ wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK);
+ } else {
+ wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
+ }
+ }
+ return r;
+}
+
+ssize_t recv_callback(wslay_event_context_ptr ctx, uint8_t *data, size_t len,
+ int flags, void *user_data)
+{
+ EchoWebSocketHandler *sv = (EchoWebSocketHandler*)user_data;
+ ssize_t r = sv->recv_data(data, len, flags);
+ if(r == -1) {
+ if(errno == EAGAIN || errno == EWOULDBLOCK) {
+ wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK);
+ } else {
+ wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
+ }
+ } else if(r == 0) {
+ wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
+ r = -1;
+ }
+ return r;
+}
+
+void on_msg_recv_callback(wslay_event_context_ptr ctx,
+ const struct wslay_event_on_msg_recv_arg *arg,
+ void *user_data)
+{
+ if(!wslay_is_ctrl_frame(arg->opcode)) {
+ struct wslay_event_msg msgarg = {
+ arg->opcode, arg->msg, arg->msg_length
+ };
+ wslay_event_queue_msg(ctx, &msgarg);
+ }
+}
+
+class HttpHandshakeSendHandler : public EventHandler {
+public:
+ HttpHandshakeSendHandler(int fd, const std::string& accept_key)
+ : fd_(fd),
+ resheaders_("HTTP/1.1 101 Switching Protocols\r\n"
+ "Upgrade: websocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Accept: "+accept_key+"\r\n"
+ "\r\n"),
+ off_(0)
+ {}
+ virtual ~HttpHandshakeSendHandler()
+ {
+ if(fd_ != -1) {
+ shutdown(fd_, SHUT_WR);
+ close(fd_);
+ }
+ }
+ virtual int on_read_event()
+ {
+ return 0;
+ }
+ virtual int on_write_event()
+ {
+ while(1) {
+ size_t len = resheaders_.size()-off_;
+ if(len == 0) {
+ break;
+ }
+ ssize_t r;
+ while((r = write(fd_, resheaders_.c_str()+off_, len)) == -1 &&
+ errno == EINTR);
+ if(r == -1) {
+ if(errno == EAGAIN || errno == EWOULDBLOCK) {
+ break;
+ } else {
+ perror("write");
+ return -1;
+ }
+ } else {
+ off_ += r;
+ }
+ }
+ return 0;
+ }
+ virtual bool want_read()
+ {
+ return false;
+ }
+ virtual bool want_write()
+ {
+ return true;
+ }
+ virtual int fd() const
+ {
+ return fd_;
+ }
+ virtual bool finish()
+ {
+ return off_ == resheaders_.size();
+ }
+ virtual EventHandler* next()
+ {
+ if(finish()) {
+ int fd = fd_;
+ fd_ = -1;
+ return new EchoWebSocketHandler(fd);
+ } else {
+ return 0;
+ }
+ }
+private:
+ int fd_;
+ std::string headers_;
+ std::string resheaders_;
+ size_t off_;
+};
+
+class HttpHandshakeRecvHandler : public EventHandler {
+public:
+ HttpHandshakeRecvHandler(int fd)
+ : fd_(fd)
+ {}
+ virtual ~HttpHandshakeRecvHandler()
+ {
+ if(fd_ != -1) {
+ close(fd_);
+ }
+ }
+ virtual int on_read_event()
+ {
+ char buf[4096];
+ ssize_t r;
+ std::string client_key;
+ while(1) {
+ while((r = read(fd_, buf, sizeof(buf))) == -1 && errno == EINTR);
+ if(r == -1) {
+ if(errno == EWOULDBLOCK || errno == EAGAIN) {
+ break;
+ } else {
+ perror("read");
+ return -1;
+ }
+ } else if(r == 0) {
+ std::cerr << "http_upgrade: Got EOF" << std::endl;
+ return -1;
+ } else {
+ headers_.append(buf, buf+r);
+ if(headers_.size() > 8192) {
+ std::cerr << "Too large http header" << std::endl;
+ return -1;
+ }
+ }
+ }
+ if(headers_.find("\r\n\r\n") != std::string::npos) {
+ std::string::size_type keyhdstart;
+ if(headers_.find("Upgrade: websocket\r\n") == std::string::npos ||
+ headers_.find("Connection: Upgrade\r\n") == std::string::npos ||
+ (keyhdstart = headers_.find("Sec-WebSocket-Key: ")) ==
+ std::string::npos) {
+ std::cerr << "http_upgrade: missing required headers" << std::endl;
+ return -1;
+ }
+ keyhdstart += 19;
+ std::string::size_type keyhdend = headers_.find("\r\n", keyhdstart);
+ client_key = headers_.substr(keyhdstart, keyhdend-keyhdstart);
+ accept_key_ = create_acceptkey(client_key);
+ }
+ return 0;
+ }
+ virtual int on_write_event()
+ {
+ return 0;
+ }
+ virtual bool want_read()
+ {
+ return true;
+ }
+ virtual bool want_write()
+ {
+ return false;
+ }
+ virtual int fd() const
+ {
+ return fd_;
+ }
+ virtual bool finish()
+ {
+ return !accept_key_.empty();
+ }
+ virtual EventHandler* next()
+ {
+ if(finish()) {
+ int fd = fd_;
+ fd_ = -1;
+ return new HttpHandshakeSendHandler(fd, accept_key_);
+ } else {
+ return 0;
+ }
+ }
+private:
+ int fd_;
+ std::string headers_;
+ std::string accept_key_;
+};
+
+class ListenEventHandler : public EventHandler {
+public:
+ ListenEventHandler(int fd)
+ : fd_(fd), cfd_(-1)
+ {}
+ virtual ~ListenEventHandler()
+ {
+ close(fd_);
+ close(cfd_);
+ }
+ virtual int on_read_event()
+ {
+ if(cfd_ != -1) {
+ close(cfd_);
+ }
+ while((cfd_ = accept(fd_, 0, 0)) == -1 && errno == EINTR);
+ if(cfd_ == -1) {
+ perror("accept");
+ }
+ return 0;
+ }
+ virtual int on_write_event()
+ {
+ return 0;
+ }
+ virtual bool want_read()
+ {
+ return true;
+ }
+ virtual bool want_write()
+ {
+ return false;
+ }
+ virtual int fd() const
+ {
+ return fd_;
+ }
+ virtual bool finish()
+ {
+ return false;
+ }
+ virtual EventHandler* next()
+ {
+ if(cfd_ != -1) {
+ int val = 1;
+ int fd = cfd_;
+ cfd_ = -1;
+ if(make_non_block(fd) == -1 ||
+ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val))
+ == -1) {
+ close(fd);
+ return 0;
+ }
+ return new HttpHandshakeRecvHandler(fd);
+ } else {
+ return 0;
+ }
+ }
+private:
+ int fd_;
+ int cfd_;
+};
+
+int ctl_epollev(int epollfd, int op, EventHandler *handler)
+{
+ epoll_event ev;
+ memset(&ev, 0, sizeof(ev));
+ int events = 0;
+ if(handler->want_read()) {
+ events |= EPOLLIN;
+ }
+ if(handler->want_write()) {
+ events |= EPOLLOUT;
+ }
+ ev.events = events;
+ ev.data.ptr = handler;
+ return epoll_ctl(epollfd, op, handler->fd(), &ev);
+}
+
+void reactor(int sfd)
+{
+ std::set<EventHandler*> handlers;
+ ListenEventHandler* listen_handler = new ListenEventHandler(sfd);
+ handlers.insert(listen_handler);
+ int epollfd = epoll_create(16);
+ if(epollfd == -1) {
+ perror("epoll_create");
+ exit(EXIT_FAILURE);
+ }
+ if(ctl_epollev(epollfd, EPOLL_CTL_ADD, listen_handler) == -1) {
+ perror("epoll_ctl");
+ exit(EXIT_FAILURE);
+ }
+ static const size_t MAX_EVENTS = 64;
+ epoll_event events[MAX_EVENTS];
+ while(1) {
+ int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
+ if(nfds == -1) {
+ perror("epoll_wait");
+ return;
+ }
+ for(int n = 0; n < nfds; ++n) {
+ EventHandler* eh = (EventHandler*)events[n].data.ptr;
+ if(((events[n].events & EPOLLIN) && eh->on_read_event() == -1) ||
+ ((events[n].events & EPOLLOUT) && eh->on_write_event() == -1) ||
+ (events[n].events & (EPOLLERR | EPOLLHUP))) {
+ handlers.erase(eh);
+ delete eh;
+ } else {
+ EventHandler* next = eh->next();
+ if(next) {
+ handlers.insert(next);
+ if(ctl_epollev(epollfd, EPOLL_CTL_ADD, next) == -1) {
+ if(errno == EEXIST) {
+ if(ctl_epollev(epollfd, EPOLL_CTL_MOD, next) == -1) {
+ perror("epoll_ctl");
+ delete next;
+ }
+ } else {
+ perror("epoll_ctl");
+ delete next;
+ }
+ }
+ }
+ if(eh->finish()) {
+ handlers.erase(eh);
+ delete eh;
+ } else {
+ if(ctl_epollev(epollfd, EPOLL_CTL_MOD, eh) == -1) {
+ perror("epoll_ctl");
+ }
+ }
+ }
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ if(argc < 2) {
+ std::cerr << "Usage: " << argv[0] << " PORT" << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ struct sigaction act;
+ memset(&act, 0, sizeof(struct sigaction));
+ act.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &act, 0);
+ int sfd = create_listen_socket(argv[1]);
+ if(sfd == -1) {
+ std::cerr << "Failed to create server socket" << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ std::cout << "WebSocket echo server, listening on " << argv[1] << std::endl;
+ reactor(sfd);
+}
diff --git a/deps/wslay/examples/fork-echoserv.c b/deps/wslay/examples/fork-echoserv.c
new file mode 100644
index 00000000..910de17c
--- /dev/null
+++ b/deps/wslay/examples/fork-echoserv.c
@@ -0,0 +1,426 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * WebSocket Echo Server
+ * This is suitable for Autobahn server test.
+ *
+ * Dependency: nettle-dev
+ *
+ * To compile:
+ * $ gcc -Wall -O2 -g -o fork-echoserv fork-echoserv.c -L../lib/.libs -I../lib/includes -lwslay -lnettle
+ *
+ * To run:
+ * $ export LD_LIBRARY_PATH=../lib/.libs
+ * $ ./a.out 9000
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <signal.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <nettle/base64.h>
+#include <nettle/sha.h>
+#include <wslay/wslay.h>
+
+/*
+ * Create server socket, listen on *service*. This function returns
+ * file descriptor of server socket if it succeeds, or returns -1.
+ */
+int create_listen_socket(const char *service)
+{
+ struct addrinfo hints, *res, *rp;
+ int sfd = -1;
+ int r;
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
+ r = getaddrinfo(0, service, &hints, &res);
+ if(r != 0) {
+ fprintf(stderr, "getaddrinfo: %s", gai_strerror(r));
+ return -1;
+ }
+ for(rp = res; rp; rp = rp->ai_next) {
+ int val = 1;
+ sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+ if(sfd == -1) {
+ continue;
+ }
+ if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &val,
+ (socklen_t)sizeof(val)) == -1) {
+ continue;
+ }
+ if(bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) {
+ break;
+ }
+ close(sfd);
+ }
+ freeaddrinfo(res);
+ if(listen(sfd, 16) == -1) {
+ perror("listen");
+ close(sfd);
+ return -1;
+ }
+ return sfd;
+}
+
+/*
+ * Makes file descriptor *fd* non-blocking mode.
+ * This function returns 0, or returns -1.
+ */
+int make_non_block(int fd)
+{
+ int flags, r;
+ while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR);
+ if(flags == -1) {
+ perror("fcntl");
+ return -1;
+ }
+ while((r = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR);
+ if(r == -1) {
+ perror("fcntl");
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Calculates SHA-1 hash of *src*. The size of *src* is *src_length* bytes.
+ * *dst* must be at least SHA1_DIGEST_SIZE.
+ */
+void sha1(uint8_t *dst, const uint8_t *src, size_t src_length)
+{
+ struct sha1_ctx ctx;
+ sha1_init(&ctx);
+ sha1_update(&ctx, src_length, src);
+ sha1_digest(&ctx, SHA1_DIGEST_SIZE, dst);
+}
+
+/*
+ * Base64-encode *src* and stores it in *dst*.
+ * The size of *src* is *src_length*.
+ * *dst* must be at least BASE64_ENCODE_RAW_LENGTH(src_length).
+ */
+void base64(uint8_t *dst, const uint8_t *src, size_t src_length)
+{
+ struct base64_encode_ctx ctx;
+ base64_encode_init(&ctx);
+ base64_encode_raw(dst, src_length, src);
+}
+
+#define WS_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+
+/*
+ * Create Server's accept key in *dst*.
+ * *client_key* is the value of |Sec-WebSocket-Key| header field in
+ * client's handshake and it must be length of 24.
+ * *dst* must be at least BASE64_ENCODE_RAW_LENGTH(20)+1.
+ */
+void create_accept_key(char *dst, const char *client_key)
+{
+ uint8_t sha1buf[20], key_src[60];
+ memcpy(key_src, client_key, 24);
+ memcpy(key_src+24, WS_GUID, 36);
+ sha1(sha1buf, key_src, sizeof(key_src));
+ base64((uint8_t*)dst, sha1buf, 20);
+ dst[BASE64_ENCODE_RAW_LENGTH(20)] = '\0';
+}
+
+/*
+ * Performs HTTP handshake. *fd* is the file descriptor of the
+ * connection to the client. This function returns 0 if it succeeds,
+ * or returns -1.
+ */
+int http_handshake(int fd)
+{
+ /*
+ * Note: The implementation of HTTP handshake in this function is
+ * written for just a example of how to use of wslay library and is
+ * not meant to be used in production code. In practice, you need
+ * to do more strict verification of the client's handshake.
+ */
+ char header[16384], accept_key[29], *keyhdstart, *keyhdend, res_header[256];
+ size_t header_length = 0, res_header_sent = 0, res_header_length;
+ ssize_t r;
+ while(1) {
+ while((r = read(fd, header+header_length,
+ sizeof(header)-header_length)) == -1 && errno == EINTR);
+ if(r == -1) {
+ perror("read");
+ return -1;
+ } else if(r == 0) {
+ fprintf(stderr, "HTTP Handshake: Got EOF");
+ return -1;
+ } else {
+ header_length += r;
+ if(header_length >= 4 &&
+ memcmp(header+header_length-4, "\r\n\r\n", 4) == 0) {
+ break;
+ } else if(header_length == sizeof(header)) {
+ fprintf(stderr, "HTTP Handshake: Too large HTTP headers");
+ return -1;
+ }
+ }
+ }
+ if(strstr(header, "\r\nUpgrade: websocket\r\n") == NULL ||
+ strstr(header, "\r\nConnection: Upgrade\r\n") == NULL ||
+ (keyhdstart = strstr(header, "\r\nSec-WebSocket-Key:")) == NULL) {
+ fprintf(stderr, "HTTP Handshake: Missing required header fields");
+ return -1;
+ }
+ keyhdstart += 20;
+ for(; *keyhdstart == ' '; ++keyhdstart);
+ keyhdend = keyhdstart;
+ for(; *keyhdend != '\r' && *keyhdend != ' '; ++keyhdend);
+ if(keyhdend-keyhdstart != 24) {
+ printf("%s\n", keyhdstart);
+ fprintf(stderr, "HTTP Handshake: Invalid value in Sec-WebSocket-Key");
+ return -1;
+ }
+ create_accept_key(accept_key, keyhdstart);
+ snprintf(res_header, sizeof(res_header),
+ "HTTP/1.1 101 Switching Protocols\r\n"
+ "Upgrade: websocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Accept: %s\r\n"
+ "\r\n", accept_key);
+ res_header_length = strlen(res_header);
+ while(res_header_sent < res_header_length) {
+ while((r = write(fd, res_header+res_header_sent,
+ res_header_length-res_header_sent)) == -1 &&
+ errno == EINTR);
+ if(r == -1) {
+ perror("write");
+ return -1;
+ } else {
+ res_header_sent += r;
+ }
+ }
+ return 0;
+}
+
+/*
+ * This struct is passed as *user_data* in callback function. The
+ * *fd* member is the file descriptor of the connection to the client.
+ */
+struct Session {
+ int fd;
+};
+
+ssize_t send_callback(wslay_event_context_ptr ctx,
+ const uint8_t *data, size_t len, int flags,
+ void *user_data)
+{
+ struct Session *session = (struct Session*)user_data;
+ ssize_t r;
+ int sflags = 0;
+#ifdef MSG_MORE
+ if(flags & WSLAY_MSG_MORE) {
+ sflags |= MSG_MORE;
+ }
+#endif // MSG_MORE
+ while((r = send(session->fd, data, len, sflags)) == -1 && errno == EINTR);
+ if(r == -1) {
+ if(errno == EAGAIN || errno == EWOULDBLOCK) {
+ wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK);
+ } else {
+ wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
+ }
+ }
+ return r;
+}
+
+ssize_t recv_callback(wslay_event_context_ptr ctx, uint8_t *buf, size_t len,
+ int flags, void *user_data)
+{
+ struct Session *session = (struct Session*)user_data;
+ ssize_t r;
+ while((r = recv(session->fd, buf, len, 0)) == -1 && errno == EINTR);
+ if(r == -1) {
+ if(errno == EAGAIN || errno == EWOULDBLOCK) {
+ wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK);
+ } else {
+ wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
+ }
+ } else if(r == 0) {
+ /* Unexpected EOF is also treated as an error */
+ wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
+ r = -1;
+ }
+ return r;
+}
+
+void on_msg_recv_callback(wslay_event_context_ptr ctx,
+ const struct wslay_event_on_msg_recv_arg *arg,
+ void *user_data)
+{
+ /* Echo back non-control message */
+ if(!wslay_is_ctrl_frame(arg->opcode)) {
+ struct wslay_event_msg msgarg = {
+ arg->opcode, arg->msg, arg->msg_length
+ };
+ wslay_event_queue_msg(ctx, &msgarg);
+ }
+}
+
+/*
+ * Communicate with the client. This function performs HTTP handshake
+ * and WebSocket data transfer until close handshake is done or an
+ * error occurs. *fd* is the file descriptor of the connection to the
+ * client. This function returns 0 if it succeeds, or returns 0.
+ */
+int communicate(int fd)
+{
+ wslay_event_context_ptr ctx;
+ struct wslay_event_callbacks callbacks = {
+ recv_callback,
+ send_callback,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ on_msg_recv_callback
+ };
+ struct Session session = { fd };
+ int val = 1;
+ struct pollfd event;
+ int res = 0;
+
+ if(http_handshake(fd) == -1) {
+ return -1;
+ }
+ if(make_non_block(fd) == -1) {
+ return -1;
+ }
+ if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val))
+ == -1) {
+ perror("setsockopt: TCP_NODELAY");
+ return -1;
+ }
+ memset(&event, 0, sizeof(struct pollfd));
+ event.fd = fd;
+ event.events = POLLIN;
+ wslay_event_context_server_init(&ctx, &callbacks, &session);
+ /*
+ * Event loop: basically loop until both wslay_event_want_read(ctx)
+ * and wslay_event_want_write(ctx) return 0.
+ */
+ while(wslay_event_want_read(ctx) || wslay_event_want_write(ctx)) {
+ int r;
+ while((r = poll(&event, 1, -1)) == -1 && errno == EINTR);
+ if(r == -1) {
+ perror("poll");
+ res = -1;
+ break;
+ }
+ if(((event.revents & POLLIN) && wslay_event_recv(ctx) != 0) ||
+ ((event.revents & POLLOUT) && wslay_event_send(ctx) != 0) ||
+ (event.revents & (POLLERR | POLLHUP | POLLNVAL))) {
+ /*
+ * If either wslay_event_recv() or wslay_event_send() return
+ * non-zero value, it means serious error which prevents wslay
+ * library from processing further data, so WebSocket connection
+ * must be closed.
+ */
+ res = -1;
+ break;
+ }
+ event.events = 0;
+ if(wslay_event_want_read(ctx)) {
+ event.events |= POLLIN;
+ }
+ if(wslay_event_want_write(ctx)) {
+ event.events |= POLLOUT;
+ }
+ }
+ return res;
+}
+
+/*
+ * Serves echo back service forever. *sfd* is the file descriptor of
+ * the server socket. when the incoming connection from the client is
+ * accepted, this function forks another process and the forked
+ * process communicates with client. The parent process goes back to
+ * the loop and can accept another client.
+ */
+void serve(int sfd)
+{
+ while(1) {
+ int fd;
+ while((fd = accept(sfd, NULL, NULL)) == -1 && errno == EINTR);
+ if(fd == -1) {
+ perror("accept");
+ } else {
+ int r = fork();
+ if(r == -1) {
+ perror("fork");
+ close(fd);
+ } else if(r == 0) {
+ int r = communicate(fd);
+ shutdown(fd, SHUT_WR);
+ close(fd);
+ if(r == 0) {
+ exit(EXIT_SUCCESS);
+ } else {
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct sigaction act;
+ int sfd;
+ if(argc < 2) {
+ fprintf(stderr, "Usage: %s PORT\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ memset(&act, 0, sizeof(struct sigaction));
+ act.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &act, NULL);
+ sigaction(SIGCHLD, &act, NULL);
+
+ sfd = create_listen_socket(argv[1]);
+ if(sfd == -1) {
+ fprintf(stderr, "Failed to create server socket\n");
+ exit(EXIT_FAILURE);
+ }
+ printf("WebSocket echo server, listening on %s\n", argv[1]);
+ serve(sfd);
+ return EXIT_SUCCESS;
+}
diff --git a/deps/wslay/examples/testclient.cc b/deps/wslay/examples/testclient.cc
new file mode 100644
index 00000000..6fbbec58
--- /dev/null
+++ b/deps/wslay/examples/testclient.cc
@@ -0,0 +1,524 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+// WebSocket Test Client for Autobahn client test
+// $ g++ -Wall -O2 -g -o testclient testclient.cc -L../lib/.libs -I../lib/includes -lwslay -lnettle
+// $ export LD_LIBRARY_PATH=../lib/.libs
+// $ ./a.out localhost 9001
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/epoll.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <signal.h>
+
+#include <cassert>
+#include <cstdio>
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include <string>
+#include <iostream>
+#include <string>
+#include <set>
+#include <iomanip>
+#include <fstream>
+
+#include <nettle/base64.h>
+#include <nettle/sha.h>
+#include <wslay/wslay.h>
+
+int connect_to(const char *host, const char *service)
+{
+ struct addrinfo hints;
+ int fd = -1;
+ int r;
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ struct addrinfo *res;
+ r = getaddrinfo(host, service, &hints, &res);
+ if(r != 0) {
+ std::cerr << "getaddrinfo: " << gai_strerror(r) << std::endl;
+ return -1;
+ }
+ for(struct addrinfo *rp = res; rp; rp = rp->ai_next) {
+ fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+ if(fd == -1) {
+ continue;
+ }
+ while((r = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
+ errno == EINTR);
+ if(r == 0) {
+ break;
+ }
+ close(fd);
+ fd = -1;
+ }
+ freeaddrinfo(res);
+ return fd;
+}
+
+int make_non_block(int fd)
+{
+ int flags, r;
+ while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR);
+ if(flags == -1) {
+ return -1;
+ }
+ while((r = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR);
+ if(r == -1) {
+ return -1;
+ }
+ return 0;
+}
+
+std::string sha1(const std::string& src)
+{
+ sha1_ctx ctx;
+ sha1_init(&ctx);
+ sha1_update(&ctx, src.size(), reinterpret_cast<const uint8_t*>(src.c_str()));
+ uint8_t temp[SHA1_DIGEST_SIZE];
+ sha1_digest(&ctx, SHA1_DIGEST_SIZE, temp);
+ std::string res(&temp[0], &temp[SHA1_DIGEST_SIZE]);
+ return res;
+}
+
+std::string base64(const std::string& src)
+{
+ base64_encode_ctx ctx;
+ base64_encode_init(&ctx);
+ int dstlen = BASE64_ENCODE_RAW_LENGTH(src.size());
+ uint8_t *dst = new uint8_t[dstlen];
+ base64_encode_raw(dst, src.size(),
+ reinterpret_cast<const uint8_t*>(src.c_str()));
+ std::string res(&dst[0], &dst[dstlen]);
+ delete [] dst;
+ return res;
+}
+
+std::string create_acceptkey(const std::string& clientkey)
+{
+ std::string s = clientkey+"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+ return base64(sha1(s));
+}
+
+class WebSocketClient {
+public:
+ WebSocketClient(int fd, struct wslay_event_callbacks *callbacks,
+ const std::string& body)
+ : fd_(fd),
+ body_(body),
+ body_off_(0),
+ dev_urand_("/dev/urandom")
+ {
+ wslay_event_context_client_init(&ctx_, callbacks, this);
+ }
+ ~WebSocketClient()
+ {
+ wslay_event_context_free(ctx_);
+ shutdown(fd_, SHUT_WR);
+ close(fd_);
+ }
+ int on_read_event()
+ {
+ return wslay_event_recv(ctx_);
+ }
+ int on_write_event()
+ {
+ return wslay_event_send(ctx_);
+ }
+ ssize_t send_data(const uint8_t *data, size_t len, int flags)
+ {
+ ssize_t r;
+ int sflags = 0;
+#ifdef MSG_MORE
+ if(flags & WSLAY_MSG_MORE) {
+ sflags |= MSG_MORE;
+ }
+#endif // MSG_MORE
+ while((r = send(fd_, data, len, sflags)) == -1 && errno == EINTR);
+ return r;
+ }
+ ssize_t feed_body(uint8_t *data, size_t len)
+ {
+ if(body_off_ < body_.size()) {
+ size_t wlen = std::min(len, body_.size()-body_off_);
+ memcpy(data, body_.c_str(), wlen);
+ body_off_ += wlen;
+ return wlen;
+ } else {
+ return 0;
+ }
+ }
+ ssize_t recv_data(uint8_t *data, size_t len, int flags)
+ {
+ ssize_t r;
+ while((r = recv(fd_, data, len, 0)) == -1 && errno == EINTR);
+ return r;
+ }
+ bool want_read()
+ {
+ return wslay_event_want_read(ctx_);
+ }
+ bool want_write()
+ {
+ return wslay_event_want_write(ctx_);
+ }
+ int fd() const
+ {
+ return fd_;
+ }
+ void get_random(uint8_t *buf, size_t len)
+ {
+ dev_urand_.read((char*)buf, len);
+ }
+ void set_callbacks(const struct wslay_event_callbacks *callbacks)
+ {
+ wslay_event_config_set_callbacks(ctx_, callbacks);
+ }
+private:
+ int fd_;
+ wslay_event_context_ptr ctx_;
+ std::string body_;
+ size_t body_off_;
+ std::fstream dev_urand_;
+};
+
+ssize_t send_callback(wslay_event_context_ptr ctx,
+ const uint8_t *data, size_t len, int flags,
+ void *user_data)
+{
+ WebSocketClient *ws = (WebSocketClient*)user_data;
+ ssize_t r = ws->send_data(data, len, flags);
+ if(r == -1) {
+ if(errno == EAGAIN || errno == EWOULDBLOCK) {
+ wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK);
+ } else {
+ wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
+ }
+ }
+ return r;
+}
+
+ssize_t recv_callback(wslay_event_context_ptr ctx, uint8_t *data, size_t len,
+ int flags, void *user_data)
+{
+ WebSocketClient *ws = (WebSocketClient*)user_data;
+ ssize_t r = ws->recv_data(data, len, flags);
+ if(r == -1) {
+ if(errno == EAGAIN || errno == EWOULDBLOCK) {
+ wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK);
+ } else {
+ wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
+ }
+ } else if(r == 0) {
+ wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
+ r = -1;
+ }
+ return r;
+}
+
+ssize_t feed_body_callback
+(wslay_event_context_ptr ctx, uint8_t *data, size_t len, int flags,
+ void *user_data)
+{
+ WebSocketClient *ws = (WebSocketClient*)user_data;
+ return ws->feed_body(data, len);
+}
+
+int genmask_callback(wslay_event_context_ptr ctx, uint8_t *buf, size_t len,
+ void *user_data)
+{
+ WebSocketClient *ws = (WebSocketClient*)user_data;
+ ws->get_random(buf, len);
+ return 0;
+}
+
+void on_msg_recv_callback(wslay_event_context_ptr ctx,
+ const struct wslay_event_on_msg_recv_arg *arg,
+ void *user_data)
+{
+ if(!wslay_is_ctrl_frame(arg->opcode)) {
+ struct wslay_event_msg msgarg = {
+ arg->opcode, arg->msg, arg->msg_length
+ };
+ wslay_event_queue_msg(ctx, &msgarg);
+ }
+}
+
+std::string casecntjson;
+
+void get_casecnt_on_msg_recv_callback
+(wslay_event_context_ptr ctx,
+ const struct wslay_event_on_msg_recv_arg *arg,
+ void *user_data)
+{
+ if(arg->opcode == WSLAY_TEXT_FRAME) {
+ casecntjson.assign(arg->msg, arg->msg+arg->msg_length);
+ }
+}
+
+int send_http_handshake(int fd, const std::string& reqheader)
+{
+ size_t off = 0;
+ while(off < reqheader.size()) {
+ ssize_t r;
+ size_t len = reqheader.size()-off;
+ while((r = write(fd, reqheader.c_str()+off, len)) == -1 && errno == EINTR);
+ if(r == -1) {
+ perror("write");
+ return -1;
+ }
+ off += r;
+ }
+ return 0;
+}
+
+int recv_http_handshake(int fd, std::string& resheader)
+{
+ char buf[4096];
+ while(1) {
+ ssize_t r;
+ while((r = read(fd, buf, sizeof(buf))) == -1 && errno == EINTR);
+ if(r <= 0) {
+ return -1;
+ }
+ resheader.append(buf, buf+r);
+ if(resheader.find("\r\n\r\n") != std::string::npos) {
+ break;
+ }
+ if(resheader.size() > 8192) {
+ std::cerr << "Too big response header" << std::endl;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+std::string get_random16()
+{
+ char buf[16];
+ std::fstream f("/dev/urandom");
+ f.read(buf, 16);
+ return std::string(buf, buf+16);
+}
+
+int http_handshake(int fd, const char *host, const char *service,
+ const char *path, std::string& body)
+{
+ char buf[4096];
+ std::string client_key = base64(get_random16());
+ snprintf(buf, sizeof(buf),
+ "GET %s HTTP/1.1\r\n"
+ "Host: %s:%s\r\n"
+ "Upgrade: websocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Key: %s\r\n"
+ "Sec-WebSocket-Version: 13\r\n"
+ "\r\n",
+ path, host, service, client_key.c_str());
+ std::string reqheader = buf;
+ if(send_http_handshake(fd, reqheader) == -1) {
+ return -1;
+ }
+ std::string resheader;
+ if(recv_http_handshake(fd, resheader) == -1) {
+ return -1;
+ }
+ std::string::size_type keyhdstart;
+ if((keyhdstart = resheader.find("Sec-WebSocket-Accept: ")) ==
+ std::string::npos) {
+ std::cerr << "http_upgrade: missing required headers" << std::endl;
+ return -1;
+ }
+ keyhdstart += 22;
+ std::string::size_type keyhdend = resheader.find("\r\n", keyhdstart);
+ std::string accept_key = resheader.substr(keyhdstart, keyhdend-keyhdstart);
+ if(accept_key == create_acceptkey(client_key)) {
+ body = resheader.substr(resheader.find("\r\n\r\n")+4);
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+void ctl_epollev(int epollfd, int op, WebSocketClient& ws)
+{
+ epoll_event ev;
+ memset(&ev, 0, sizeof(ev));
+ if(ws.want_read()) {
+ ev.events |= EPOLLIN;
+ }
+ if(ws.want_write()) {
+ ev.events |= EPOLLOUT;
+ }
+ if(epoll_ctl(epollfd, op, ws.fd(), &ev) == -1) {
+ perror("epoll_ctl");
+ exit(EXIT_FAILURE);
+ }
+}
+
+int communicate(const char *host, const char *service, const char *path,
+ const struct wslay_event_callbacks *callbacks)
+{
+ struct wslay_event_callbacks cb = *callbacks;
+ cb.recv_callback = feed_body_callback;
+ int fd = connect_to(host, service);
+ if(fd == -1) {
+ std::cerr << "Could not connect to the host" << std::endl;
+ return -1;
+ }
+ std::string body;
+ if(http_handshake(fd, host, service, path, body) == -1) {
+ std::cerr << "Failed handshake" << std::endl;
+ close(fd);
+ return -1;
+ }
+ make_non_block(fd);
+ int val = 1;
+ if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val))
+ == -1) {
+ perror("setsockopt: TCP_NODELAY");
+ return -1;
+ }
+ WebSocketClient ws(fd, &cb, body);
+ if(ws.on_read_event() == -1) {
+ return -1;
+ }
+ cb.recv_callback = callbacks->recv_callback;
+ ws.set_callbacks(&cb);
+ int epollfd = epoll_create(1);
+ if(epollfd == -1) {
+ perror("epoll_create");
+ return -1;
+ }
+ ctl_epollev(epollfd, EPOLL_CTL_ADD, ws);
+ static const size_t MAX_EVENTS = 1;
+ epoll_event events[MAX_EVENTS];
+ bool ok = true;
+ while(ws.want_read() || ws.want_write()) {
+ int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
+ if(nfds == -1) {
+ perror("epoll_wait");
+ return -1;
+ }
+ for(int n = 0; n < nfds; ++n) {
+ if(((events[n].events & EPOLLIN) && ws.on_read_event() != 0) ||
+ ((events[n].events & EPOLLOUT) && ws.on_write_event() != 0)) {
+ ok = false;
+ break;
+ }
+ }
+ if(!ok) {
+ break;
+ }
+ ctl_epollev(epollfd, EPOLL_CTL_MOD, ws);
+ }
+ return ok ? 0 : -1;
+}
+
+int get_casecnt(const char *host, const char *service)
+{
+ struct wslay_event_callbacks callbacks = {
+ recv_callback,
+ send_callback,
+ genmask_callback,
+ NULL, /* on_frame_recv_start_callback */
+ NULL, /* on_frame_recv_callback */
+ NULL, /* on_frame_recv_end_callback */
+ get_casecnt_on_msg_recv_callback
+ };
+ if(communicate(host, service, "/getCaseCount", &callbacks) == -1) {
+ return -1;
+ }
+ errno = 0;
+ int casecnt = strtol(casecntjson.c_str(), 0, 10);
+ if(errno == ERANGE) {
+ return -1;
+ } else {
+ return casecnt;
+ }
+}
+
+int run_testcase(const char *host, const char *service, int casenum)
+{
+ struct wslay_event_callbacks callbacks = {
+ recv_callback,
+ send_callback,
+ genmask_callback,
+ NULL, /* on_frame_recv_start_callback */
+ NULL, /* on_frame_recv_callback */
+ NULL, /* on_frame_recv_end_callback */
+ on_msg_recv_callback
+ };
+ char buf[1024];
+ snprintf(buf, sizeof(buf), "/runCase?case=%d&agent=wslay", casenum);
+ return communicate(host, service, buf, &callbacks);
+}
+int update_reports(const char *host, const char *service)
+{
+ struct wslay_event_callbacks callbacks = {
+ recv_callback,
+ send_callback,
+ genmask_callback,
+ NULL, /* on_frame_recv_start_callback */
+ NULL, /* on_frame_recv_callback */
+ NULL, /* on_frame_recv_end_callback */
+ NULL, /* on_msg_recv_callback */
+ };
+ return communicate(host, service, "/updateReports?&agent=wslay", &callbacks);
+}
+
+int main(int argc, char **argv)
+{
+ if(argc < 2) {
+ std::cerr << "Usage: " << argv[0] << " HOST SERV" << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ struct sigaction act;
+ memset(&act, 0, sizeof(struct sigaction));
+ act.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &act, 0);
+ const char *host = argv[1];
+ const char *service = argv[2];
+ int casecnt = get_casecnt(host, service);
+ if(casecnt == -1) {
+ std::cerr << "Failed to get case count." << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ for(int i = 1; i <= casecnt; ++i) {
+ std::cout << "Running test case " << i << std::endl;
+ if(run_testcase(host, service, i) == -1) {
+ std::cout << "Detected error during test" << std::endl;
+ }
+ }
+ if(update_reports(host, service) == -1) {
+ std::cerr << "Failed to update reports." << std::endl;
+ exit(EXIT_FAILURE);
+ }
+}
diff --git a/deps/wslay/lib/Makefile.am b/deps/wslay/lib/Makefile.am
new file mode 100644
index 00000000..51516313
--- /dev/null
+++ b/deps/wslay/lib/Makefile.am
@@ -0,0 +1,42 @@
+# Wslay - The WebSocket Library
+
+# Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+SUBDIRS = includes
+
+AM_CFLAGS = -Wall
+AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libwslay.pc
+DISTCLEANFILES = $(pkgconfig_DATA)
+
+lib_LTLIBRARIES = libwslay.la
+
+OBJECTS = wslay_frame.c wslay_event.c\
+ wslay_queue.c wslay_net.c
+
+HFILES = wslay_frame.h wslay_event.h\
+ wslay_queue.h wlsay_net.h
+
+libwslay_la_SOURCES = $(HFILES) $(OBJECTS)
+libwslay_la_LDFLAGS = -no-undefined \
+ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
diff --git a/deps/wslay/lib/includes/Makefile.am b/deps/wslay/lib/includes/Makefile.am
new file mode 100644
index 00000000..3ea5ab65
--- /dev/null
+++ b/deps/wslay/lib/includes/Makefile.am
@@ -0,0 +1,23 @@
+# Wslay - The WebSocket Library
+
+# Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+nobase_include_HEADERS = wslay/wslay.h wslay/wslayver.h
diff --git a/deps/wslay/lib/includes/wslay/wslay.h b/deps/wslay/lib/includes/wslay/wslay.h
new file mode 100644
index 00000000..e4a6ec2f
--- /dev/null
+++ b/deps/wslay/lib/includes/wslay/wslay.h
@@ -0,0 +1,736 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef WSLAY_H
+#define WSLAY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <wslay/wslayver.h>
+
+enum wslay_error {
+ WSLAY_ERR_WANT_READ = -100,
+ WSLAY_ERR_WANT_WRITE = -101,
+ WSLAY_ERR_PROTO = -200,
+ WSLAY_ERR_INVALID_ARGUMENT = -300,
+ WSLAY_ERR_INVALID_CALLBACK = -301,
+ WSLAY_ERR_NO_MORE_MSG = -302,
+ WSLAY_ERR_CALLBACK_FAILURE = -400,
+ WSLAY_ERR_WOULDBLOCK = -401,
+ WSLAY_ERR_NOMEM = -500
+};
+
+/*
+ * Status codes defined in RFC6455
+ */
+enum wslay_status_code {
+ WSLAY_CODE_NORMAL_CLOSURE = 1000,
+ WSLAY_CODE_GOING_AWAY = 1001,
+ WSLAY_CODE_PROTOCOL_ERROR = 1002,
+ WSLAY_CODE_UNSUPPORTED_DATA = 1003,
+ WSLAY_CODE_NO_STATUS_RCVD = 1005,
+ WSLAY_CODE_ABNORMAL_CLOSURE = 1006,
+ WSLAY_CODE_INVALID_FRAME_PAYLOAD_DATA = 1007,
+ WSLAY_CODE_POLICY_VIOLATION = 1008,
+ WSLAY_CODE_MESSAGE_TOO_BIG = 1009,
+ WSLAY_CODE_MANDATORY_EXT = 1010,
+ WSLAY_CODE_INTERNAL_SERVER_ERROR = 1011,
+ WSLAY_CODE_TLS_HANDSHAKE = 1015
+};
+
+enum wslay_io_flags {
+ /*
+ * There is more data to send.
+ */
+ WSLAY_MSG_MORE = 1
+};
+
+/*
+ * Callback function used by wslay_frame_send() function when it needs
+ * to send data. The implementation of this function must send at most
+ * len bytes of data in data. flags is the bitwise OR of zero or more
+ * of the following flag:
+ *
+ * WSLAY_MSG_MORE
+ * There is more data to send
+ *
+ * It provides some hints to tune performance and behaviour. user_data
+ * is one given in wslay_frame_context_init() function. The
+ * implementation of this function must return the number of bytes
+ * sent. If there is an error, return -1. The return value 0 is also
+ * treated an error by the library.
+ */
+typedef ssize_t (*wslay_frame_send_callback)(const uint8_t *data, size_t len,
+ int flags, void *user_data);
+/*
+ * Callback function used by wslay_frame_recv() function when it needs
+ * more data. The implementation of this function must fill at most
+ * len bytes of data into buf. The memory area of buf is allocated by
+ * library and not be freed by the application code. flags is always 0
+ * in this version. user_data is one given in
+ * wslay_frame_context_init() function. The implementation of this
+ * function must return the number of bytes filled. If there is an
+ * error, return -1. The return value 0 is also treated an error by
+ * the library.
+ */
+typedef ssize_t (*wslay_frame_recv_callback)(uint8_t *buf, size_t len,
+ int flags, void *user_data);
+/*
+ * Callback function used by wslay_frame_send() function when it needs
+ * new mask key. The implementation of this function must write
+ * exactly len bytes of mask key to buf. user_data is one given in
+ * wslay_frame_context_init() function. The implementation of this
+ * function return 0 on success. If there is an error, return -1.
+ */
+typedef int (*wslay_frame_genmask_callback)(uint8_t *buf, size_t len,
+ void *user_data);
+
+struct wslay_frame_callbacks {
+ wslay_frame_send_callback send_callback;
+ wslay_frame_recv_callback recv_callback;
+ wslay_frame_genmask_callback genmask_callback;
+};
+
+/*
+ * The opcode defined in RFC6455.
+ */
+enum wslay_opcode {
+ WSLAY_CONTINUATION_FRAME = 0x0u,
+ WSLAY_TEXT_FRAME = 0x1u,
+ WSLAY_BINARY_FRAME = 0x2u,
+ WSLAY_CONNECTION_CLOSE = 0x8u,
+ WSLAY_PING = 0x9u,
+ WSLAY_PONG = 0xau
+};
+
+/*
+ * Macro that returns 1 if opcode is control frame opcode, otherwise
+ * returns 0.
+ */
+#define wslay_is_ctrl_frame(opcode) ((opcode >> 3) & 1)
+
+/*
+ * Macros that returns reserved bits: RSV1, RSV2, RSV3. These macros
+ * assumes that rsv is constructed by ((RSV1 << 2) | (RSV2 << 1) |
+ * RSV3)
+ */
+#define wslay_get_rsv1(rsv) ((rsv >> 2) & 1)
+#define wslay_get_rsv2(rsv) ((rsv >> 1) & 1)
+#define wslay_get_rsv3(rsv) (rsv & 1)
+
+struct wslay_frame_iocb {
+ /* 1 for fragmented final frame, 0 for otherwise */
+ uint8_t fin;
+ /*
+ * reserved 3 bits. rsv = ((RSV1 << 2) | (RSV << 1) | RSV3).
+ * RFC6455 requires 0 unless extensions are negotiated.
+ */
+ uint8_t rsv;
+ /* 4 bit opcode */
+ uint8_t opcode;
+ /* payload length [0, 2**63-1] */
+ uint64_t payload_length;
+ /* 1 for masked frame, 0 for unmasked */
+ uint8_t mask;
+ /* part of payload data */
+ const uint8_t *data;
+ /* bytes of data defined above */
+ size_t data_length;
+};
+
+struct wslay_frame_context;
+typedef struct wslay_frame_context *wslay_frame_context_ptr;
+
+/*
+ * Initializes ctx using given callbacks and user_data. This function
+ * allocates memory for struct wslay_frame_context and stores the
+ * result to *ctx. The callback functions specified in callbacks are
+ * copied to ctx. user_data is stored in ctx and it will be passed to
+ * callback functions. When the user code finished using ctx, it must
+ * call wslay_frame_context_free to deallocate memory.
+ */
+int wslay_frame_context_init(wslay_frame_context_ptr *ctx,
+ const struct wslay_frame_callbacks *callbacks,
+ void *user_data);
+
+/*
+ * Deallocates memory pointed by ctx.
+ */
+void wslay_frame_context_free(wslay_frame_context_ptr ctx);
+
+/*
+ * Send WebSocket frame specified in iocb. ctx must be initialized
+ * using wslay_frame_context_init() function. iocb->fin must be 1 if
+ * this is a fin frame, otherwise 0. iocb->rsv is reserved bits.
+ * iocb->opcode must be the opcode of this frame. iocb->mask must be
+ * 1 if this is masked frame, otherwise 0. iocb->payload_length is
+ * the payload_length of this frame. iocb->data must point to the
+ * payload data to be sent. iocb->data_length must be the length of
+ * the data. This function calls recv_callback function if it needs
+ * to send bytes. This function calls gen_mask_callback function if
+ * it needs new mask key. This function returns the number of payload
+ * bytes sent. Please note that it does not include any number of
+ * header bytes. If it cannot send any single bytes of payload, it
+ * returns WSLAY_ERR_WANT_WRITE. If the library detects error in iocb,
+ * this function returns WSLAY_ERR_INVALID_ARGUMENT. If callback
+ * functions report a failure, this function returns
+ * WSLAY_ERR_INVALID_CALLBACK. This function does not always send all
+ * given data in iocb. If there are remaining data to be sent, adjust
+ * data and data_length in iocb accordingly and call this function
+ * again.
+ */
+ssize_t wslay_frame_send(wslay_frame_context_ptr ctx,
+ struct wslay_frame_iocb *iocb);
+
+/*
+ * Receives WebSocket frame and stores it in iocb. This function
+ * returns the number of payload bytes received. This does not
+ * include header bytes. In this case, iocb will be populated as
+ * follows: iocb->fin is 1 if received frame is fin frame, otherwise
+ * 0. iocb->rsv is reserved bits of received frame. iocb->opcode is
+ * opcode of received frame. iocb->mask is 1 if received frame is
+ * masked, otherwise 0. iocb->payload_length is the payload length of
+ * received frame. iocb->data is pointed to the buffer containing
+ * received payload data. This buffer is allocated by the library and
+ * must be read-only. iocb->data_length is the number of payload
+ * bytes recieved. This function calls recv_callback if it needs to
+ * receive additional bytes. If it cannot receive any single bytes of
+ * payload, it returns WSLAY_ERR_WANT_READ. If the library detects
+ * protocol violation in a received frame, this function returns
+ * WSLAY_ERR_PROTO. If callback functions report a failure, this
+ * function returns WSLAY_ERR_INVALID_CALLBACK. This function does
+ * not always receive whole frame in a single call. If there are
+ * remaining data to be received, call this function again. This
+ * function ensures frame alignment.
+ */
+ssize_t wslay_frame_recv(wslay_frame_context_ptr ctx,
+ struct wslay_frame_iocb *iocb);
+
+struct wslay_event_context;
+/* Pointer to the event-based API context */
+typedef struct wslay_event_context *wslay_event_context_ptr;
+
+struct wslay_event_on_msg_recv_arg {
+ /* reserved bits: rsv = (RSV1 << 2) | (RSV2 << 1) | RSV3 */
+ uint8_t rsv;
+ /* opcode */
+ uint8_t opcode;
+ /* received message */
+ const uint8_t *msg;
+ /* message length */
+ size_t msg_length;
+ /*
+ * Status code iff opcode == WSLAY_CONNECTION_CLOSE. If no status
+ * code is included in the close control frame, it is set to 0.
+ */
+ uint16_t status_code;
+};
+
+/*
+ * Callback function invoked by wslay_event_recv() when a message is
+ * completely received.
+ */
+typedef void (*wslay_event_on_msg_recv_callback)
+(wslay_event_context_ptr ctx,
+ const struct wslay_event_on_msg_recv_arg *arg, void *user_data);
+
+struct wslay_event_on_frame_recv_start_arg {
+ /* fin bit; 1 for final frame, or 0. */
+ uint8_t fin;
+ /* reserved bits: rsv = (RSV1 << 2) | (RSV2 << 1) | RSV3 */
+ uint8_t rsv;
+ /* opcode of the frame */
+ uint8_t opcode;
+ /* payload length of ths frame */
+ uint64_t payload_length;
+};
+
+/*
+ * Callback function invoked by wslay_event_recv() when a new frame
+ * starts to be received. This callback function is only invoked once
+ * for each frame.
+ */
+typedef void (*wslay_event_on_frame_recv_start_callback)
+(wslay_event_context_ptr ctx,
+ const struct wslay_event_on_frame_recv_start_arg *arg, void *user_data);
+
+struct wslay_event_on_frame_recv_chunk_arg {
+ /* chunk of payload data */
+ const uint8_t *data;
+ /* length of data */
+ size_t data_length;
+};
+
+/*
+ * Callback function invoked by wslay_event_recv() when a chunk of
+ * frame payload is received.
+ */
+typedef void (*wslay_event_on_frame_recv_chunk_callback)
+(wslay_event_context_ptr ctx,
+ const struct wslay_event_on_frame_recv_chunk_arg *arg, void *user_data);
+
+/*
+ * Callback function invoked by wslay_event_recv() when a frame is
+ * completely received.
+ */
+typedef void (*wslay_event_on_frame_recv_end_callback)
+(wslay_event_context_ptr ctx, void *user_data);
+
+/*
+ * Callback function invoked by wslay_event_recv() when it wants to
+ * receive more data from peer. The implementation of this callback
+ * function must read data at most len bytes from peer and store them
+ * in buf and return the number of bytes read. flags is always 0 in
+ * this version.
+ *
+ * If there is an error, return -1 and set error code
+ * WSLAY_ERR_CALLBACK_FAILURE using wslay_event_set_error(). Wslay
+ * event-based API on the whole assumes non-blocking I/O. If the cause
+ * of error is EAGAIN or EWOULDBLOCK, set WSLAY_ERR_WOULDBLOCK
+ * instead. This is important because it tells wslay_event_recv() to
+ * stop receiving further data and return.
+ */
+typedef ssize_t (*wslay_event_recv_callback)(wslay_event_context_ptr ctx,
+ uint8_t *buf, size_t len,
+ int flags, void *user_data);
+
+/*
+ * Callback function invoked by wslay_event_send() when it wants to
+ * send more data to peer. The implementation of this callback
+ * function must send data at most len bytes to peer and return the
+ * number of bytes sent. flags is the bitwise OR of zero or more of
+ * the following flag:
+ *
+ * WSLAY_MSG_MORE
+ * There is more data to send
+ *
+ * It provides some hints to tune performance and behaviour.
+ *
+ * If there is an error, return -1 and set error code
+ * WSLAY_ERR_CALLBACK_FAILURE using wslay_event_set_error(). Wslay
+ * event-based API on the whole assumes non-blocking I/O. If the cause
+ * of error is EAGAIN or EWOULDBLOCK, set WSLAY_ERR_WOULDBLOCK
+ * instead. This is important because it tells wslay_event_send() to
+ * stop sending data and return.
+ */
+typedef ssize_t (*wslay_event_send_callback)(wslay_event_context_ptr ctx,
+ const uint8_t *data, size_t len,
+ int flags, void *user_data);
+
+/*
+ * Callback function invoked by wslay_event_send() when it wants new
+ * mask key. As described in RFC6455, only the traffic from WebSocket
+ * client is masked, so this callback function is only needed if an
+ * event-based API is initialized for WebSocket client use.
+ */
+typedef int (*wslay_event_genmask_callback)(wslay_event_context_ptr ctx,
+ uint8_t *buf, size_t len,
+ void *user_data);
+
+struct wslay_event_callbacks {
+ wslay_event_recv_callback recv_callback;
+ wslay_event_send_callback send_callback;
+ wslay_event_genmask_callback genmask_callback;
+ wslay_event_on_frame_recv_start_callback on_frame_recv_start_callback;
+ wslay_event_on_frame_recv_chunk_callback on_frame_recv_chunk_callback;
+ wslay_event_on_frame_recv_end_callback on_frame_recv_end_callback;
+ wslay_event_on_msg_recv_callback on_msg_recv_callback;
+};
+
+/*
+ * Initializes ctx as WebSocket Server. user_data is an arbitrary
+ * pointer, which is directly passed to each callback functions as
+ * user_data argument.
+ *
+ * On success, returns 0. On error, returns one of following negative
+ * values:
+ *
+ * WSLAY_ERR_NOMEM
+ * Out of memory.
+ */
+int wslay_event_context_server_init
+(wslay_event_context_ptr *ctx,
+ const struct wslay_event_callbacks *callbacks, void *user_data);
+
+/*
+ * Initializes ctx as WebSocket client. user_data is an arbitrary
+ * pointer, which is directly passed to each callback functions as
+ * user_data argument.
+ *
+ * On success, returns 0. On error, returns one of following negative
+ * values:
+ *
+ * WSLAY_ERR_NOMEM
+ * Out of memory.
+ */
+int wslay_event_context_client_init
+(wslay_event_context_ptr *ctx,
+ const struct wslay_event_callbacks *callbacks, void *user_data);
+
+/*
+ * Releases allocated resources for ctx.
+ */
+void wslay_event_context_free(wslay_event_context_ptr ctx);
+
+/*
+ * Enables or disables buffering of an entire message for non-control
+ * frames. If val is 0, buffering is enabled. Otherwise, buffering is
+ * disabled. If wslay_event_on_msg_recv_callback is invoked when
+ * buffering is disabled, the msg_length member of struct
+ * wslay_event_on_msg_recv_arg is set to 0.
+ *
+ * The control frames are always buffered regardless of this function call.
+ *
+ * This function must not be used after the first invocation of
+ * wslay_event_recv() function.
+ */
+void wslay_event_config_set_no_buffering(wslay_event_context_ptr ctx, int val);
+
+/*
+ * Sets maximum length of a message that can be received. The length
+ * of message is checked by wslay_event_recv() function. If the length
+ * of a message is larger than this value, reading operation is
+ * disabled (same effect with wslay_event_shutdown_read() call) and
+ * close control frame with WSLAY_CODE_MESSAGE_TOO_BIG is queued. If
+ * buffering for non-control frames is disabled, the library checks
+ * each frame payload length and does not check length of entire
+ * message.
+ *
+ * The default value is (1u << 31)-1.
+ */
+void wslay_event_config_set_max_recv_msg_length(wslay_event_context_ptr ctx,
+ uint64_t val);
+
+/*
+ * Sets callbacks to ctx. The callbacks previouly set by this function
+ * or wslay_event_context_server_init() or
+ * wslay_event_context_client_init() are replaced with callbacks.
+ */
+void wslay_event_config_set_callbacks
+(wslay_event_context_ptr ctx, const struct wslay_event_callbacks *callbacks);
+
+/*
+ * Receives messages from peer. When receiving
+ * messages, it uses wslay_event_recv_callback function. Single call
+ * of this function receives multiple messages until
+ * wslay_event_recv_callback function sets error code
+ * WSLAY_ERR_WOULDBLOCK.
+ *
+ * When close control frame is received, this function automatically
+ * queues close control frame. Also this function calls
+ * wslay_event_set_read_enabled() with second argument 0 to disable
+ * further read from peer.
+ *
+ * When ping control frame is received, this function automatically
+ * queues pong control frame.
+ *
+ * In case of a fatal errror which leads to negative return code, this
+ * function calls wslay_event_set_read_enabled() with second argument
+ * 0 to disable further read from peer.
+ *
+ * wslay_event_recv() returns 0 if it succeeds, or one of the
+ * following negative error codes:
+ *
+ * WSLAY_ERR_CALLBACK_FAILURE
+ * User defined callback function is failed.
+ *
+ * WSLAY_ERR_NOMEM
+ * Out of memory.
+ *
+ * When negative error code is returned, application must not make any
+ * further call of wslay_event_recv() and must close WebSocket
+ * connection.
+ */
+int wslay_event_recv(wslay_event_context_ptr ctx);
+
+/*
+ * Sends queued messages to peer. When sending a
+ * message, it uses wslay_event_send_callback function. Single call of
+ * wslay_event_send() sends multiple messages until
+ * wslay_event_send_callback sets error code WSLAY_ERR_WOULDBLOCK.
+ *
+ * If ctx is initialized for WebSocket client use, wslay_event_send()
+ * uses wslay_event_genmask_callback to get new mask key.
+ *
+ * When a message queued using wslay_event_queue_fragmented_msg() is
+ * sent, wslay_event_send() invokes
+ * wslay_event_fragmented_msg_callback for that message.
+ *
+ * After close control frame is sent, this function calls
+ * wslay_event_set_write_enabled() with second argument 0 to disable
+ * further transmission to peer.
+ *
+ * If there are any pending messages, wslay_event_want_write() returns
+ * 1, otherwise returns 0.
+ *
+ * In case of a fatal errror which leads to negative return code, this
+ * function calls wslay_event_set_write_enabled() with second argument
+ * 0 to disable further transmission to peer.
+ *
+ * wslay_event_send() returns 0 if it succeeds, or one of the
+ * following negative error codes:
+ *
+ * WSLAY_ERR_CALLBACK_FAILURE
+ * User defined callback function is failed.
+ *
+ * WSLAY_ERR_NOMEM
+ * Out of memory.
+ *
+ * When negative error code is returned, application must not make any
+ * further call of wslay_event_send() and must close WebSocket
+ * connection.
+ */
+int wslay_event_send(wslay_event_context_ptr ctx);
+
+struct wslay_event_msg {
+ uint8_t opcode;
+ const uint8_t *msg;
+ size_t msg_length;
+};
+
+/*
+ * Queues message specified in arg.
+ *
+ * This function supports both control and non-control messages and
+ * the given message is sent without fragmentation. If fragmentation
+ * is needed, use wslay_event_queue_fragmented_msg() function instead.
+ *
+ * This function just queues a message and does not send
+ * it. wslay_event_send() function call sends these queued messages.
+ *
+ * wslay_event_queue_msg() returns 0 if it succeeds, or returns the
+ * following negative error codes:
+ *
+ * WSLAY_ERR_NO_MORE_MSG
+ * Could not queue given message. The one of possible reason is that
+ * close control frame has been queued/sent and no further queueing
+ * message is not allowed.
+ *
+ * WSLAY_ERR_INVALID_ARGUMENT
+ * The given message is invalid.
+ *
+ * WSLAY_ERR_NOMEM
+ * Out of memory.
+ */
+int wslay_event_queue_msg(wslay_event_context_ptr ctx,
+ const struct wslay_event_msg *arg);
+
+/*
+ * Specify "source" to generate message.
+ */
+union wslay_event_msg_source {
+ int fd;
+ void *data;
+};
+
+/*
+ * Callback function called by wslay_event_send() to read message data
+ * from source. The implementation of
+ * wslay_event_fragmented_msg_callback must store at most len bytes of
+ * data to buf and return the number of stored bytes. If all data is
+ * read (i.e., EOF), set *eof to 1. If no data can be generated at the
+ * moment, return 0. If there is an error, return -1 and set error
+ * code WSLAY_ERR_CALLBACK_FAILURE using wslay_event_set_error().
+ */
+typedef ssize_t (*wslay_event_fragmented_msg_callback)
+(wslay_event_context_ptr ctx,
+ uint8_t *buf, size_t len, const union wslay_event_msg_source *source,
+ int *eof, void *user_data);
+
+struct wslay_event_fragmented_msg {
+ /* opcode */
+ uint8_t opcode;
+ /* "source" to generate message data */
+ union wslay_event_msg_source source;
+ /* Callback function to read message data from source. */
+ wslay_event_fragmented_msg_callback read_callback;
+};
+
+/*
+ * Queues a fragmented message specified in arg.
+ *
+ * This function supports non-control messages only. For control frames,
+ * use wslay_event_queue_msg() or wslay_event_queue_close().
+ *
+ * This function just queues a message and does not send
+ * it. wslay_event_send() function call sends these queued messages.
+ *
+ * wslay_event_queue_fragmented_msg() returns 0 if it succeeds, or
+ * returns the following negative error codes:
+ *
+ * WSLAY_ERR_NO_MORE_MSG
+ * Could not queue given message. The one of possible reason is that
+ * close control frame has been queued/sent and no further queueing
+ * message is not allowed.
+ *
+ * WSLAY_ERR_INVALID_ARGUMENT
+ * The given message is invalid.
+ *
+ * WSLAY_ERR_NOMEM
+ * Out of memory.
+ */
+int wslay_event_queue_fragmented_msg
+(wslay_event_context_ptr ctx, const struct wslay_event_fragmented_msg *arg);
+
+/*
+ * Queues close control frame. This function is provided just for
+ * convenience. wslay_event_queue_msg() can queue a close control
+ * frame as well. status_code is the status code of close control
+ * frame. reason is the close reason encoded in UTF-8. reason_length
+ * is the length of reason in bytes. reason_length must be less than
+ * 123 bytes.
+ *
+ * If status_code is 0, reason and reason_length is not used and close
+ * control frame with zero-length payload will be queued.
+ *
+ * This function just queues a message and does not send
+ * it. wslay_event_send() function call sends these queued messages.
+ *
+ * wslay_event_queue_close() returns 0 if it succeeds, or returns the
+ * following negative error codes:
+ *
+ * WSLAY_ERR_NO_MORE_MSG
+ * Could not queue given message. The one of possible reason is that
+ * close control frame has been queued/sent and no further queueing
+ * message is not allowed.
+ *
+ * WSLAY_ERR_INVALID_ARGUMENT
+ * The given message is invalid.
+ *
+ * WSLAY_ERR_NOMEM
+ * Out of memory.
+ */
+int wslay_event_queue_close(wslay_event_context_ptr ctx,
+ uint16_t status_code,
+ const uint8_t *reason, size_t reason_length);
+
+/*
+ * Sets error code to tell the library there is an error. This
+ * function is typically used in user defined callback functions. See
+ * the description of callback function to know which error code
+ * should be used.
+ */
+void wslay_event_set_error(wslay_event_context_ptr ctx, int val);
+
+/*
+ * Query whehter the library want to read more data from peer.
+ *
+ * wslay_event_want_read() returns 1 if the library want to read more
+ * data from peer, or returns 0.
+ */
+int wslay_event_want_read(wslay_event_context_ptr ctx);
+
+/*
+ * Query whehter the library want to send more data to peer.
+ *
+ * wslay_event_want_write() returns 1 if the library want to send more
+ * data to peer, or returns 0.
+ */
+int wslay_event_want_write(wslay_event_context_ptr ctx);
+
+/*
+ * Prevents the event-based API context from reading any further data
+ * from peer.
+ *
+ * This function may be used with wslay_event_queue_close() if the
+ * application detects error in the data received and wants to fail
+ * WebSocket connection.
+ */
+void wslay_event_shutdown_read(wslay_event_context_ptr ctx);
+
+/*
+ * Prevents the event-based API context from sending any further data
+ * to peer.
+ */
+void wslay_event_shutdown_write(wslay_event_context_ptr ctx);
+
+/*
+ * Returns 1 if the event-based API context allows read operation, or
+ * return 0.
+ *
+ * After wslay_event_shutdown_read() is called,
+ * wslay_event_get_read_enabled() returns 0.
+ */
+int wslay_event_get_read_enabled(wslay_event_context_ptr ctx);
+
+/*
+ * Returns 1 if the event-based API context allows write operation, or
+ * return 0.
+ *
+ * After wslay_event_shutdown_write() is called,
+ * wslay_event_get_write_enabled() returns 0.
+ */
+int wslay_event_get_write_enabled(wslay_event_context_ptr ctx);
+
+/*
+ * Returns 1 if a close control frame has been received from peer, or
+ * returns 0.
+ */
+int wslay_event_get_close_received(wslay_event_context_ptr ctx);
+
+/*
+ * Returns 1 if a close control frame has been sent to peer, or
+ * returns 0.
+ */
+int wslay_event_get_close_sent(wslay_event_context_ptr ctx);
+
+/*
+ * Returns status code received in close control frame. If no close
+ * control frame has not been received, returns
+ * WSLAY_CODE_ABNORMAL_CLOSURE. If received close control frame has no
+ * status code, returns WSLAY_CODE_NO_STATUS_RCVD.
+ */
+uint16_t wslay_event_get_status_code_received(wslay_event_context_ptr ctx);
+
+/*
+ * Returns status code sent in close control frame. If no close
+ * control frame has not been sent, returns
+ * WSLAY_CODE_ABNORMAL_CLOSURE. If sent close control frame has no
+ * status code, returns WSLAY_CODE_NO_STATUS_RCVD.
+ */
+uint16_t wslay_event_get_status_code_sent(wslay_event_context_ptr ctx);
+
+/*
+ * Returns the number of queued messages.
+ */
+size_t wslay_event_get_queued_msg_count(wslay_event_context_ptr ctx);
+
+/*
+ * Returns the sum of queued message length. It only counts the
+ * message length queued using wslay_event_queue_msg() or
+ * wslay_event_queue_close().
+ */
+size_t wslay_event_get_queued_msg_length(wslay_event_context_ptr ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WSLAY_H */
diff --git a/deps/wslay/lib/includes/wslay/wslayver.h.in b/deps/wslay/lib/includes/wslay/wslayver.h.in
new file mode 100644
index 00000000..f56059fb
--- /dev/null
+++ b/deps/wslay/lib/includes/wslay/wslayver.h.in
@@ -0,0 +1,31 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef WSLAYVER_H
+#define WSLAYVER_H
+
+/* Version number of wslay release */
+#define WSLAY_VERSION "@PACKAGE_VERSION@"
+
+#endif /* WSLAYVER_H */
diff --git a/deps/wslay/lib/libwslay.pc.in b/deps/wslay/lib/libwslay.pc.in
new file mode 100644
index 00000000..e5a14409
--- /dev/null
+++ b/deps/wslay/lib/libwslay.pc.in
@@ -0,0 +1,33 @@
+# Wslay - The WebSocket Library
+
+# Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: Wslay
+Description: Low-level WebSockets library
+URL: http://wslay.sourceforge.net/
+Version: @VERSION@
+Libs: -L${libdir} -lwslay
+Cflags: -I${includedir}
diff --git a/deps/wslay/lib/wslay_event.c b/deps/wslay/lib/wslay_event.c
new file mode 100644
index 00000000..a01c9df1
--- /dev/null
+++ b/deps/wslay/lib/wslay_event.c
@@ -0,0 +1,984 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "wslay_event.h"
+
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "wslay_queue.h"
+#include "wslay_frame.h"
+#include "wslay_net.h"
+/* Start of utf8 dfa */
+/* Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
+ * See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
+ *
+ * Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#define UTF8_ACCEPT 0
+#define UTF8_REJECT 12
+
+static const uint8_t utf8d[] = {
+ /*
+ * The first part of the table maps bytes to character classes that
+ * to reduce the size of the transition table and create bitmasks.
+ */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
+
+ /*
+ * The second part is a transition table that maps a combination
+ * of a state of the automaton and a character class to a state.
+ */
+ 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
+ 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
+ 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
+ 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
+ 12,36,12,12,12,12,12,12,12,12,12,12,
+};
+
+static uint32_t
+decode(uint32_t* state, uint32_t* codep, uint32_t byte) {
+ uint32_t type = utf8d[byte];
+
+ *codep = (*state != UTF8_ACCEPT) ?
+ (byte & 0x3fu) | (*codep << 6) :
+ (0xff >> type) & (byte);
+
+ *state = utf8d[256 + *state + type];
+ return *state;
+}
+
+/* End of utf8 dfa */
+
+static ssize_t wslay_event_frame_recv_callback(uint8_t *buf, size_t len,
+ int flags, void *user_data)
+{
+ struct wslay_event_frame_user_data *e =
+ (struct wslay_event_frame_user_data*)user_data;
+ return e->ctx->callbacks.recv_callback(e->ctx, buf, len, flags, e->user_data);
+}
+
+static ssize_t wslay_event_frame_send_callback(const uint8_t *data, size_t len,
+ int flags, void *user_data)
+{
+ struct wslay_event_frame_user_data *e =
+ (struct wslay_event_frame_user_data*)user_data;
+ return e->ctx->callbacks.send_callback(e->ctx, data, len, flags,
+ e->user_data);
+}
+
+static int wslay_event_frame_genmask_callback(uint8_t *buf, size_t len,
+ void *user_data)
+{
+ struct wslay_event_frame_user_data *e =
+ (struct wslay_event_frame_user_data*)user_data;
+ return e->ctx->callbacks.genmask_callback(e->ctx, buf, len, e->user_data);
+}
+
+static int wslay_event_byte_chunk_init
+(struct wslay_event_byte_chunk **chunk, size_t len)
+{
+ *chunk = (struct wslay_event_byte_chunk*)malloc
+ (sizeof(struct wslay_event_byte_chunk));
+ if(*chunk == NULL) {
+ return WSLAY_ERR_NOMEM;
+ }
+ memset(*chunk, 0, sizeof(struct wslay_event_byte_chunk));
+ if(len) {
+ (*chunk)->data = (uint8_t*)malloc(len);
+ (*chunk)->data_length = len;
+ }
+ return 0;
+}
+
+static void wslay_event_byte_chunk_free(struct wslay_event_byte_chunk *c)
+{
+ if(!c) {
+ return;
+ }
+ free(c->data);
+ free(c);
+}
+
+static void wslay_event_byte_chunk_copy(struct wslay_event_byte_chunk *c,
+ size_t off,
+ const uint8_t *data, size_t data_length)
+{
+ memcpy(c->data+off, data, data_length);
+}
+
+static void wslay_event_imsg_set(struct wslay_event_imsg *m,
+ uint8_t fin, uint8_t rsv, uint8_t opcode)
+{
+ m->fin = fin;
+ m->rsv = rsv;
+ m->opcode = opcode;
+ m->msg_length = 0;
+}
+
+static void wslay_event_imsg_chunks_free(struct wslay_event_imsg *m)
+{
+ if(!m->chunks) {
+ return;
+ }
+ while(!wslay_queue_empty(m->chunks)) {
+ wslay_event_byte_chunk_free(wslay_queue_top(m->chunks));
+ wslay_queue_pop(m->chunks);
+ }
+}
+
+static void wslay_event_imsg_reset(struct wslay_event_imsg *m)
+{
+ m->opcode = 0xffu;
+ m->utf8state = UTF8_ACCEPT;
+ wslay_event_imsg_chunks_free(m);
+}
+
+static int wslay_event_imsg_append_chunk(struct wslay_event_imsg *m, size_t len)
+{
+ if(len == 0) {
+ return 0;
+ } else {
+ int r;
+ struct wslay_event_byte_chunk *chunk;
+ if((r = wslay_event_byte_chunk_init(&chunk, len)) != 0) {
+ return r;
+ }
+ if((r = wslay_queue_push(m->chunks, chunk)) != 0) {
+ return r;
+ }
+ m->msg_length += len;
+ return 0;
+ }
+}
+
+static int wslay_event_omsg_non_fragmented_init
+(struct wslay_event_omsg **m, uint8_t opcode,
+ const uint8_t *msg, size_t msg_length)
+{
+ *m = (struct wslay_event_omsg*)malloc(sizeof(struct wslay_event_omsg));
+ if(!*m) {
+ return WSLAY_ERR_NOMEM;
+ }
+ memset(*m, 0, sizeof(struct wslay_event_omsg));
+ (*m)->fin = 1;
+ (*m)->opcode = opcode;
+ (*m)->type = WSLAY_NON_FRAGMENTED;
+ if(msg_length) {
+ (*m)->data = (uint8_t*)malloc(msg_length);
+ if(!(*m)->data) {
+ free(*m);
+ return WSLAY_ERR_NOMEM;
+ }
+ memcpy((*m)->data, msg, msg_length);
+ (*m)->data_length = msg_length;
+ }
+ return 0;
+}
+
+static int wslay_event_omsg_fragmented_init
+(struct wslay_event_omsg **m, uint8_t opcode,
+ const union wslay_event_msg_source source,
+ wslay_event_fragmented_msg_callback read_callback)
+{
+ *m = (struct wslay_event_omsg*)malloc(sizeof(struct wslay_event_omsg));
+ if(!*m) {
+ return WSLAY_ERR_NOMEM;
+ }
+ memset(*m, 0, sizeof(struct wslay_event_omsg));
+ (*m)->opcode = opcode;
+ (*m)->type = WSLAY_FRAGMENTED;
+ (*m)->source = source;
+ (*m)->read_callback = read_callback;
+ return 0;
+}
+
+static void wslay_event_omsg_free(struct wslay_event_omsg *m)
+{
+ if(!m) {
+ return;
+ }
+ free(m->data);
+ free(m);
+}
+
+static uint8_t* wslay_event_flatten_queue(struct wslay_queue *queue, size_t len)
+{
+ if(len == 0) {
+ return NULL;
+ } else {
+ size_t off = 0;
+ uint8_t *buf = (uint8_t*)malloc(len);
+ if(!buf) {
+ return NULL;
+ }
+ while(!wslay_queue_empty(queue)) {
+ struct wslay_event_byte_chunk *chunk = wslay_queue_top(queue);
+ memcpy(buf+off, chunk->data, chunk->data_length);
+ off += chunk->data_length;
+ wslay_event_byte_chunk_free(chunk);
+ wslay_queue_pop(queue);
+ assert(off <= len);
+ }
+ assert(len == off);
+ return buf;
+ }
+}
+
+static int wslay_event_is_msg_queueable(wslay_event_context_ptr ctx)
+{
+ return ctx->write_enabled && (ctx->close_status & WSLAY_CLOSE_QUEUED) == 0;
+}
+
+int wslay_event_queue_close(wslay_event_context_ptr ctx, uint16_t status_code,
+ const uint8_t *reason, size_t reason_length)
+{
+ if(!wslay_event_is_msg_queueable(ctx)) {
+ return WSLAY_ERR_NO_MORE_MSG;
+ } else if(reason_length > 123) {
+ return WSLAY_ERR_INVALID_ARGUMENT;
+ } else {
+ uint8_t msg[128];
+ size_t msg_length;
+ struct wslay_event_msg arg;
+ uint16_t ncode;
+ int r;
+ if(status_code == 0) {
+ msg_length = 0;
+ } else {
+ ncode = htons(status_code);
+ memcpy(msg, &ncode, 2);
+ memcpy(msg+2, reason, reason_length);
+ msg_length = reason_length+2;
+ }
+ arg.opcode = WSLAY_CONNECTION_CLOSE;
+ arg.msg = msg;
+ arg.msg_length = msg_length;
+ r = wslay_event_queue_msg(ctx, &arg);
+ if(r == 0) {
+ ctx->close_status |= WSLAY_CLOSE_QUEUED;
+ }
+ return r;
+ }
+}
+
+static int wslay_event_queue_close_wrapper
+(wslay_event_context_ptr ctx, uint16_t status_code,
+ const uint8_t *reason, size_t reason_length)
+{
+ int r;
+ ctx->read_enabled = 0;
+ if((r = wslay_event_queue_close(ctx, status_code, reason, reason_length)) &&
+ r != WSLAY_ERR_NO_MORE_MSG) {
+ return r;
+ }
+ return 0;
+}
+
+int wslay_event_queue_msg(wslay_event_context_ptr ctx,
+ const struct wslay_event_msg *arg)
+{
+ int r;
+ struct wslay_event_omsg *omsg;
+ if(!wslay_event_is_msg_queueable(ctx)) {
+ return WSLAY_ERR_NO_MORE_MSG;
+ }
+ if(wslay_is_ctrl_frame(arg->opcode) && arg->msg_length > 125) {
+ return WSLAY_ERR_INVALID_ARGUMENT;
+ }
+ if((r = wslay_event_omsg_non_fragmented_init
+ (&omsg, arg->opcode, arg->msg, arg->msg_length)) != 0) {
+ return r;
+ }
+ if(wslay_is_ctrl_frame(arg->opcode)) {
+ if((r = wslay_queue_push(ctx->send_ctrl_queue, omsg)) != 0) {
+ return r;
+ }
+ } else {
+ if((r = wslay_queue_push(ctx->send_queue, omsg)) != 0) {
+ return r;
+ }
+ }
+ ++ctx->queued_msg_count;
+ ctx->queued_msg_length += arg->msg_length;
+ return 0;
+}
+
+int wslay_event_queue_fragmented_msg
+(wslay_event_context_ptr ctx, const struct wslay_event_fragmented_msg *arg)
+{
+ int r;
+ struct wslay_event_omsg *omsg;
+ if(!wslay_event_is_msg_queueable(ctx)) {
+ return WSLAY_ERR_NO_MORE_MSG;
+ }
+ if(wslay_is_ctrl_frame(arg->opcode)) {
+ return WSLAY_ERR_INVALID_ARGUMENT;
+ }
+ if((r = wslay_event_omsg_fragmented_init
+ (&omsg, arg->opcode, arg->source, arg->read_callback)) != 0) {
+ return r;
+ }
+ if((r = wslay_queue_push(ctx->send_queue, omsg)) != 0) {
+ return r;
+ }
+ ++ctx->queued_msg_count;
+ return 0;
+}
+
+void wslay_event_config_set_callbacks
+(wslay_event_context_ptr ctx, const struct wslay_event_callbacks *callbacks)
+{
+ ctx->callbacks = *callbacks;
+}
+
+static int wslay_event_context_init
+(wslay_event_context_ptr *ctx,
+ const struct wslay_event_callbacks *callbacks,
+ void *user_data)
+{
+ int i, r;
+ struct wslay_frame_callbacks frame_callbacks = {
+ wslay_event_frame_send_callback,
+ wslay_event_frame_recv_callback,
+ wslay_event_frame_genmask_callback
+ };
+ *ctx = (wslay_event_context_ptr)malloc(sizeof(struct wslay_event_context));
+ if(!*ctx) {
+ return WSLAY_ERR_NOMEM;
+ }
+ memset(*ctx, 0, sizeof(struct wslay_event_context));
+ wslay_event_config_set_callbacks(*ctx, callbacks);
+ (*ctx)->user_data = user_data;
+ (*ctx)->frame_user_data.ctx = *ctx;
+ (*ctx)->frame_user_data.user_data = user_data;
+ if((r = wslay_frame_context_init(&(*ctx)->frame_ctx, &frame_callbacks,
+ &(*ctx)->frame_user_data)) != 0) {
+ wslay_event_context_free(*ctx);
+ return r;
+ }
+ (*ctx)->read_enabled = (*ctx)->write_enabled = 1;
+ (*ctx)->send_queue = wslay_queue_new();
+ if(!(*ctx)->send_queue) {
+ wslay_event_context_free(*ctx);
+ return WSLAY_ERR_NOMEM;
+ }
+ (*ctx)->send_ctrl_queue = wslay_queue_new();
+ if(!(*ctx)->send_ctrl_queue) {
+ wslay_event_context_free(*ctx);
+ return WSLAY_ERR_NOMEM;
+ }
+ (*ctx)->queued_msg_count = 0;
+ (*ctx)->queued_msg_length = 0;
+ for(i = 0; i < 2; ++i) {
+ wslay_event_imsg_reset(&(*ctx)->imsgs[i]);
+ (*ctx)->imsgs[i].chunks = wslay_queue_new();
+ if(!(*ctx)->imsgs[i].chunks) {
+ wslay_event_context_free(*ctx);
+ return WSLAY_ERR_NOMEM;
+ }
+ }
+ (*ctx)->imsg = &(*ctx)->imsgs[0];
+ (*ctx)->obufmark = (*ctx)->obuflimit = (*ctx)->obuf;
+ (*ctx)->status_code_sent = WSLAY_CODE_ABNORMAL_CLOSURE;
+ (*ctx)->status_code_recv = WSLAY_CODE_ABNORMAL_CLOSURE;
+ (*ctx)->max_recv_msg_length = (1u << 31)-1;
+ return 0;
+}
+
+int wslay_event_context_server_init
+(wslay_event_context_ptr *ctx,
+ const struct wslay_event_callbacks *callbacks,
+ void *user_data)
+{
+ int r;
+ if((r = wslay_event_context_init(ctx, callbacks, user_data)) != 0) {
+ return r;
+ }
+ (*ctx)->server = 1;
+ return 0;
+}
+
+int wslay_event_context_client_init
+(wslay_event_context_ptr *ctx,
+ const struct wslay_event_callbacks *callbacks,
+ void *user_data)
+{
+ int r;
+ if((r = wslay_event_context_init(ctx, callbacks, user_data)) != 0) {
+ return r;
+ }
+ (*ctx)->server = 0;
+ return 0;
+}
+
+void wslay_event_context_free(wslay_event_context_ptr ctx)
+{
+ int i;
+ if(!ctx) {
+ return;
+ }
+ for(i = 0; i < 2; ++i) {
+ wslay_event_imsg_chunks_free(&ctx->imsgs[i]);
+ wslay_queue_free(ctx->imsgs[i].chunks);
+ }
+ if(ctx->send_queue) {
+ while(!wslay_queue_empty(ctx->send_queue)) {
+ wslay_event_omsg_free(wslay_queue_top(ctx->send_queue));
+ wslay_queue_pop(ctx->send_queue);
+ }
+ wslay_queue_free(ctx->send_queue);
+ }
+ if(ctx->send_ctrl_queue) {
+ while(!wslay_queue_empty(ctx->send_ctrl_queue)) {
+ wslay_event_omsg_free(wslay_queue_top(ctx->send_ctrl_queue));
+ wslay_queue_pop(ctx->send_ctrl_queue);
+ }
+ wslay_queue_free(ctx->send_ctrl_queue);
+ }
+ wslay_frame_context_free(ctx->frame_ctx);
+ wslay_event_omsg_free(ctx->omsg);
+ free(ctx);
+}
+
+static void wslay_event_call_on_frame_recv_start_callback
+(wslay_event_context_ptr ctx, const struct wslay_frame_iocb *iocb)
+{
+ if(ctx->callbacks.on_frame_recv_start_callback) {
+ struct wslay_event_on_frame_recv_start_arg arg;
+ arg.fin = iocb->fin;
+ arg.rsv = iocb->rsv;
+ arg.opcode = iocb->opcode;
+ arg.payload_length = iocb->payload_length;
+ ctx->callbacks.on_frame_recv_start_callback(ctx, &arg, ctx->user_data);
+ }
+}
+
+static void wslay_event_call_on_frame_recv_chunk_callback
+(wslay_event_context_ptr ctx, const struct wslay_frame_iocb *iocb)
+{
+ if(ctx->callbacks.on_frame_recv_chunk_callback) {
+ struct wslay_event_on_frame_recv_chunk_arg arg = {
+ iocb->data, iocb->data_length
+ };
+ ctx->callbacks.on_frame_recv_chunk_callback(ctx, &arg, ctx->user_data);
+ }
+};
+
+static void wslay_event_call_on_frame_recv_end_callback
+(wslay_event_context_ptr ctx)
+{
+ if(ctx->callbacks.on_frame_recv_end_callback) {
+ ctx->callbacks.on_frame_recv_end_callback(ctx, ctx->user_data);
+ }
+}
+
+static int wslay_event_is_valid_status_code(uint16_t status_code)
+{
+ return (1000 <= status_code && status_code <= 1011 &&
+ status_code != 1004 && status_code != 1005 && status_code != 1006) ||
+ (3000 <= status_code && status_code <= 4999);
+}
+
+static int wslay_event_config_get_no_buffering(wslay_event_context_ptr ctx)
+{
+ return (ctx->config & WSLAY_CONFIG_NO_BUFFERING) > 0;
+}
+
+int wslay_event_recv(wslay_event_context_ptr ctx)
+{
+ struct wslay_frame_iocb iocb;
+ ssize_t r;
+ while(ctx->read_enabled) {
+ memset(&iocb, 0, sizeof(iocb));
+ r = wslay_frame_recv(ctx->frame_ctx, &iocb);
+ if(r >= 0) {
+ int new_frame = 0;
+ /* We only allow rsv == 0 ATM. */
+ if(iocb.rsv != 0 ||
+ ((ctx->server && !iocb.mask) || (!ctx->server && iocb.mask))) {
+ if((r = wslay_event_queue_close_wrapper
+ (ctx, WSLAY_CODE_PROTOCOL_ERROR, NULL, 0)) != 0) {
+ return r;
+ }
+ break;
+ }
+ if(ctx->imsg->opcode == 0xffu) {
+ if(iocb.opcode == WSLAY_TEXT_FRAME ||
+ iocb.opcode == WSLAY_BINARY_FRAME ||
+ iocb.opcode == WSLAY_CONNECTION_CLOSE ||
+ iocb.opcode == WSLAY_PING ||
+ iocb.opcode == WSLAY_PONG) {
+ wslay_event_imsg_set(ctx->imsg, iocb.fin, iocb.rsv, iocb.opcode);
+ new_frame = 1;
+ } else {
+ if((r = wslay_event_queue_close_wrapper
+ (ctx, WSLAY_CODE_PROTOCOL_ERROR, NULL, 0)) != 0) {
+ return r;
+ }
+ break;
+ }
+ } else if(ctx->ipayloadlen == 0 && ctx->ipayloadoff == 0) {
+ if(iocb.opcode == WSLAY_CONTINUATION_FRAME) {
+ ctx->imsg->fin = iocb.fin;
+ } else if(iocb.opcode == WSLAY_CONNECTION_CLOSE ||
+ iocb.opcode == WSLAY_PING ||
+ iocb.opcode == WSLAY_PONG) {
+ ctx->imsg = &ctx->imsgs[1];
+ wslay_event_imsg_set(ctx->imsg, iocb.fin, iocb.rsv, iocb.opcode);
+ } else {
+ if((r = wslay_event_queue_close_wrapper
+ (ctx, WSLAY_CODE_PROTOCOL_ERROR, NULL, 0)) != 0) {
+ return r;
+ }
+ break;
+ }
+ new_frame = 1;
+ }
+ if(new_frame) {
+ if(ctx->imsg->msg_length+iocb.payload_length >
+ ctx->max_recv_msg_length) {
+ if((r = wslay_event_queue_close_wrapper
+ (ctx, WSLAY_CODE_MESSAGE_TOO_BIG, NULL, 0)) != 0) {
+ return r;
+ }
+ break;
+ }
+ ctx->ipayloadlen = iocb.payload_length;
+ wslay_event_call_on_frame_recv_start_callback(ctx, &iocb);
+ if(!wslay_event_config_get_no_buffering(ctx) ||
+ wslay_is_ctrl_frame(iocb.opcode)) {
+ if((r = wslay_event_imsg_append_chunk(ctx->imsg,
+ iocb.payload_length)) != 0) {
+ ctx->read_enabled = 0;
+ return r;
+ }
+ }
+ }
+ if(ctx->imsg->opcode == WSLAY_TEXT_FRAME ||
+ ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE) {
+ size_t i;
+ if(ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE) {
+ i = 2;
+ } else {
+ i = 0;
+ }
+ for(; i < iocb.data_length; ++i) {
+ uint32_t codep;
+ if(decode(&ctx->imsg->utf8state, &codep,
+ iocb.data[i]) == UTF8_REJECT) {
+ if((r = wslay_event_queue_close_wrapper
+ (ctx, WSLAY_CODE_INVALID_FRAME_PAYLOAD_DATA, NULL, 0)) != 0) {
+ return r;
+ }
+ break;
+ }
+ }
+ }
+ if(ctx->imsg->utf8state == UTF8_REJECT) {
+ break;
+ }
+ wslay_event_call_on_frame_recv_chunk_callback(ctx, &iocb);
+ if(iocb.data_length > 0) {
+ if(!wslay_event_config_get_no_buffering(ctx) ||
+ wslay_is_ctrl_frame(iocb.opcode)) {
+ struct wslay_event_byte_chunk *chunk;
+ chunk = wslay_queue_tail(ctx->imsg->chunks);
+ wslay_event_byte_chunk_copy(chunk, ctx->ipayloadoff,
+ iocb.data, iocb.data_length);
+ }
+ ctx->ipayloadoff += iocb.data_length;
+ }
+ if(ctx->ipayloadoff == ctx->ipayloadlen) {
+ if(ctx->imsg->fin &&
+ (ctx->imsg->opcode == WSLAY_TEXT_FRAME ||
+ ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE) &&
+ ctx->imsg->utf8state != UTF8_ACCEPT) {
+ if((r = wslay_event_queue_close_wrapper
+ (ctx, WSLAY_CODE_INVALID_FRAME_PAYLOAD_DATA, NULL, 0)) != 0) {
+ return r;
+ }
+ break;
+ }
+ wslay_event_call_on_frame_recv_end_callback(ctx);
+ if(ctx->imsg->fin) {
+ if(ctx->callbacks.on_msg_recv_callback ||
+ ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE ||
+ ctx->imsg->opcode == WSLAY_PING) {
+ struct wslay_event_on_msg_recv_arg arg;
+ uint16_t status_code = 0;
+ uint8_t *msg = NULL;
+ size_t msg_length = 0;
+ if(!wslay_event_config_get_no_buffering(ctx) ||
+ wslay_is_ctrl_frame(iocb.opcode)) {
+ msg = wslay_event_flatten_queue(ctx->imsg->chunks,
+ ctx->imsg->msg_length);
+ if(ctx->imsg->msg_length && !msg) {
+ ctx->read_enabled = 0;
+ return WSLAY_ERR_NOMEM;
+ }
+ msg_length = ctx->imsg->msg_length;
+ }
+ if(ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE) {
+ const uint8_t *reason;
+ size_t reason_length;
+ if(ctx->imsg->msg_length >= 2) {
+ memcpy(&status_code, msg, 2);
+ status_code = ntohs(status_code);
+ if(!wslay_event_is_valid_status_code(status_code)) {
+ free(msg);
+ if((r = wslay_event_queue_close_wrapper
+ (ctx, WSLAY_CODE_PROTOCOL_ERROR, NULL, 0)) != 0) {
+ return r;
+ }
+ break;
+ }
+ reason = msg+2;
+ reason_length = ctx->imsg->msg_length-2;
+ } else {
+ reason = NULL;
+ reason_length = 0;
+ }
+ ctx->close_status |= WSLAY_CLOSE_RECEIVED;
+ ctx->status_code_recv =
+ status_code == 0 ? WSLAY_CODE_NO_STATUS_RCVD : status_code;
+ if((r = wslay_event_queue_close_wrapper
+ (ctx, status_code, reason, reason_length)) != 0) {
+ free(msg);
+ return r;
+ }
+ } else if(ctx->imsg->opcode == WSLAY_PING) {
+ struct wslay_event_msg arg = {
+ WSLAY_PONG, msg, ctx->imsg->msg_length
+ };
+ if((r = wslay_event_queue_msg(ctx, &arg)) &&
+ r != WSLAY_ERR_NO_MORE_MSG) {
+ ctx->read_enabled = 0;
+ free(msg);
+ return r;
+ }
+ }
+ if(ctx->callbacks.on_msg_recv_callback) {
+ arg.rsv = ctx->imsg->rsv;
+ arg.opcode = ctx->imsg->opcode;
+ arg.msg = msg;
+ arg.msg_length = msg_length;
+ arg.status_code = status_code;
+ ctx->error = 0;
+ ctx->callbacks.on_msg_recv_callback(ctx, &arg, ctx->user_data);
+ }
+ free(msg);
+ }
+ wslay_event_imsg_reset(ctx->imsg);
+ if(ctx->imsg == &ctx->imsgs[1]) {
+ ctx->imsg = &ctx->imsgs[0];
+ }
+ }
+ ctx->ipayloadlen = ctx->ipayloadoff = 0;
+ }
+ } else {
+ if(r != WSLAY_ERR_WANT_READ ||
+ (ctx->error != WSLAY_ERR_WOULDBLOCK && ctx->error != 0)) {
+ if((r = wslay_event_queue_close_wrapper(ctx, 0, NULL, 0)) != 0) {
+ return r;
+ }
+ return WSLAY_ERR_CALLBACK_FAILURE;
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+static void wslay_event_on_non_fragmented_msg_popped
+(wslay_event_context_ptr ctx)
+{
+ ctx->omsg->fin = 1;
+ ctx->opayloadlen = ctx->omsg->data_length;
+ ctx->opayloadoff = 0;
+}
+
+static struct wslay_event_omsg* wslay_event_send_ctrl_queue_pop
+(wslay_event_context_ptr ctx)
+{
+ /*
+ * If Close control frame is queued, we don't send any control frame
+ * other than Close.
+ */
+ if(ctx->close_status & WSLAY_CLOSE_QUEUED) {
+ while(!wslay_queue_empty(ctx->send_ctrl_queue)) {
+ struct wslay_event_omsg *msg = wslay_queue_top(ctx->send_ctrl_queue);
+ wslay_queue_pop(ctx->send_ctrl_queue);
+ if(msg->opcode == WSLAY_CONNECTION_CLOSE) {
+ return msg;
+ } else {
+ wslay_event_omsg_free(msg);
+ }
+ }
+ return NULL;
+ } else {
+ struct wslay_event_omsg *msg = wslay_queue_top(ctx->send_ctrl_queue);
+ wslay_queue_pop(ctx->send_ctrl_queue);
+ return msg;
+ }
+}
+
+int wslay_event_send(wslay_event_context_ptr ctx)
+{
+ struct wslay_frame_iocb iocb;
+ ssize_t r;
+ while(ctx->write_enabled &&
+ (!wslay_queue_empty(ctx->send_queue) ||
+ !wslay_queue_empty(ctx->send_ctrl_queue) || ctx->omsg)) {
+ if(!ctx->omsg) {
+ if(wslay_queue_empty(ctx->send_ctrl_queue)) {
+ ctx->omsg = wslay_queue_top(ctx->send_queue);
+ wslay_queue_pop(ctx->send_queue);
+ } else {
+ ctx->omsg = wslay_event_send_ctrl_queue_pop(ctx);
+ if(ctx->omsg == NULL) {
+ break;
+ }
+ }
+ if(ctx->omsg->type == WSLAY_NON_FRAGMENTED) {
+ wslay_event_on_non_fragmented_msg_popped(ctx);
+ }
+ } else if(!wslay_is_ctrl_frame(ctx->omsg->opcode) &&
+ ctx->frame_ctx->ostate == PREP_HEADER &&
+ !wslay_queue_empty(ctx->send_ctrl_queue)) {
+ if((r = wslay_queue_push_front(ctx->send_queue, ctx->omsg)) != 0) {
+ ctx->write_enabled = 0;
+ return r;
+ }
+ ctx->omsg = wslay_event_send_ctrl_queue_pop(ctx);
+ if(ctx->omsg == NULL) {
+ break;
+ }
+ /* ctrl message has WSLAY_NON_FRAGMENTED */
+ wslay_event_on_non_fragmented_msg_popped(ctx);
+ }
+ if(ctx->omsg->type == WSLAY_NON_FRAGMENTED) {
+ memset(&iocb, 0, sizeof(iocb));
+ iocb.fin = 1;
+ iocb.opcode = ctx->omsg->opcode;
+ iocb.mask = ctx->server^1;
+ iocb.data = ctx->omsg->data+ctx->opayloadoff;
+ iocb.data_length = ctx->opayloadlen-ctx->opayloadoff;
+ iocb.payload_length = ctx->opayloadlen;
+ r = wslay_frame_send(ctx->frame_ctx, &iocb);
+ if(r >= 0) {
+ ctx->opayloadoff += r;
+ if(ctx->opayloadoff == ctx->opayloadlen) {
+ --ctx->queued_msg_count;
+ ctx->queued_msg_length -= ctx->omsg->data_length;
+ if(ctx->omsg->opcode == WSLAY_CONNECTION_CLOSE) {
+ uint16_t status_code = 0;
+ ctx->write_enabled = 0;
+ ctx->close_status |= WSLAY_CLOSE_SENT;
+ if(ctx->omsg->data_length >= 2) {
+ memcpy(&status_code, ctx->omsg->data, 2);
+ status_code = ntohs(status_code);
+ }
+ ctx->status_code_sent =
+ status_code == 0 ? WSLAY_CODE_NO_STATUS_RCVD : status_code;
+ }
+ wslay_event_omsg_free(ctx->omsg);
+ ctx->omsg = NULL;
+ } else {
+ break;
+ }
+ } else {
+ if(r != WSLAY_ERR_WANT_WRITE ||
+ (ctx->error != WSLAY_ERR_WOULDBLOCK && ctx->error != 0)) {
+ ctx->write_enabled = 0;
+ return WSLAY_ERR_CALLBACK_FAILURE;
+ }
+ break;
+ }
+ } else {
+ if(ctx->omsg->fin == 0 && ctx->obuflimit == ctx->obufmark) {
+ int eof = 0;
+ r = ctx->omsg->read_callback(ctx, ctx->obuf, sizeof(ctx->obuf),
+ &ctx->omsg->source,
+ &eof, ctx->user_data);
+ if(r == 0) {
+ break;
+ } else if(r < 0) {
+ ctx->write_enabled = 0;
+ return WSLAY_ERR_CALLBACK_FAILURE;
+ }
+ ctx->obuflimit = ctx->obuf+r;
+ if(eof) {
+ ctx->omsg->fin = 1;
+ } else if(r == 0) {
+ break;
+ }
+ ctx->opayloadlen = r;
+ ctx->opayloadoff = 0;
+ }
+ memset(&iocb, 0, sizeof(iocb));
+ iocb.fin = ctx->omsg->fin;
+ iocb.opcode = ctx->omsg->opcode;
+ iocb.mask = ctx->server ? 0 : 1;
+ iocb.data = ctx->obufmark;
+ iocb.data_length = ctx->obuflimit-ctx->obufmark;
+ iocb.payload_length = ctx->opayloadlen;
+ r = wslay_frame_send(ctx->frame_ctx, &iocb);
+ if(r >= 0) {
+ ctx->obufmark += r;
+ if(ctx->obufmark == ctx->obuflimit) {
+ ctx->obufmark = ctx->obuflimit = ctx->obuf;
+ if(ctx->omsg->fin) {
+ --ctx->queued_msg_count;
+ wslay_event_omsg_free(ctx->omsg);
+ ctx->omsg = NULL;
+ } else {
+ ctx->omsg->opcode = WSLAY_CONTINUATION_FRAME;
+ }
+ } else {
+ break;
+ }
+ } else {
+ if(r != WSLAY_ERR_WANT_WRITE ||
+ (ctx->error != WSLAY_ERR_WOULDBLOCK &&
+ ctx->error != 0)) {
+ ctx->write_enabled = 0;
+ return WSLAY_ERR_CALLBACK_FAILURE;
+ }
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+void wslay_event_set_error(wslay_event_context_ptr ctx, int val)
+{
+ ctx->error = val;
+}
+
+int wslay_event_want_read(wslay_event_context_ptr ctx)
+{
+ return ctx->read_enabled;
+}
+
+int wslay_event_want_write(wslay_event_context_ptr ctx)
+{
+ return ctx->write_enabled &&
+ (!wslay_queue_empty(ctx->send_queue) ||
+ !wslay_queue_empty(ctx->send_ctrl_queue) || ctx->omsg);
+}
+
+void wslay_event_shutdown_read(wslay_event_context_ptr ctx)
+{
+ ctx->read_enabled = 0;
+}
+
+void wslay_event_shutdown_write(wslay_event_context_ptr ctx)
+{
+ ctx->write_enabled = 0;
+}
+
+int wslay_event_get_read_enabled(wslay_event_context_ptr ctx)
+{
+ return ctx->read_enabled;
+}
+
+int wslay_event_get_write_enabled(wslay_event_context_ptr ctx)
+{
+ return ctx->write_enabled;
+}
+
+int wslay_event_get_close_received(wslay_event_context_ptr ctx)
+{
+ return (ctx->close_status & WSLAY_CLOSE_RECEIVED) > 0;
+}
+
+int wslay_event_get_close_sent(wslay_event_context_ptr ctx)
+{
+ return (ctx->close_status & WSLAY_CLOSE_SENT) > 0;
+}
+
+void wslay_event_config_set_no_buffering(wslay_event_context_ptr ctx, int val)
+{
+ if(val) {
+ ctx->config |= WSLAY_CONFIG_NO_BUFFERING;
+ } else {
+ ctx->config &= ~WSLAY_CONFIG_NO_BUFFERING;
+ }
+}
+
+void wslay_event_config_set_max_recv_msg_length(wslay_event_context_ptr ctx,
+ uint64_t val)
+{
+ ctx->max_recv_msg_length = val;
+}
+
+uint16_t wslay_event_get_status_code_received(wslay_event_context_ptr ctx)
+{
+ return ctx->status_code_recv;
+}
+
+uint16_t wslay_event_get_status_code_sent(wslay_event_context_ptr ctx)
+{
+ return ctx->status_code_sent;
+}
+
+size_t wslay_event_get_queued_msg_count(wslay_event_context_ptr ctx)
+{
+ return ctx->queued_msg_count;
+}
+
+size_t wslay_event_get_queued_msg_length(wslay_event_context_ptr ctx)
+{
+ return ctx->queued_msg_length;
+}
diff --git a/deps/wslay/lib/wslay_event.h b/deps/wslay/lib/wslay_event.h
new file mode 100644
index 00000000..d1a59d44
--- /dev/null
+++ b/deps/wslay/lib/wslay_event.h
@@ -0,0 +1,140 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef WSLAY_EVENT_H
+#define WSLAY_EVENT_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <wslay/wslay.h>
+
+struct wslay_stack;
+struct wslay_queue;
+
+struct wslay_event_byte_chunk {
+ uint8_t *data;
+ size_t data_length;
+};
+
+struct wslay_event_imsg {
+ uint8_t fin;
+ uint8_t rsv;
+ uint8_t opcode;
+ uint32_t utf8state;
+ struct wslay_queue *chunks;
+ size_t msg_length;
+};
+
+enum wslay_event_msg_type {
+ WSLAY_NON_FRAGMENTED,
+ WSLAY_FRAGMENTED
+};
+
+struct wslay_event_omsg {
+ uint8_t fin;
+ uint8_t opcode;
+ enum wslay_event_msg_type type;
+
+ uint8_t *data;
+ size_t data_length;
+
+ union wslay_event_msg_source source;
+ wslay_event_fragmented_msg_callback read_callback;
+};
+
+struct wslay_event_frame_user_data {
+ wslay_event_context_ptr ctx;
+ void *user_data;
+};
+
+enum wslay_event_close_status {
+ WSLAY_CLOSE_RECEIVED = 1 << 0,
+ WSLAY_CLOSE_QUEUED = 1 << 1,
+ WSLAY_CLOSE_SENT = 1 << 2,
+};
+
+enum wslay_event_config {
+ WSLAY_CONFIG_NO_BUFFERING = 1 << 0
+};
+
+struct wslay_event_context {
+ /* config status, bitwise OR of enum wslay_event_config values*/
+ uint32_t config;
+ /* maximum message length that can be received */
+ uint64_t max_recv_msg_length;
+ /* 1 if initialized for server, otherwise 0 */
+ uint8_t server;
+ /* bitwise OR of enum wslay_event_close_status values */
+ uint8_t close_status;
+ /* status code in received close control frame */
+ uint16_t status_code_recv;
+ /* status code in sent close control frame */
+ uint16_t status_code_sent;
+ wslay_frame_context_ptr frame_ctx;
+ /* 1 if reading is enabled, otherwise 0. Upon receiving close
+ control frame this value set to 0. If any errors in read
+ operation will also set this value to 0. */
+ uint8_t read_enabled;
+ /* 1 if writing is enabled, otherwise 0 Upon completing sending
+ close control frame, this value set to 0. If any errors in write
+ opration will also set this value to 0. */
+ uint8_t write_enabled;
+ /* imsg buffer to allow interleaved control frame between
+ non-control frames. */
+ struct wslay_event_imsg imsgs[2];
+ /* Pointer to imsgs to indicate current used buffer. */
+ struct wslay_event_imsg *imsg;
+ /* payload length of frame currently being received. */
+ uint64_t ipayloadlen;
+ /* next byte offset of payload currently being received. */
+ uint64_t ipayloadoff;
+ /* error value set by user callback */
+ int error;
+ /* Pointer to the message currently being sent. NULL if no message
+ is currently sent. */
+ struct wslay_event_omsg *omsg;
+ /* Queue for non-control frames */
+ struct wslay_queue/*<wslay_omsg*>*/ *send_queue;
+ /* Queue for control frames */
+ struct wslay_queue/*<wslay_omsg*>*/ *send_ctrl_queue;
+ /* Size of send_queue + size of send_ctrl_queue */
+ size_t queued_msg_count;
+ /* The sum of message length in send_queue */
+ size_t queued_msg_length;
+ /* Buffer used for fragmented messages */
+ uint8_t obuf[4096];
+ uint8_t *obuflimit;
+ uint8_t *obufmark;
+ /* payload length of frame currently being sent. */
+ uint64_t opayloadlen;
+ /* next byte offset of payload currently being sent. */
+ uint64_t opayloadoff;
+ struct wslay_event_callbacks callbacks;
+ struct wslay_event_frame_user_data frame_user_data;
+ void *user_data;
+};
+
+#endif /* WSLAY_EVENT_H */
diff --git a/deps/wslay/lib/wslay_frame.c b/deps/wslay/lib/wslay_frame.c
new file mode 100644
index 00000000..74bb0b24
--- /dev/null
+++ b/deps/wslay/lib/wslay_frame.c
@@ -0,0 +1,342 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "wslay_frame.h"
+
+#include <stddef.h>
+#include <string.h>
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif // HAVE_ARPA_INET_H
+#include <assert.h>
+
+#include "wslay_net.h"
+
+#define wslay_min(A, B) (((A) < (B)) ? (A) : (B))
+
+int wslay_frame_context_init(wslay_frame_context_ptr *ctx,
+ const struct wslay_frame_callbacks *callbacks,
+ void *user_data)
+{
+ *ctx = (wslay_frame_context_ptr)malloc(sizeof(struct wslay_frame_context));
+ if(ctx == NULL) {
+ return -1;
+ }
+ memset(*ctx, 0, sizeof(struct wslay_frame_context));
+ (*ctx)->istate = RECV_HEADER1;
+ (*ctx)->ireqread = 2;
+ (*ctx)->ostate = PREP_HEADER;
+ (*ctx)->user_data = user_data;
+ (*ctx)->ibufmark = (*ctx)->ibuflimit = (*ctx)->ibuf;
+ (*ctx)->callbacks = *callbacks;
+ return 0;
+}
+
+void wslay_frame_context_free(wslay_frame_context_ptr ctx)
+{
+ free(ctx);
+}
+
+ssize_t wslay_frame_send(wslay_frame_context_ptr ctx,
+ struct wslay_frame_iocb *iocb)
+{
+ if(iocb->data_length > iocb->payload_length) {
+ return WSLAY_ERR_INVALID_ARGUMENT;
+ }
+ if(ctx->ostate == PREP_HEADER) {
+ uint8_t *hdptr = ctx->oheader;
+ memset(ctx->oheader, 0, sizeof(ctx->oheader));
+ *hdptr |= (iocb->fin << 7) & 0x80u;
+ *hdptr |= (iocb->rsv << 4) & 0x70u;
+ *hdptr |= iocb->opcode & 0xfu;
+ ++hdptr;
+ *hdptr |= (iocb->mask << 7) & 0x80u;
+ if(wslay_is_ctrl_frame(iocb->opcode) && iocb->payload_length > 125) {
+ return WSLAY_ERR_INVALID_ARGUMENT;
+ }
+ if(iocb->payload_length < 126) {
+ *hdptr |= iocb->payload_length;
+ ++hdptr;
+ } else if(iocb->payload_length < (1 << 16)) {
+ uint16_t len = htons(iocb->payload_length);
+ *hdptr |= 126;
+ ++hdptr;
+ memcpy(hdptr, &len, 2);
+ hdptr += 2;
+ } else if(iocb->payload_length < (1ull << 63)) {
+ uint64_t len = hton64(iocb->payload_length);
+ *hdptr |= 127;
+ ++hdptr;
+ memcpy(hdptr, &len, 8);
+ hdptr += 8;
+ } else {
+ /* Too large payload length */
+ return WSLAY_ERR_INVALID_ARGUMENT;
+ }
+ if(iocb->mask) {
+ if(ctx->callbacks.genmask_callback(ctx->omaskkey, 4,
+ ctx->user_data) != 0) {
+ return WSLAY_ERR_INVALID_CALLBACK;
+ } else {
+ ctx->omask = 1;
+ memcpy(hdptr, ctx->omaskkey, 4);
+ hdptr += 4;
+ }
+ }
+ ctx->ostate = SEND_HEADER;
+ ctx->oheadermark = ctx->oheader;
+ ctx->oheaderlimit = hdptr;
+ ctx->opayloadlen = iocb->payload_length;
+ ctx->opayloadoff = 0;
+ }
+ if(ctx->ostate == SEND_HEADER) {
+ ptrdiff_t len = ctx->oheaderlimit-ctx->oheadermark;
+ ssize_t r;
+ int flags = 0;
+ if(iocb->data_length > 0) {
+ flags |= WSLAY_MSG_MORE;
+ };
+ r = ctx->callbacks.send_callback(ctx->oheadermark, len, flags,
+ ctx->user_data);
+ if(r > 0) {
+ if(r > len) {
+ return WSLAY_ERR_INVALID_CALLBACK;
+ } else {
+ ctx->oheadermark += r;
+ if(ctx->oheadermark == ctx->oheaderlimit) {
+ ctx->ostate = SEND_PAYLOAD;
+ } else {
+ return WSLAY_ERR_WANT_WRITE;
+ }
+ }
+ } else {
+ return WSLAY_ERR_WANT_WRITE;
+ }
+ }
+ if(ctx->ostate == SEND_PAYLOAD) {
+ size_t totallen = 0;
+ if(iocb->data_length > 0) {
+ if(ctx->omask) {
+ uint8_t temp[4096];
+ const uint8_t *datamark = iocb->data,
+ *datalimit = iocb->data+iocb->data_length;
+ while(datamark < datalimit) {
+ const uint8_t *writelimit = datamark+
+ wslay_min(sizeof(temp), datalimit-datamark);
+ size_t writelen = writelimit-datamark;
+ ssize_t r;
+ int i;
+ for(i = 0; i < writelen; ++i) {
+ temp[i] = datamark[i]^ctx->omaskkey[(ctx->opayloadoff+i)%4];
+ }
+ r = ctx->callbacks.send_callback(temp, writelen, 0, ctx->user_data);
+ if(r > 0) {
+ if(r > writelen) {
+ return WSLAY_ERR_INVALID_CALLBACK;
+ } else {
+ datamark += r;
+ ctx->opayloadoff += r;
+ totallen += r;
+ }
+ } else {
+ if(totallen > 0) {
+ break;
+ } else {
+ return WSLAY_ERR_WANT_WRITE;
+ }
+ }
+ }
+ } else {
+ ssize_t r;
+ r = ctx->callbacks.send_callback(iocb->data, iocb->data_length, 0,
+ ctx->user_data);
+ if(r > 0) {
+ if(r > iocb->data_length) {
+ return WSLAY_ERR_INVALID_CALLBACK;
+ } else {
+ ctx->opayloadoff += r;
+ totallen = r;
+ }
+ } else {
+ return WSLAY_ERR_WANT_WRITE;
+ }
+ }
+ }
+ if(ctx->opayloadoff == ctx->opayloadlen) {
+ ctx->ostate = PREP_HEADER;
+ }
+ return totallen;
+ }
+ return WSLAY_ERR_INVALID_ARGUMENT;
+}
+
+static void wslay_shift_ibuf(wslay_frame_context_ptr ctx)
+{
+ ptrdiff_t len = ctx->ibuflimit-ctx->ibufmark;
+ memmove(ctx->ibuf, ctx->ibufmark, len);
+ ctx->ibuflimit = ctx->ibuf+len;
+ ctx->ibufmark = ctx->ibuf;
+}
+
+static ssize_t wslay_recv(wslay_frame_context_ptr ctx)
+{
+ ssize_t r;
+ if(ctx->ibufmark != ctx->ibuf) {
+ wslay_shift_ibuf(ctx);
+ }
+ r = ctx->callbacks.recv_callback
+ (ctx->ibuflimit, ctx->ibuf+sizeof(ctx->ibuf)-ctx->ibuflimit,
+ 0, ctx->user_data);
+ if(r > 0) {
+ ctx->ibuflimit += r;
+ } else {
+ r = WSLAY_ERR_WANT_READ;
+ }
+ return r;
+}
+
+#define WSLAY_AVAIL_IBUF(ctx) (ctx->ibuflimit-ctx->ibufmark)
+
+ssize_t wslay_frame_recv(wslay_frame_context_ptr ctx,
+ struct wslay_frame_iocb *iocb)
+{
+ ssize_t r;
+ if(ctx->istate == RECV_HEADER1) {
+ uint8_t fin, opcode, rsv, payloadlen;
+ if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
+ if((r = wslay_recv(ctx)) <= 0) {
+ return r;
+ }
+ }
+ if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
+ return WSLAY_ERR_WANT_READ;
+ }
+ fin = (ctx->ibufmark[0] >> 7) & 1;
+ rsv = (ctx->ibufmark[0] >> 4) & 7;
+ opcode = ctx->ibufmark[0] & 0xfu;
+ ctx->iom.opcode = opcode;
+ ctx->iom.fin = fin;
+ ctx->iom.rsv = rsv;
+ ++ctx->ibufmark;
+ ctx->imask = (ctx->ibufmark[0] >> 7) & 1;
+ payloadlen = ctx->ibufmark[0] & 0x7fu;
+ ++ctx->ibufmark;
+ if(wslay_is_ctrl_frame(opcode) && (payloadlen > 125 || !fin)) {
+ return WSLAY_ERR_PROTO;
+ }
+ if(payloadlen == 126) {
+ ctx->istate = RECV_EXT_PAYLOADLEN;
+ ctx->ireqread = 2;
+ } else if(payloadlen == 127) {
+ ctx->istate = RECV_EXT_PAYLOADLEN;
+ ctx->ireqread = 8;
+ } else {
+ ctx->ipayloadlen = payloadlen;
+ ctx->ipayloadoff = 0;
+ if(ctx->imask) {
+ ctx->istate = RECV_MASKKEY;
+ ctx->ireqread = 4;
+ } else {
+ ctx->istate = RECV_PAYLOAD;
+ }
+ }
+ }
+ if(ctx->istate == RECV_EXT_PAYLOADLEN) {
+ if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
+ if((r = wslay_recv(ctx)) <= 0) {
+ return r;
+ }
+ if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
+ return WSLAY_ERR_WANT_READ;
+ }
+ }
+ ctx->ipayloadlen = 0;
+ ctx->ipayloadoff = 0;
+ memcpy((uint8_t*)&ctx->ipayloadlen+(8-ctx->ireqread),
+ ctx->ibufmark, ctx->ireqread);
+ ctx->ipayloadlen = ntoh64(ctx->ipayloadlen);
+ ctx->ibufmark += ctx->ireqread;
+ if(ctx->ireqread == 8) {
+ if(ctx->ipayloadlen < (1 << 16) ||
+ ctx->ipayloadlen & (1ull << 63)) {
+ return WSLAY_ERR_PROTO;
+ }
+ } else if(ctx->ipayloadlen < 126) {
+ return WSLAY_ERR_PROTO;
+ }
+ if(ctx->imask) {
+ ctx->istate = RECV_MASKKEY;
+ ctx->ireqread = 4;
+ } else {
+ ctx->istate = RECV_PAYLOAD;
+ }
+ }
+ if(ctx->istate == RECV_MASKKEY) {
+ if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
+ if((r = wslay_recv(ctx)) <= 0) {
+ return r;
+ }
+ if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
+ return WSLAY_ERR_WANT_READ;
+ }
+ }
+ memcpy(ctx->imaskkey, ctx->ibufmark, 4);
+ ctx->ibufmark += 4;
+ ctx->istate = RECV_PAYLOAD;
+ }
+ if(ctx->istate == RECV_PAYLOAD) {
+ uint8_t *readlimit, *readmark;
+ uint64_t rempayloadlen = ctx->ipayloadlen-ctx->ipayloadoff;
+ if(WSLAY_AVAIL_IBUF(ctx) == 0 && rempayloadlen > 0) {
+ if((r = wslay_recv(ctx)) <= 0) {
+ return r;
+ }
+ }
+ readmark = ctx->ibufmark;
+ readlimit = WSLAY_AVAIL_IBUF(ctx) < rempayloadlen ?
+ ctx->ibuflimit : ctx->ibufmark+rempayloadlen;
+ if(ctx->imask) {
+ for(; ctx->ibufmark != readlimit;
+ ++ctx->ibufmark, ++ctx->ipayloadoff) {
+ ctx->ibufmark[0] ^= ctx->imaskkey[ctx->ipayloadoff % 4];
+ }
+ } else {
+ ctx->ibufmark = readlimit;
+ ctx->ipayloadoff += readlimit-readmark;
+ }
+ iocb->fin = ctx->iom.fin;
+ iocb->rsv = ctx->iom.rsv;
+ iocb->opcode = ctx->iom.opcode;
+ iocb->payload_length = ctx->ipayloadlen;
+ iocb->mask = ctx->imask;
+ iocb->data = readmark;
+ iocb->data_length = ctx->ibufmark-readmark;
+ if(ctx->ipayloadlen == ctx->ipayloadoff) {
+ ctx->istate = RECV_HEADER1;
+ ctx->ireqread = 2;
+ }
+ return iocb->data_length;
+ }
+ return WSLAY_ERR_INVALID_ARGUMENT;
+}
diff --git a/deps/wslay/lib/wslay_frame.h b/deps/wslay/lib/wslay_frame.h
new file mode 100644
index 00000000..6a75858c
--- /dev/null
+++ b/deps/wslay/lib/wslay_frame.h
@@ -0,0 +1,76 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef WSLAY_FRAME_H
+#define WSLAY_FRAME_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <wslay/wslay.h>
+
+enum wslay_frame_state {
+ PREP_HEADER,
+ SEND_HEADER,
+ SEND_PAYLOAD,
+ RECV_HEADER1,
+ RECV_PAYLOADLEN,
+ RECV_EXT_PAYLOADLEN,
+ RECV_MASKKEY,
+ RECV_PAYLOAD
+};
+
+struct wslay_frame_opcode_memo {
+ uint8_t fin;
+ uint8_t opcode;
+ uint8_t rsv;
+};
+
+struct wslay_frame_context {
+ uint8_t ibuf[4096];
+ uint8_t *ibufmark;
+ uint8_t *ibuflimit;
+ struct wslay_frame_opcode_memo iom;
+ uint64_t ipayloadlen;
+ uint64_t ipayloadoff;
+ uint8_t imask;
+ uint8_t imaskkey[4];
+ enum wslay_frame_state istate;
+ size_t ireqread;
+
+ uint8_t oheader[14];
+ uint8_t *oheadermark;
+ uint8_t *oheaderlimit;
+ uint64_t opayloadlen;
+ uint64_t opayloadoff;
+ uint8_t omask;
+ uint8_t omaskkey[4];
+ enum wslay_frame_state ostate;
+
+ struct wslay_frame_callbacks callbacks;
+ void *user_data;
+};
+
+#endif /* WSLAY_FRAME_H */
diff --git a/deps/wslay/lib/wslay_net.c b/deps/wslay/lib/wslay_net.c
new file mode 100644
index 00000000..eedd0a62
--- /dev/null
+++ b/deps/wslay/lib/wslay_net.c
@@ -0,0 +1,62 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "wslay_net.h"
+
+#ifndef WORDS_BIGENDIAN
+
+static uint16_t byteswap16(uint16_t x)
+{
+ return ((x & 0xffu) << 8) | (x >> 8);;
+}
+
+#ifdef HAVE_NTOHL
+# define byteswap32(x) ntohl(x)
+#else /* !HAVE_NTOHL */
+static uint32_t byteswap32(uint32_t x)
+{
+ uint32_t u = byteswap16(x & 0xffffu);
+ uint32_t l = byteswap16(x >> 16);
+ return (u << 16) | l;
+}
+#endif /* !HAVE_NTOHL */
+
+static uint64_t byteswap64(uint64_t x)
+{
+ uint64_t u = byteswap32(x & 0xffffffffllu);
+ uint64_t l = byteswap32(x >> 32);
+ return (u << 32) | l;
+}
+
+uint16_t wslay_byteswap16(uint16_t x)
+{
+ return byteswap16(x);
+}
+
+uint64_t wslay_byteswap64(uint64_t x)
+{
+ return byteswap64(x);
+}
+
+#endif /* !WORDS_BIGENDIAN */
diff --git a/deps/wslay/lib/wslay_net.h b/deps/wslay/lib/wslay_net.h
new file mode 100644
index 00000000..60d0c91c
--- /dev/null
+++ b/deps/wslay/lib/wslay_net.h
@@ -0,0 +1,63 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef WSLAY_NET_H
+#define WSLAY_NET_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <wslay/wslay.h>
+
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif /* HAVE_ARPA_INET_H */
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+
+#ifdef WORDS_BIGENDIAN
+# ifndef HAVE_HTONS
+# define htons(x) (x)
+# endif /* !HAVE_HTONS */
+# ifndef HAVE_NTOHS
+# define ntohs(x) (x)
+# endif /* !HAVE_NTOHS */
+# define ntoh64(x) (x)
+# define hton64(x) (x)
+#else /* !WORDS_BIGENDIAN */
+uint16_t wslay_byteswap16(uint16_t x);
+uint64_t wslay_byteswap64(uint64_t x);
+# ifndef HAVE_HTONS
+# define htons(x) wslay_byteswap16(x)
+# endif /* !HAVE_HTONS */
+# ifndef HAVE_NTOHS
+# define ntohs(x) wslay_byteswap16(x)
+# endif /* !HAVE_NTOHS */
+# define ntoh64(x) wslay_byteswap64(x)
+# define hton64(x) wslay_byteswap64(x)
+#endif /* !WORDS_BIGENDIAN */
+
+#endif /* WSLAY_NET_H */
diff --git a/deps/wslay/lib/wslay_queue.c b/deps/wslay/lib/wslay_queue.c
new file mode 100644
index 00000000..534684a0
--- /dev/null
+++ b/deps/wslay/lib/wslay_queue.c
@@ -0,0 +1,116 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "wslay_queue.h"
+
+#include <string.h>
+#include <assert.h>
+
+struct wslay_queue* wslay_queue_new()
+{
+ struct wslay_queue *queue = (struct wslay_queue*)malloc
+ (sizeof(struct wslay_queue));
+ if(!queue) {
+ return NULL;
+ }
+ queue->top = queue->tail = NULL;
+ return queue;
+}
+
+void wslay_queue_free(struct wslay_queue *queue)
+{
+ if(!queue) {
+ return;
+ }
+ struct wslay_queue_cell *p = queue->top;
+ while(p) {
+ struct wslay_queue_cell *next = p->next;
+ free(p);
+ p = next;
+ }
+ free(queue);
+}
+
+int wslay_queue_push(struct wslay_queue *queue, void *data)
+{
+ struct wslay_queue_cell *new_cell = (struct wslay_queue_cell*)malloc
+ (sizeof(struct wslay_queue_cell));
+ if(!new_cell) {
+ return WSLAY_ERR_NOMEM;
+ }
+ new_cell->data = data;
+ new_cell->next = NULL;
+ if(queue->tail) {
+ queue->tail->next = new_cell;
+ queue->tail = new_cell;
+
+ } else {
+ queue->top = queue->tail = new_cell;
+ }
+ return 0;
+}
+
+int wslay_queue_push_front(struct wslay_queue *queue, void *data)
+{
+ struct wslay_queue_cell *new_cell = (struct wslay_queue_cell*)malloc
+ (sizeof(struct wslay_queue_cell));
+ if(!new_cell) {
+ return WSLAY_ERR_NOMEM;
+ }
+ new_cell->data = data;
+ new_cell->next = queue->top;
+ queue->top = new_cell;
+ if(!queue->tail) {
+ queue->tail = queue->top;
+ }
+ return 0;
+}
+
+void wslay_queue_pop(struct wslay_queue *queue)
+{
+ struct wslay_queue_cell *top = queue->top;
+ assert(top);
+ queue->top = top->next;
+ if(top == queue->tail) {
+ queue->tail = NULL;
+ }
+ free(top);
+}
+
+void* wslay_queue_top(struct wslay_queue *queue)
+{
+ assert(queue->top);
+ return queue->top->data;
+}
+
+void* wslay_queue_tail(struct wslay_queue *queue)
+{
+ assert(queue->tail);
+ return queue->tail->data;
+}
+
+int wslay_queue_empty(struct wslay_queue *queue)
+{
+ return queue->top == NULL;
+}
diff --git a/deps/wslay/lib/wslay_queue.h b/deps/wslay/lib/wslay_queue.h
new file mode 100644
index 00000000..205852ed
--- /dev/null
+++ b/deps/wslay/lib/wslay_queue.h
@@ -0,0 +1,53 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef WSLAY_QUEUE_H
+#define WSLAY_QUEUE_H
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <wslay/wslay.h>
+
+struct wslay_queue_cell {
+ void *data;
+ struct wslay_queue_cell *next;
+};
+
+struct wslay_queue {
+ struct wslay_queue_cell *top;
+ struct wslay_queue_cell *tail;
+};
+
+struct wslay_queue* wslay_queue_new();
+void wslay_queue_free(struct wslay_queue *queue);
+int wslay_queue_push(struct wslay_queue *queue, void *data);
+int wslay_queue_push_front(struct wslay_queue *queue, void *data);
+void wslay_queue_pop(struct wslay_queue *queue);
+void* wslay_queue_top(struct wslay_queue *queue);
+void* wslay_queue_tail(struct wslay_queue *queue);
+int wslay_queue_empty(struct wslay_queue *queue);
+
+#endif /* WSLAY_QUEUE_H */
diff --git a/deps/wslay/lib/wslay_stack.c b/deps/wslay/lib/wslay_stack.c
new file mode 100644
index 00000000..752cf721
--- /dev/null
+++ b/deps/wslay/lib/wslay_stack.c
@@ -0,0 +1,85 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "wslay_stack.h"
+
+#include <string.h>
+#include <assert.h>
+
+struct wslay_stack* wslay_stack_new()
+{
+ struct wslay_stack *stack = (struct wslay_stack*)malloc
+ (sizeof(struct wslay_stack));
+ if(!stack) {
+ return NULL;
+ }
+ stack->top = NULL;
+ return stack;
+}
+
+void wslay_stack_free(struct wslay_stack *stack)
+{
+ if(!stack) {
+ return;
+ }
+ struct wslay_stack_cell *p = stack->top;
+ while(p) {
+ struct wslay_stack_cell *next = p->next;
+ free(p);
+ p = next;
+ }
+ free(stack);
+}
+
+int wslay_stack_push(struct wslay_stack *stack, void *data)
+{
+ struct wslay_stack_cell *new_cell = (struct wslay_stack_cell*)malloc
+ (sizeof(struct wslay_stack_cell));
+ if(!new_cell) {
+ return WSLAY_ERR_NOMEM;
+ }
+ new_cell->data = data;
+ new_cell->next = stack->top;
+ stack->top = new_cell;
+ return 0;
+}
+
+void wslay_stack_pop(struct wslay_stack *stack)
+{
+ struct wslay_stack_cell *top = stack->top;
+ assert(top);
+ stack->top = top->next;
+ free(top);
+}
+
+void* wslay_stack_top(struct wslay_stack *stack)
+{
+ assert(stack->top);
+ return stack->top->data;
+}
+
+int wslay_stack_empty(struct wslay_stack *stack)
+{
+ return stack->top == NULL;
+}
diff --git a/deps/wslay/lib/wslay_stack.h b/deps/wslay/lib/wslay_stack.h
new file mode 100644
index 00000000..16e4e968
--- /dev/null
+++ b/deps/wslay/lib/wslay_stack.h
@@ -0,0 +1,50 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef WSLAY_STACK_H
+#define WSLAY_STACK_H
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <wslay/wslay.h>
+
+struct wslay_stack_cell {
+ void *data;
+ struct wslay_stack_cell *next;
+};
+
+struct wslay_stack {
+ struct wslay_stack_cell *top;
+};
+
+struct wslay_stack* wslay_stack_new();
+void wslay_stack_free(struct wslay_stack *stack);
+int wslay_stack_push(struct wslay_stack *stack, void *data);
+void wslay_stack_pop(struct wslay_stack *stack);
+void* wslay_stack_top(struct wslay_stack *stack);
+int wslay_stack_empty(struct wslay_stack *stack);
+
+#endif /* WSLAY_STACK_H */
diff --git a/deps/wslay/m4/README b/deps/wslay/m4/README
new file mode 100644
index 00000000..a416f2ec
--- /dev/null
+++ b/deps/wslay/m4/README
@@ -0,0 +1 @@
+Empty m4 directory to make `autoreconf -i` happy.
diff --git a/deps/wslay/tests/Makefile.am b/deps/wslay/tests/Makefile.am
new file mode 100644
index 00000000..79621db7
--- /dev/null
+++ b/deps/wslay/tests/Makefile.am
@@ -0,0 +1,44 @@
+# Wslay - The WebSocket Library
+
+# Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+if HAVE_CUNIT
+
+check_PROGRAMS = main
+
+OBJECTS = main.c wslay_frame_test.c\
+ wslay_event_test.c\
+ wslay_queue_test.c
+
+HFILES = wslay_session_test.h wslay_frame_test.h\
+ wslay_event_test.h\
+ wslay_queue_test.h
+
+main_SOURCES = $(HFILES) $(OBJECTS)
+
+main_LDADD = ${top_builddir}/lib/libwslay.la -lcunit
+main_LDFLAGS = -static
+AM_CFLAGS = -Wall -g -O2 -I${top_srcdir}/lib -I${top_srcdir}/lib/includes
+# DEFS += -D_ISOC99_SOURCE -D_GNU_SOURCE
+TESTS = main
+
+endif # HAVE_CUNIT
diff --git a/deps/wslay/tests/main.c b/deps/wslay/tests/main.c
new file mode 100644
index 00000000..c68e1f9e
--- /dev/null
+++ b/deps/wslay/tests/main.c
@@ -0,0 +1,126 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <CUnit/Basic.h>
+/* include test cases' include files here */
+#include "wslay_frame_test.h"
+#include "wslay_event_test.h"
+#include "wslay_queue_test.h"
+
+int init_suite1(void)
+{
+ return 0;
+}
+
+int clean_suite1(void)
+{
+ return 0;
+}
+
+
+int main()
+{
+ CU_pSuite pSuite = NULL;
+
+ /* initialize the CUnit test registry */
+ if (CUE_SUCCESS != CU_initialize_registry())
+ return CU_get_error();
+
+ /* add a suite to the registry */
+ pSuite = CU_add_suite("libwslay_TestSuite", init_suite1, clean_suite1);
+ if (NULL == pSuite) {
+ CU_cleanup_registry();
+ return CU_get_error();
+ }
+
+ /* add the tests to the suite */
+ if(!CU_add_test(pSuite, "wslay_frame_context_init",
+ test_wslay_frame_context_init) ||
+ !CU_add_test(pSuite, "wslay_frame_recv", test_wslay_frame_recv) ||
+ !CU_add_test(pSuite, "wslay_frame_recv_1byte",
+ test_wslay_frame_recv_1byte) ||
+ !CU_add_test(pSuite, "wslay_frame_recv_fragmented",
+ test_wslay_frame_recv_fragmented) ||
+ !CU_add_test(pSuite, "wslay_frame_recv_interleaved_ctrl_frame",
+ test_wslay_frame_recv_interleaved_ctrl_frame) ||
+ !CU_add_test(pSuite, "wslay_frame_recv_zero_payloadlen",
+ test_wslay_frame_recv_zero_payloadlen) ||
+ !CU_add_test(pSuite, "wslay_frame_recv_too_large_payload",
+ test_wslay_frame_recv_too_large_payload) ||
+ !CU_add_test(pSuite, "wslay_frame_recv_ctrl_too_large_payload",
+ test_wslay_frame_recv_ctrl_frame_too_large_payload) ||
+ !CU_add_test(pSuite, "wslay_frame_recv_minimum_ext_payload16",
+ test_wslay_frame_recv_minimum_ext_payload16) ||
+ !CU_add_test(pSuite, "wslay_frame_recv_minimum_ext_payload64",
+ test_wslay_frame_recv_minimum_ext_payload64) ||
+ !CU_add_test(pSuite, "wslay_frame_send", test_wslay_frame_send) ||
+ !CU_add_test(pSuite, "wslay_frame_send_fragmented",
+ test_wslay_frame_send_fragmented) ||
+ !CU_add_test(pSuite, "wslay_frame_send_interleaved_ctrl_frame",
+ test_wslay_frame_send_interleaved_ctrl_frame) ||
+ !CU_add_test(pSuite, "wslay_frame_send_1byte_masked",
+ test_wslay_frame_send_1byte_masked) ||
+ !CU_add_test(pSuite, "wslay_frame_send_zero_payloadlen",
+ test_wslay_frame_send_zero_payloadlen) ||
+ !CU_add_test(pSuite, "wslay_frame_send_too_large_payload",
+ test_wslay_frame_send_too_large_payload) ||
+ !CU_add_test(pSuite, "wslay_frame_send_ctrl_frame_too_large_payload",
+ test_wslay_frame_send_ctrl_frame_too_large_payload) ||
+ !CU_add_test(pSuite, "wslay_event_send_fragmented_msg",
+ test_wslay_event_send_fragmented_msg) ||
+ !CU_add_test(pSuite, "wslay_event_send_fragmented_msg_with_ctrl",
+ test_wslay_event_send_fragmented_msg_with_ctrl) ||
+ !CU_add_test(pSuite, "wslay_event_send_ctrl_msg_first",
+ test_wslay_event_send_ctrl_msg_first) ||
+ !CU_add_test(pSuite, "wslay_event_queue_close",
+ test_wslay_event_queue_close) ||
+ !CU_add_test(pSuite, "wslay_event_queue_close_without_code",
+ test_wslay_event_queue_close_without_code) ||
+ !CU_add_test(pSuite, "wslay_event_recv_close_without_code",
+ test_wslay_event_recv_close_without_code) ||
+ !CU_add_test(pSuite, "wslay_event_reply_close",
+ test_wslay_event_reply_close) ||
+ !CU_add_test(pSuite, "wslay_event_no_more_msg",
+ test_wslay_event_no_more_msg) ||
+ !CU_add_test(pSuite, "wslay_event_callback_failure",
+ test_wslay_event_callback_failure) ||
+ !CU_add_test(pSuite, "wslay_event_no_buffering",
+ test_wslay_event_no_buffering) ||
+ !CU_add_test(pSuite, "wslay_event_frame_too_big",
+ test_wslay_event_frame_too_big) ||
+ !CU_add_test(pSuite, "wslay_event_message_too_big",
+ test_wslay_event_message_too_big) ||
+ !CU_add_test(pSuite, "wslay_queue", test_wslay_queue)) {
+ CU_cleanup_registry();
+ return CU_get_error();
+ }
+
+ /* Run all tests using the CUnit Basic interface */
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ CU_cleanup_registry();
+ return CU_get_error();
+}
diff --git a/deps/wslay/tests/wslay_event_test.c b/deps/wslay/tests/wslay_event_test.c
new file mode 100644
index 00000000..15bfdfa4
--- /dev/null
+++ b/deps/wslay/tests/wslay_event_test.c
@@ -0,0 +1,491 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "wslay_event_test.h"
+
+#include <assert.h>
+
+#include <CUnit/CUnit.h>
+
+#include "wslay_event.h"
+
+struct scripted_data_feed {
+ uint8_t data[8192];
+ uint8_t* datamark;
+ uint8_t* datalimit;
+ size_t feedseq[8192];
+ size_t seqidx;
+};
+
+struct accumulator {
+ uint8_t buf[4096];
+ size_t length;
+};
+
+struct my_user_data {
+ struct scripted_data_feed *df;
+ struct accumulator *acc;
+};
+
+static void scripted_data_feed_init(struct scripted_data_feed *df,
+ const uint8_t *data, size_t data_length)
+{
+ memset(df, 0, sizeof(struct scripted_data_feed));
+ memcpy(df->data, data, data_length);
+ df->datamark = df->data;
+ df->datalimit = df->data+data_length;
+ df->feedseq[0] = data_length;
+}
+
+static ssize_t scripted_read_callback
+(wslay_event_context_ptr ctx,
+ uint8_t *data, size_t len, const union wslay_event_msg_source *source,
+ int *eof, void *user_data)
+{
+ struct scripted_data_feed *df = (struct scripted_data_feed*)source->data;
+ size_t wlen = df->feedseq[df->seqidx] > len ? len : df->feedseq[df->seqidx];
+ memcpy(data, df->datamark, wlen);
+ df->datamark += wlen;
+ if(wlen <= len) {
+ ++df->seqidx;
+ } else {
+ df->feedseq[df->seqidx] -= wlen;
+ }
+ if(df->datamark == df->datalimit) {
+ *eof = 1;
+ }
+ return wlen;
+}
+
+static ssize_t scripted_recv_callback(wslay_event_context_ptr ctx,
+ uint8_t* data, size_t len, int flags,
+ void *user_data)
+{
+ struct scripted_data_feed *df = ((struct my_user_data*)user_data)->df;
+ size_t wlen = df->feedseq[df->seqidx] > len ? len : df->feedseq[df->seqidx];
+ memcpy(data, df->datamark, wlen);
+ df->datamark += wlen;
+ if(wlen <= len) {
+ ++df->seqidx;
+ } else {
+ df->feedseq[df->seqidx] -= wlen;
+ }
+ return wlen;
+}
+
+static ssize_t accumulator_send_callback(wslay_event_context_ptr ctx,
+ const uint8_t *buf, size_t len,
+ int flags, void* user_data)
+{
+ struct accumulator *acc = ((struct my_user_data*)user_data)->acc;
+ assert(acc->length+len < sizeof(acc->buf));
+ memcpy(acc->buf+acc->length, buf, len);
+ acc->length += len;
+ return len;
+}
+
+static ssize_t one_accumulator_send_callback(wslay_event_context_ptr ctx,
+ const uint8_t *buf, size_t len,
+ int flags, void* user_data)
+{
+ struct accumulator *acc = ((struct my_user_data*)user_data)->acc;
+ assert(len > 0);
+ memcpy(acc->buf+acc->length, buf, 1);
+ acc->length += 1;
+ return 1;
+}
+
+static ssize_t fail_recv_callback(wslay_event_context_ptr ctx,
+ uint8_t* data, size_t len, int flags,
+ void *user_data)
+{
+ wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
+ return -1;
+}
+
+static ssize_t fail_send_callback(wslay_event_context_ptr ctx,
+ const uint8_t *buf, size_t len, int flags,
+ void* user_data)
+{
+ wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
+ return -1;
+}
+
+
+void test_wslay_event_send_fragmented_msg()
+{
+ wslay_event_context_ptr ctx;
+ struct wslay_event_callbacks callbacks;
+ struct my_user_data ud;
+ struct accumulator acc;
+ const char msg[] = "Hello";
+ struct scripted_data_feed df;
+ struct wslay_event_fragmented_msg arg;
+ const uint8_t ans[] = {
+ 0x01, 0x03, 0x48, 0x65, 0x6c,
+ 0x80, 0x02, 0x6c, 0x6f
+ };
+ scripted_data_feed_init(&df, (const uint8_t*)msg, sizeof(msg)-1);
+ df.feedseq[0] = 3;
+ df.feedseq[1] = 2;
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.send_callback = accumulator_send_callback;
+ memset(&acc, 0, sizeof(acc));
+ ud.acc = &acc;
+ wslay_event_context_server_init(&ctx, &callbacks, &ud);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.opcode = WSLAY_TEXT_FRAME;
+ arg.source.data = &df;
+ arg.read_callback = scripted_read_callback;
+ CU_ASSERT(0 == wslay_event_queue_fragmented_msg(ctx, &arg));
+ CU_ASSERT(0 == wslay_event_send(ctx));
+ CU_ASSERT_EQUAL(9, acc.length);
+ CU_ASSERT(0 == memcmp(ans, acc.buf, acc.length));
+ wslay_event_context_free(ctx);
+}
+
+void test_wslay_event_send_fragmented_msg_with_ctrl()
+{
+ int i;
+ wslay_event_context_ptr ctx;
+ struct wslay_event_callbacks callbacks;
+ struct my_user_data ud;
+ struct accumulator acc;
+ const char msg[] = "Hello";
+ struct scripted_data_feed df;
+ struct wslay_event_fragmented_msg arg;
+ struct wslay_event_msg ctrl_arg;
+ const uint8_t ans[] = {
+ 0x01, 0x03, 0x48, 0x65, 0x6c, /* "Hel" */
+ 0x89, 0x00, /* unmasked ping */
+ 0x80, 0x02, 0x6c, 0x6f /* "lo" */
+ };
+ scripted_data_feed_init(&df, (const uint8_t*)msg, sizeof(msg)-1);
+ df.feedseq[0] = 3;
+ df.feedseq[1] = 2;
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.send_callback = one_accumulator_send_callback;
+ memset(&acc, 0, sizeof(acc));
+ ud.acc = &acc;
+ wslay_event_context_server_init(&ctx, &callbacks, &ud);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.opcode = WSLAY_TEXT_FRAME;
+ arg.source.data = &df;
+ arg.read_callback = scripted_read_callback;
+ CU_ASSERT(0 == wslay_event_queue_fragmented_msg(ctx, &arg));
+ CU_ASSERT(1 == wslay_event_get_queued_msg_count(ctx));
+ CU_ASSERT(0 == wslay_event_get_queued_msg_length(ctx));
+ CU_ASSERT(0 == wslay_event_send(ctx));
+
+ memset(&ctrl_arg, 0, sizeof(ctrl_arg));
+ ctrl_arg.opcode = WSLAY_PING;
+ ctrl_arg.msg_length = 0;
+ CU_ASSERT(0 == wslay_event_queue_msg(ctx, &ctrl_arg));
+ CU_ASSERT(2 == wslay_event_get_queued_msg_count(ctx));
+ for(i = 0; i < 10; ++i) {
+ CU_ASSERT(0 == wslay_event_send(ctx));
+ }
+ CU_ASSERT(0 == wslay_event_get_queued_msg_count(ctx));
+ CU_ASSERT(11 == acc.length);
+ CU_ASSERT(0 == memcmp(ans, acc.buf, acc.length));
+ wslay_event_context_free(ctx);
+}
+
+void test_wslay_event_send_ctrl_msg_first()
+{
+ wslay_event_context_ptr ctx;
+ struct wslay_event_callbacks callbacks;
+ struct my_user_data ud;
+ struct accumulator acc;
+ const char msg[] = "Hello";
+ struct wslay_event_msg arg;
+ const uint8_t ans[] = {
+ 0x89, 0x00, /* unmasked ping */
+ 0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f /* "Hello" */
+ };
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.send_callback = accumulator_send_callback;
+ memset(&acc, 0, sizeof(acc));
+ ud.acc = &acc;
+ wslay_event_context_server_init(&ctx, &callbacks, &ud);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.opcode = WSLAY_PING;
+ arg.msg_length = 0;
+ CU_ASSERT(0 == wslay_event_queue_msg(ctx, &arg));
+ arg.opcode = WSLAY_TEXT_FRAME;
+ arg.msg = (const uint8_t*)msg;
+ arg.msg_length = 5;
+ CU_ASSERT(0 == wslay_event_queue_msg(ctx, &arg));
+ CU_ASSERT(0 == wslay_event_send(ctx));
+ CU_ASSERT(9 == acc.length);
+ CU_ASSERT(0 == memcmp(ans, acc.buf, acc.length));
+ wslay_event_context_free(ctx);
+}
+
+void test_wslay_event_queue_close()
+{
+ wslay_event_context_ptr ctx;
+ struct wslay_event_callbacks callbacks;
+ struct my_user_data ud;
+ struct accumulator acc;
+ const char msg[] = "H";
+ const uint8_t ans[] = {
+ 0x88, 0x03, 0x03, 0xf1, 0x48 /* "H" */
+ };
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.send_callback = accumulator_send_callback;
+ memset(&acc, 0, sizeof(acc));
+ ud.acc = &acc;
+ wslay_event_context_server_init(&ctx, &callbacks, &ud);
+ CU_ASSERT(0 == wslay_event_queue_close(ctx, WSLAY_CODE_MESSAGE_TOO_BIG,
+ (const uint8_t*)msg, 1));
+ CU_ASSERT(0 == wslay_event_send(ctx));
+ CU_ASSERT(5 == acc.length);
+ CU_ASSERT(0 == memcmp(ans, acc.buf, acc.length));
+ CU_ASSERT(1 == wslay_event_get_close_sent(ctx));
+ wslay_event_context_free(ctx);
+}
+
+void test_wslay_event_queue_close_without_code()
+{
+ wslay_event_context_ptr ctx;
+ struct wslay_event_callbacks callbacks;
+ struct my_user_data ud;
+ struct accumulator acc;
+ const uint8_t ans[] = { 0x88, 0x00 };
+ struct wslay_event_msg ping = { WSLAY_PING, NULL, 0 };
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.send_callback = accumulator_send_callback;
+ memset(&acc, 0, sizeof(acc));
+ ud.acc = &acc;
+ wslay_event_context_server_init(&ctx, &callbacks, &ud);
+ CU_ASSERT(0 == wslay_event_queue_msg(ctx, &ping));
+ // See that ping is not sent because close frame is queued
+ CU_ASSERT(0 == wslay_event_queue_close(ctx, 0, NULL, 0));
+ CU_ASSERT(0 == wslay_event_send(ctx));
+ CU_ASSERT(2 == acc.length);
+ CU_ASSERT(0 == memcmp(ans, acc.buf, acc.length));
+ CU_ASSERT(1 == wslay_event_get_close_sent(ctx));
+ CU_ASSERT(WSLAY_CODE_NO_STATUS_RCVD ==
+ wslay_event_get_status_code_sent(ctx));
+ wslay_event_context_free(ctx);
+}
+
+void test_wslay_event_recv_close_without_code()
+{
+ wslay_event_context_ptr ctx;
+ struct wslay_event_callbacks callbacks;
+ struct my_user_data ud;
+ const uint8_t msg[] = { 0x88u, 0x00 };
+ struct scripted_data_feed df;
+ scripted_data_feed_init(&df, (const uint8_t*)msg, sizeof(msg));
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.recv_callback = scripted_recv_callback;
+ ud.df = &df;
+ wslay_event_context_client_init(&ctx, &callbacks, &ud);
+ CU_ASSERT(0 == wslay_event_recv(ctx));
+ CU_ASSERT(1 == wslay_event_get_close_received(ctx));
+ CU_ASSERT(WSLAY_CODE_NO_STATUS_RCVD ==
+ wslay_event_get_status_code_received(ctx));
+ wslay_event_context_free(ctx);
+}
+
+void test_wslay_event_reply_close()
+{
+ wslay_event_context_ptr ctx;
+ struct wslay_event_callbacks callbacks;
+ struct my_user_data ud;
+ struct accumulator acc;
+ /* Masked close frame with code = 1009, reason = "Hello" */
+ const uint8_t msg[] = { 0x88u, 0x87u, 0x00u, 0x00u, 0x00u, 0x00u,
+ 0x03, 0xf1, /* 1009 */
+ 0x48, 0x65, 0x6c, 0x6c, 0x6f /* "Hello" */
+ };
+ const uint8_t ans[] = { 0x88u, 0x07u,
+ 0x03, 0xf1, /* 1009 */
+ 0x48, 0x65, 0x6c, 0x6c, 0x6f /* "Hello" */
+ };
+ struct scripted_data_feed df;
+ scripted_data_feed_init(&df, (const uint8_t*)msg, sizeof(msg));
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.send_callback = accumulator_send_callback;
+ callbacks.recv_callback = scripted_recv_callback;
+ memset(&acc, 0, sizeof(acc));
+ ud.df = &df;
+ ud.acc = &acc;
+ wslay_event_context_server_init(&ctx, &callbacks, &ud);
+ CU_ASSERT(0 == wslay_event_recv(ctx));
+ CU_ASSERT(1 == wslay_event_get_queued_msg_count(ctx));
+ /* 7 bytes = 2 bytes status code + "Hello" */
+ CU_ASSERT(7 == wslay_event_get_queued_msg_length(ctx));
+ CU_ASSERT(1 == wslay_event_get_close_received(ctx));
+ CU_ASSERT(WSLAY_CODE_MESSAGE_TOO_BIG ==
+ wslay_event_get_status_code_received(ctx));
+ CU_ASSERT(WSLAY_CODE_ABNORMAL_CLOSURE ==
+ wslay_event_get_status_code_sent(ctx));
+ CU_ASSERT(0 == wslay_event_send(ctx));
+ CU_ASSERT(0 == wslay_event_get_queued_msg_count(ctx));
+ CU_ASSERT(0 == wslay_event_get_queued_msg_length(ctx));
+ CU_ASSERT(9 == acc.length);
+ CU_ASSERT(0 == memcmp(ans, acc.buf, acc.length));
+ CU_ASSERT(1 == wslay_event_get_close_sent(ctx));
+ CU_ASSERT(WSLAY_CODE_MESSAGE_TOO_BIG ==
+ wslay_event_get_status_code_received(ctx));
+ CU_ASSERT(WSLAY_CODE_MESSAGE_TOO_BIG ==
+ wslay_event_get_status_code_sent(ctx));
+ wslay_event_context_free(ctx);
+}
+
+void test_wslay_event_no_more_msg()
+{
+ wslay_event_context_ptr ctx;
+ struct wslay_event_callbacks callbacks;
+ memset(&callbacks, 0, sizeof(callbacks));
+ wslay_event_context_server_init(&ctx, &callbacks, NULL);
+ CU_ASSERT(0 == wslay_event_queue_close(ctx, 0, NULL, 0));
+ CU_ASSERT(WSLAY_ERR_NO_MORE_MSG == wslay_event_queue_close(ctx, 0, NULL, 0));
+ wslay_event_context_free(ctx);
+}
+
+void test_wslay_event_callback_failure()
+{
+ wslay_event_context_ptr ctx;
+ struct wslay_event_callbacks callbacks;
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.recv_callback = fail_recv_callback;
+ callbacks.send_callback = fail_send_callback;
+ wslay_event_context_server_init(&ctx, &callbacks, NULL);
+ CU_ASSERT(WSLAY_ERR_CALLBACK_FAILURE == wslay_event_recv(ctx));
+ /* close control frame is in queue */
+ CU_ASSERT(WSLAY_ERR_CALLBACK_FAILURE == wslay_event_send(ctx));
+ wslay_event_context_free(ctx);
+}
+
+static void no_buffering_callback(wslay_event_context_ptr ctx,
+ const struct wslay_event_on_msg_recv_arg *arg,
+ void *user_data)
+{
+ if(arg->opcode == WSLAY_PING) {
+ CU_ASSERT(3 == arg->msg_length);
+ CU_ASSERT(0 == memcmp("Foo", arg->msg, arg->msg_length));
+ } else {
+ CU_ASSERT(WSLAY_TEXT_FRAME == arg->opcode);
+ CU_ASSERT(0 == arg->msg_length);
+ }
+}
+
+void test_wslay_event_no_buffering()
+{
+ wslay_event_context_ptr ctx;
+ struct wslay_event_callbacks callbacks;
+ struct my_user_data ud;
+ const uint8_t msg[] = {
+ 0x01, 0x03, 0x48, 0x65, 0x6c, /* "Hel" */
+ 0x89, 0x03, 0x46, 0x6f, 0x6f, /* ping with "Foo" */
+ 0x80, 0x02, 0x6c, 0x6f, /* "lo" */
+ };
+ struct scripted_data_feed df;
+ scripted_data_feed_init(&df, (const uint8_t*)msg, sizeof(msg));
+ memset(&callbacks, 0, sizeof(callbacks));
+ ud.df = &df;
+ callbacks.recv_callback = scripted_recv_callback;
+ callbacks.on_msg_recv_callback = no_buffering_callback;
+ wslay_event_context_client_init(&ctx, &callbacks, &ud);
+ wslay_event_config_set_no_buffering(ctx, 1);
+ CU_ASSERT(0 == wslay_event_recv(ctx));
+ /* pong must be queued */
+ CU_ASSERT(wslay_event_want_write(ctx));
+ wslay_event_context_free(ctx);
+}
+
+void test_wslay_event_frame_too_big()
+{
+ wslay_event_context_ptr ctx;
+ struct wslay_event_callbacks callbacks;
+ struct my_user_data ud;
+ struct accumulator acc;
+ /* Masked text frame */
+ const uint8_t msg[] = { 0x81, 0x85, 0x00, 0x00, 0x00, 0x00,
+ 0x48, 0x65, 0x6c, 0x6c, 0x6f /* "Hello" */
+ };
+ const uint8_t ans[] = { 0x88, 0x02,
+ 0x03, 0xf1 /* 1009 */
+ };
+ struct scripted_data_feed df;
+ scripted_data_feed_init(&df, (const uint8_t*)msg, sizeof(msg));
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.send_callback = accumulator_send_callback;
+ callbacks.recv_callback = scripted_recv_callback;
+ memset(&acc, 0, sizeof(acc));
+ ud.df = &df;
+ ud.acc = &acc;
+ wslay_event_context_server_init(&ctx, &callbacks, &ud);
+ wslay_event_config_set_max_recv_msg_length(ctx, 4);
+ CU_ASSERT(0 == wslay_event_recv(ctx));
+ CU_ASSERT(0 == wslay_event_send(ctx));
+ CU_ASSERT(4 == acc.length);
+ CU_ASSERT(0 == memcmp(ans, acc.buf, acc.length));
+ CU_ASSERT(1 == wslay_event_get_close_sent(ctx));
+ CU_ASSERT(WSLAY_CODE_MESSAGE_TOO_BIG ==
+ wslay_event_get_status_code_sent(ctx));
+ wslay_event_context_free(ctx);
+}
+
+void test_wslay_event_message_too_big()
+{
+ wslay_event_context_ptr ctx;
+ struct wslay_event_callbacks callbacks;
+ struct my_user_data ud;
+ struct accumulator acc;
+ /* Masked text 2 frames */
+ const uint8_t msg[] = { 0x01, 0x85, 0x00, 0x00, 0x00, 0x00,
+ 0x48, 0x65, 0x6c, 0x6c, 0x6f, /* "Hello" */
+ 0x80, 0x85, 0x00, 0x00, 0x00, 0x00,
+ 0x48, 0x65, 0x6c, 0x6c, 0x6f /* "Hello" */
+ };
+ const uint8_t ans[] = { 0x88, 0x02,
+ 0x03, 0xf1 /* 1009 */
+ };
+ struct scripted_data_feed df;
+ scripted_data_feed_init(&df, (const uint8_t*)msg, sizeof(msg));
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.send_callback = accumulator_send_callback;
+ callbacks.recv_callback = scripted_recv_callback;
+ memset(&acc, 0, sizeof(acc));
+ ud.df = &df;
+ ud.acc = &acc;
+ wslay_event_context_server_init(&ctx, &callbacks, &ud);
+ wslay_event_config_set_max_recv_msg_length(ctx, 9);
+ CU_ASSERT(0 == wslay_event_recv(ctx));
+ CU_ASSERT(0 == wslay_event_send(ctx));
+ CU_ASSERT(4 == acc.length);
+ CU_ASSERT(0 == memcmp(ans, acc.buf, acc.length));
+ CU_ASSERT(1 == wslay_event_get_close_sent(ctx));
+ CU_ASSERT(WSLAY_CODE_MESSAGE_TOO_BIG ==
+ wslay_event_get_status_code_sent(ctx));
+ wslay_event_context_free(ctx);
+}
diff --git a/deps/wslay/tests/wslay_event_test.h b/deps/wslay/tests/wslay_event_test.h
new file mode 100644
index 00000000..c33e906e
--- /dev/null
+++ b/deps/wslay/tests/wslay_event_test.h
@@ -0,0 +1,41 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef WSLAY_EVENT_TEST_H
+#define WSLAY_EVENT_TEST_H
+
+void test_wslay_event_send_fragmented_msg();
+void test_wslay_event_send_fragmented_msg_with_ctrl();
+void test_wslay_event_send_ctrl_msg_first();
+void test_wslay_event_queue_close();
+void test_wslay_event_queue_close_without_code();
+void test_wslay_event_recv_close_without_code();
+void test_wslay_event_reply_close();
+void test_wslay_event_no_more_msg();
+void test_wslay_event_callback_failure();
+void test_wslay_event_no_buffering();
+void test_wslay_event_frame_too_big();
+void test_wslay_event_message_too_big();
+
+#endif // WSLAY_EVENT_TEST_H
diff --git a/deps/wslay/tests/wslay_frame_test.c b/deps/wslay/tests/wslay_frame_test.c
new file mode 100644
index 00000000..66d0e955
--- /dev/null
+++ b/deps/wslay/tests/wslay_frame_test.c
@@ -0,0 +1,560 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "wslay_frame_test.h"
+
+#include <assert.h>
+
+#include <CUnit/CUnit.h>
+
+#include "wslay_frame.h"
+
+void test_wslay_frame_context_init()
+{
+ wslay_frame_context_ptr ctx;
+ struct wslay_frame_callbacks callbacks;
+ int user_data;
+ CU_ASSERT_FALSE(wslay_frame_context_init(&ctx, &callbacks, &user_data));
+
+ wslay_frame_context_free(ctx);
+}
+
+struct scripted_data_feed {
+ uint8_t data[8192];
+ uint8_t* datamark;
+ uint8_t* datalimit;
+ size_t feedseq[8192];
+ size_t seqidx;
+};
+
+static void scripted_data_feed_init(struct scripted_data_feed *df,
+ uint8_t *data, size_t data_length)
+{
+ memset(df, 0, sizeof(struct scripted_data_feed));
+ memcpy(df->data, data, data_length);
+ df->datamark = df->data;
+ df->datalimit = df->data+data_length;
+ df->feedseq[0] = data_length;
+}
+
+static ssize_t scripted_recv_callback(uint8_t* data, size_t len, int flags,
+ void *user_data)
+{
+ struct scripted_data_feed *df = (struct scripted_data_feed*)user_data;
+ size_t wlen = df->feedseq[df->seqidx] > len ? len : df->feedseq[df->seqidx];
+ memcpy(data, df->datamark, wlen);
+ df->datamark += wlen;
+ if(wlen <= len) {
+ ++df->seqidx;
+ } else {
+ df->feedseq[df->seqidx] -= wlen;
+ }
+ return wlen;
+}
+
+static ssize_t scripted_send_callback(const uint8_t* data, size_t len,
+ int flags, void *user_data)
+{
+ struct scripted_data_feed *df = (struct scripted_data_feed*)user_data;
+ size_t wlen = df->feedseq[df->seqidx] > len ? len : df->feedseq[df->seqidx];
+ memcpy(df->datamark, data, wlen);
+ df->datamark += wlen;
+ if(wlen <= len) {
+ ++df->seqidx;
+ } else {
+ df->feedseq[df->seqidx] -= wlen;
+ }
+ return wlen;
+}
+
+void test_wslay_frame_recv()
+{
+ wslay_frame_context_ptr ctx;
+ struct wslay_frame_callbacks callbacks = { NULL,
+ scripted_recv_callback,
+ NULL };
+ struct scripted_data_feed df;
+ struct wslay_frame_iocb iocb;
+ /* Masked text frame containing "Hello" */
+ uint8_t msg[] = { 0x81u, 0x85u, 0x37u, 0xfau, 0x21u, 0x3du, 0x7fu, 0x9fu,
+ 0x4du, 0x51u, 0x58u };
+ scripted_data_feed_init(&df, msg, sizeof(msg));
+ wslay_frame_context_init(&ctx, &callbacks, &df);
+
+ CU_ASSERT(5 == wslay_frame_recv(ctx, &iocb));
+ CU_ASSERT_EQUAL(1, iocb.fin);
+ CU_ASSERT_EQUAL(0, iocb.rsv);
+ CU_ASSERT_EQUAL(0x1, iocb.opcode);
+ CU_ASSERT_EQUAL(5, iocb.payload_length);
+ CU_ASSERT_EQUAL(1, iocb.mask);
+ CU_ASSERT_EQUAL(5, iocb.data_length);
+ CU_ASSERT(memcmp("Hello", iocb.data, iocb.data_length) == 0);
+
+ wslay_frame_context_free(ctx);
+}
+
+void test_wslay_frame_recv_1byte()
+{
+ wslay_frame_context_ptr ctx;
+ struct wslay_frame_callbacks callbacks = { NULL,
+ scripted_recv_callback,
+ NULL };
+ struct scripted_data_feed df;
+ struct wslay_frame_iocb iocb;
+ int i;
+ /* Masked text frame containing "Hello" */
+ uint8_t msg[] = { 0x81u, 0x85u, 0x37u, 0xfau, 0x21u, 0x3du, 0x7fu, 0x9fu,
+ 0x4du, 0x51u, 0x58u };
+ scripted_data_feed_init(&df, msg, sizeof(msg));
+ for(i = 0; i < sizeof(msg); ++i) {
+ df.feedseq[i] = 1;
+ }
+ wslay_frame_context_init(&ctx, &callbacks, &df);
+
+ for(i = 0; i < 4; ++i) {
+ CU_ASSERT(WSLAY_ERR_WANT_READ == wslay_frame_recv(ctx, &iocb));
+ }
+ for(i = 0; i < 5; ++i) {
+ CU_ASSERT(1 == wslay_frame_recv(ctx, &iocb));
+ CU_ASSERT_EQUAL(1, iocb.fin);
+ CU_ASSERT_EQUAL(0, iocb.rsv);
+ CU_ASSERT_EQUAL(0x1, iocb.opcode);
+ CU_ASSERT_EQUAL(5, iocb.payload_length);
+ CU_ASSERT_EQUAL(1, iocb.mask);
+ CU_ASSERT_EQUAL(1, iocb.data_length);
+ CU_ASSERT_EQUAL(msg[6+i]^msg[2+i%4], iocb.data[0]);
+ }
+ CU_ASSERT(WSLAY_ERR_WANT_READ == wslay_frame_recv(ctx, &iocb));
+
+ wslay_frame_context_free(ctx);
+}
+
+void test_wslay_frame_recv_fragmented()
+{
+ wslay_frame_context_ptr ctx;
+ struct wslay_frame_callbacks callbacks = { NULL,
+ scripted_recv_callback,
+ NULL };
+ struct scripted_data_feed df;
+ struct wslay_frame_iocb iocb;
+ /* Unmasked message */
+ uint8_t msg[] = { 0x01, 0x03, 0x48, 0x65, 0x6c, /* "Hel" */
+ 0x80, 0x02, 0x6c, 0x6f }; /* "lo" */
+ scripted_data_feed_init(&df, msg, sizeof(msg));
+ df.feedseq[0] = 5;
+ df.feedseq[1] = 4;
+ wslay_frame_context_init(&ctx, &callbacks, &df);
+
+ CU_ASSERT(3 == wslay_frame_recv(ctx, &iocb));
+ CU_ASSERT_EQUAL(0, iocb.fin);
+ CU_ASSERT_EQUAL(0, iocb.rsv);
+ CU_ASSERT_EQUAL(WSLAY_TEXT_FRAME, iocb.opcode);
+ CU_ASSERT_EQUAL(3, iocb.payload_length);
+ CU_ASSERT_EQUAL(0, iocb.mask);
+ CU_ASSERT_EQUAL(3, iocb.data_length);
+ CU_ASSERT(memcmp("Hel", iocb.data, iocb.data_length) == 0);
+
+ CU_ASSERT(2 == wslay_frame_recv(ctx, &iocb));
+ CU_ASSERT_EQUAL(1, iocb.fin);
+ CU_ASSERT_EQUAL(0, iocb.rsv);
+ CU_ASSERT_EQUAL(WSLAY_CONTINUATION_FRAME, iocb.opcode);
+ CU_ASSERT_EQUAL(2, iocb.payload_length);
+ CU_ASSERT_EQUAL(0, iocb.mask);
+ CU_ASSERT_EQUAL(2, iocb.data_length);
+ CU_ASSERT(memcmp("lo", iocb.data, iocb.data_length) == 0);
+
+ wslay_frame_context_free(ctx);
+}
+
+void test_wslay_frame_recv_interleaved_ctrl_frame()
+{
+ wslay_frame_context_ptr ctx;
+ struct wslay_frame_callbacks callbacks = { NULL,
+ scripted_recv_callback,
+ NULL };
+ struct scripted_data_feed df;
+ struct wslay_frame_iocb iocb;
+ /* Unmasked message */
+ uint8_t msg[] = { 0x01, 0x03, 0x48, 0x65, 0x6c, /* "Hel" */
+ /* ping with "Hello" */
+ 0x89, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f,
+ 0x80, 0x02, 0x6c, 0x6f }; /* "lo" */
+ scripted_data_feed_init(&df, msg, sizeof(msg));
+ df.feedseq[0] = 5;
+ df.feedseq[1] = 7,
+ df.feedseq[2] = 4;
+ wslay_frame_context_init(&ctx, &callbacks, &df);
+
+ CU_ASSERT(3 == wslay_frame_recv(ctx, &iocb));
+ CU_ASSERT_EQUAL(0, iocb.fin);
+ CU_ASSERT_EQUAL(0, iocb.rsv);
+ CU_ASSERT_EQUAL(WSLAY_TEXT_FRAME, iocb.opcode);
+ CU_ASSERT_EQUAL(3, iocb.payload_length);
+ CU_ASSERT_EQUAL(0, iocb.mask);
+ CU_ASSERT_EQUAL(3, iocb.data_length);
+ CU_ASSERT(memcmp("Hel", iocb.data, iocb.data_length) == 0);
+
+ CU_ASSERT(5 == wslay_frame_recv(ctx, &iocb));
+ CU_ASSERT_EQUAL(1, iocb.fin);
+ CU_ASSERT_EQUAL(0, iocb.rsv);
+ CU_ASSERT_EQUAL(WSLAY_PING, iocb.opcode);
+ CU_ASSERT_EQUAL(5, iocb.payload_length);
+ CU_ASSERT_EQUAL(0, iocb.mask);
+ CU_ASSERT_EQUAL(5, iocb.data_length);
+ CU_ASSERT(memcmp("Hello", iocb.data, iocb.data_length) == 0);
+
+ CU_ASSERT(2 == wslay_frame_recv(ctx, &iocb));
+ CU_ASSERT_EQUAL(1, iocb.fin);
+ CU_ASSERT_EQUAL(0, iocb.rsv);
+ CU_ASSERT_EQUAL(WSLAY_CONTINUATION_FRAME, iocb.opcode);
+ CU_ASSERT_EQUAL(2, iocb.payload_length);
+ CU_ASSERT_EQUAL(0, iocb.mask);
+ CU_ASSERT_EQUAL(2, iocb.data_length);
+ CU_ASSERT(memcmp("lo", iocb.data, iocb.data_length) == 0);
+
+ wslay_frame_context_free(ctx);
+}
+
+void test_wslay_frame_recv_zero_payloadlen()
+{
+ wslay_frame_context_ptr ctx;
+ struct wslay_frame_callbacks callbacks = { NULL,
+ scripted_recv_callback,
+ NULL };
+ struct scripted_data_feed df;
+ struct wslay_frame_iocb iocb;
+ /* Unmasked message */
+ uint8_t msg[] = { 0x81, 0x00 }; /* "" */
+ scripted_data_feed_init(&df, msg, sizeof(msg));
+ df.feedseq[0] = 2;
+ wslay_frame_context_init(&ctx, &callbacks, &df);
+
+ CU_ASSERT(0 == wslay_frame_recv(ctx, &iocb));
+ CU_ASSERT_EQUAL(1, iocb.fin);
+ CU_ASSERT_EQUAL(0, iocb.rsv);
+ CU_ASSERT_EQUAL(WSLAY_TEXT_FRAME, iocb.opcode);
+ CU_ASSERT_EQUAL(0, iocb.payload_length);
+ CU_ASSERT_EQUAL(0, iocb.mask);
+ CU_ASSERT_EQUAL(0, iocb.data_length);
+
+ wslay_frame_context_free(ctx);
+}
+
+void test_wslay_frame_recv_too_large_payload()
+{
+ wslay_frame_context_ptr ctx;
+ struct wslay_frame_callbacks callbacks = { NULL,
+ scripted_recv_callback,
+ NULL };
+ struct scripted_data_feed df;
+ struct wslay_frame_iocb iocb;
+ uint8_t msg[] = { 0x81, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ scripted_data_feed_init(&df, msg, sizeof(msg));
+ df.feedseq[0] = sizeof(msg);
+ wslay_frame_context_init(&ctx, &callbacks, &df);
+ CU_ASSERT_EQUAL(WSLAY_ERR_PROTO, wslay_frame_recv(ctx, &iocb));
+
+ wslay_frame_context_free(ctx);
+}
+
+void test_wslay_frame_recv_ctrl_frame_too_large_payload()
+{
+ wslay_frame_context_ptr ctx;
+ struct wslay_frame_callbacks callbacks = { NULL,
+ scripted_recv_callback,
+ NULL };
+ struct scripted_data_feed df;
+ struct wslay_frame_iocb iocb;
+ uint8_t msg[] = { 0x88, 0x7e };
+ scripted_data_feed_init(&df, msg, sizeof(msg));
+ df.feedseq[0] = sizeof(msg);
+ wslay_frame_context_init(&ctx, &callbacks, &df);
+ CU_ASSERT_EQUAL(WSLAY_ERR_PROTO, wslay_frame_recv(ctx, &iocb));
+
+ wslay_frame_context_free(ctx);
+}
+
+void test_wslay_frame_recv_minimum_ext_payload16()
+{
+ wslay_frame_context_ptr ctx;
+ struct wslay_frame_callbacks callbacks = { NULL,
+ scripted_recv_callback,
+ NULL };
+ struct scripted_data_feed df;
+ struct wslay_frame_iocb iocb;
+ uint8_t msg[] = { 0x81, 0x7e, 0x00, 0x7d };
+ scripted_data_feed_init(&df, msg, sizeof(msg));
+ df.feedseq[0] = sizeof(msg);
+ wslay_frame_context_init(&ctx, &callbacks, &df);
+ CU_ASSERT_EQUAL(WSLAY_ERR_PROTO, wslay_frame_recv(ctx, &iocb));
+
+ wslay_frame_context_free(ctx);
+}
+
+void test_wslay_frame_recv_minimum_ext_payload64()
+{
+ wslay_frame_context_ptr ctx;
+ struct wslay_frame_callbacks callbacks = { NULL,
+ scripted_recv_callback,
+ NULL };
+ struct scripted_data_feed df;
+ struct wslay_frame_iocb iocb;
+ uint8_t msg[] = { 0x81, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff};
+ scripted_data_feed_init(&df, msg, sizeof(msg));
+ df.feedseq[0] = sizeof(msg);
+ wslay_frame_context_init(&ctx, &callbacks, &df);
+ CU_ASSERT_EQUAL(WSLAY_ERR_PROTO, wslay_frame_recv(ctx, &iocb));
+
+ wslay_frame_context_free(ctx);
+}
+
+struct accumulator {
+ uint8_t buf[4096];
+ size_t length;
+};
+
+static ssize_t accumulator_send_callback(const uint8_t *buf, size_t len,
+ int flags, void* user_data)
+{
+ struct accumulator *acc = (struct accumulator*)user_data;
+ assert(acc->length+len < sizeof(acc->buf));
+ memcpy(acc->buf+acc->length, buf, len);
+ acc->length += len;
+ return len;
+}
+
+static int static_genmask_callback(uint8_t *buf, size_t len,
+ void* user_data)
+{
+ const static uint8_t makskey[] = { 0x37u, 0xfau, 0x21u, 0x3du };
+ memcpy(buf, makskey, 4);
+ return 0;
+}
+
+void test_wslay_frame_send()
+{
+ wslay_frame_context_ptr ctx;
+ struct wslay_frame_callbacks callbacks = { accumulator_send_callback,
+ NULL,
+ static_genmask_callback };
+ struct accumulator acc;
+ struct wslay_frame_iocb iocb;
+ /* Masked text frame containing "Hello" */
+ uint8_t msg[] = { 0x81u, 0x85u, 0x37u, 0xfau, 0x21u, 0x3du, 0x7fu, 0x9fu,
+ 0x4du, 0x51u, 0x58u };
+ wslay_frame_context_init(&ctx, &callbacks, &acc);
+ memset(&iocb, 0, sizeof(iocb));
+ acc.length = 0;
+ iocb.fin = 1;
+ iocb.opcode = WSLAY_TEXT_FRAME;
+ iocb.mask = 1;
+ iocb.payload_length = 5;
+ iocb.data = (const uint8_t*)"Hello";
+ iocb.data_length = 5;
+ CU_ASSERT(5 == wslay_frame_send(ctx, &iocb));
+ CU_ASSERT_EQUAL(sizeof(msg), acc.length);
+ CU_ASSERT(memcmp(msg, acc.buf, sizeof(msg)) == 0);
+
+ wslay_frame_context_free(ctx);
+}
+
+void test_wslay_frame_send_fragmented()
+{
+ wslay_frame_context_ptr ctx;
+ struct wslay_frame_callbacks callbacks = { accumulator_send_callback,
+ NULL,
+ static_genmask_callback };
+ struct accumulator acc;
+ struct wslay_frame_iocb iocb;
+ /* Unmasked message */
+ uint8_t msg1[] = { 0x01, 0x03, 0x48, 0x65, 0x6c }; /* "Hel" */
+ uint8_t msg2[] = { 0x80, 0x02, 0x6c, 0x6f }; /* "lo" */
+ wslay_frame_context_init(&ctx, &callbacks, &acc);
+ memset(&iocb, 0, sizeof(iocb));
+ acc.length = 0;
+ iocb.fin = 0;
+ iocb.opcode = WSLAY_TEXT_FRAME;
+ iocb.mask = 0;
+ iocb.payload_length = 3;
+ iocb.data = (const uint8_t*)"Hel";
+ iocb.data_length = 3;
+ CU_ASSERT(3 == wslay_frame_send(ctx, &iocb));
+ CU_ASSERT_EQUAL(sizeof(msg1), acc.length);
+ CU_ASSERT(memcmp(msg1, acc.buf, sizeof(msg1)) == 0);
+
+ acc.length = 0;
+ iocb.fin = 1;
+ iocb.opcode = WSLAY_CONTINUATION_FRAME;
+ iocb.payload_length = 2;
+ iocb.data = (const uint8_t*)"lo";
+ iocb.data_length = 2;
+ CU_ASSERT(2 == wslay_frame_send(ctx, &iocb));
+ CU_ASSERT_EQUAL(sizeof(msg2), acc.length);
+ CU_ASSERT(memcmp(msg2, acc.buf, sizeof(msg2)) == 0);
+
+ wslay_frame_context_free(ctx);
+}
+
+void test_wslay_frame_send_interleaved_ctrl_frame()
+{
+ wslay_frame_context_ptr ctx;
+ struct wslay_frame_callbacks callbacks = { accumulator_send_callback,
+ NULL,
+ static_genmask_callback };
+ struct accumulator acc;
+ struct wslay_frame_iocb iocb;
+ /* Unmasked message */
+ /* text with "Hel", with fin = 0 */
+ uint8_t msg1[] = { 0x01, 0x03, 0x48, 0x65, 0x6c };
+ /* ping with "Hello" */
+ uint8_t msg2[] = { 0x89, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f };
+ /* text with "lo", continuation frame for msg1, with fin = 1 */
+ uint8_t msg3[] = { 0x80, 0x02, 0x6c, 0x6f };
+ wslay_frame_context_init(&ctx, &callbacks, &acc);
+ memset(&iocb, 0, sizeof(iocb));
+ acc.length = 0;
+ iocb.fin = 0;
+ iocb.opcode = WSLAY_TEXT_FRAME;
+ iocb.mask = 0;
+ iocb.payload_length = 3;
+ iocb.data = (const uint8_t*)"Hel";
+ iocb.data_length = 3;
+ CU_ASSERT(3 == wslay_frame_send(ctx, &iocb));
+ CU_ASSERT_EQUAL(sizeof(msg1), acc.length);
+ CU_ASSERT(memcmp(msg1, acc.buf, sizeof(msg1)) == 0);
+
+ acc.length = 0;
+ iocb.fin = 1;
+ iocb.opcode = WSLAY_PING;
+ iocb.payload_length = 5;
+ iocb.data = (const uint8_t*)"Hello";
+ iocb.data_length = 5;
+ CU_ASSERT(5 == wslay_frame_send(ctx, &iocb));
+ CU_ASSERT_EQUAL(sizeof(msg2), acc.length);
+ CU_ASSERT(memcmp(msg2, acc.buf, sizeof(msg2)) == 0);
+
+ acc.length = 0;
+ iocb.fin = 1;
+ iocb.opcode = WSLAY_CONTINUATION_FRAME;
+ iocb.payload_length = 2;
+ iocb.data = (const uint8_t*)"lo";
+ iocb.data_length = 2;
+ CU_ASSERT(2 == wslay_frame_send(ctx, &iocb));
+ CU_ASSERT_EQUAL(sizeof(msg3), acc.length);
+ CU_ASSERT(memcmp(msg3, acc.buf, sizeof(msg3)) == 0);
+
+ wslay_frame_context_free(ctx);
+}
+
+void test_wslay_frame_send_1byte_masked()
+{
+ wslay_frame_context_ptr ctx;
+ struct wslay_frame_callbacks callbacks = { scripted_send_callback,
+ NULL,
+ static_genmask_callback };
+ struct wslay_frame_iocb iocb;
+ /* Masked text frame containing "Hello" */
+ uint8_t msg[] = { 0x81u, 0x85u, 0x37u, 0xfau, 0x21u, 0x3du, 0x7fu, 0x9fu,
+ 0x4du, 0x51u, 0x58u };
+ uint8_t hello[] = "Hello";
+ struct scripted_data_feed df;
+ int i;
+ scripted_data_feed_init(&df, NULL, 0);
+ for(i = 0; i < sizeof(msg); ++i) {
+ df.feedseq[i] = 1;
+ }
+ wslay_frame_context_init(&ctx, &callbacks, &df);
+ memset(&iocb, 0, sizeof(iocb));
+ iocb.fin = 1;
+ iocb.opcode = WSLAY_TEXT_FRAME;
+ iocb.mask = 1;
+ iocb.payload_length = 5;
+ iocb.data = hello;
+ iocb.data_length = sizeof(hello)-1;
+ for(i = 0; i < 5; ++i) {
+ CU_ASSERT_EQUAL(WSLAY_ERR_WANT_WRITE, wslay_frame_send(ctx, &iocb));
+ }
+ CU_ASSERT_EQUAL(5, wslay_frame_send(ctx, &iocb));
+
+ wslay_frame_context_free(ctx);
+}
+
+void test_wslay_frame_send_zero_payloadlen()
+{
+ wslay_frame_context_ptr ctx;
+ struct wslay_frame_callbacks callbacks = { accumulator_send_callback,
+ NULL,
+ static_genmask_callback };
+ struct accumulator acc;
+ struct wslay_frame_iocb iocb;
+ /* Unmasked message */
+ uint8_t msg[] = { 0x81, 0x00 }; /* "" */
+ acc.length = 0;
+ wslay_frame_context_init(&ctx, &callbacks, &acc);
+ memset(&iocb, 0, sizeof(iocb));
+ iocb.fin = 1;
+ iocb.opcode = WSLAY_TEXT_FRAME;
+ iocb.mask = 0;
+ iocb.payload_length = 0;
+ iocb.data_length = 0;
+ CU_ASSERT(0 == wslay_frame_send(ctx, &iocb));
+ CU_ASSERT_EQUAL(sizeof(msg), acc.length);
+ CU_ASSERT(memcmp(msg, acc.buf, sizeof(msg)) == 0);
+
+ wslay_frame_context_free(ctx);
+}
+
+void test_wslay_frame_send_too_large_payload()
+{
+ wslay_frame_context_ptr ctx;
+ struct wslay_frame_callbacks callbacks;
+ struct wslay_frame_iocb iocb;
+ wslay_frame_context_init(&ctx, &callbacks, NULL);
+ memset(&iocb, 0, sizeof(iocb));
+ iocb.fin = 1;
+ iocb.opcode = WSLAY_TEXT_FRAME;
+ iocb.mask = 0;
+ iocb.payload_length = UINT64_MAX;
+ CU_ASSERT_EQUAL(WSLAY_ERR_INVALID_ARGUMENT,
+ wslay_frame_send(ctx, &iocb));
+
+ wslay_frame_context_free(ctx);
+}
+
+void test_wslay_frame_send_ctrl_frame_too_large_payload()
+{
+ wslay_frame_context_ptr ctx;
+ struct wslay_frame_callbacks callbacks;
+ struct wslay_frame_iocb iocb;
+ wslay_frame_context_init(&ctx, &callbacks, NULL);
+ memset(&iocb, 0, sizeof(iocb));
+ iocb.fin = 1;
+ iocb.opcode = WSLAY_PING;
+ iocb.mask = 0;
+ iocb.payload_length = 1024;
+ CU_ASSERT_EQUAL(WSLAY_ERR_INVALID_ARGUMENT,
+ wslay_frame_send(ctx, &iocb));
+
+ wslay_frame_context_free(ctx);
+}
diff --git a/deps/wslay/tests/wslay_frame_test.h b/deps/wslay/tests/wslay_frame_test.h
new file mode 100644
index 00000000..03693b68
--- /dev/null
+++ b/deps/wslay/tests/wslay_frame_test.h
@@ -0,0 +1,46 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef WSLAY_FRAME_TEST_H
+#define WSLAY_FRAME_TEST_H
+
+void test_wslay_frame_context_init();
+void test_wslay_frame_recv();
+void test_wslay_frame_recv_1byte();
+void test_wslay_frame_recv_fragmented();
+void test_wslay_frame_recv_interleaved_ctrl_frame();
+void test_wslay_frame_recv_zero_payloadlen();
+void test_wslay_frame_recv_too_large_payload();
+void test_wslay_frame_recv_ctrl_frame_too_large_payload();
+void test_wslay_frame_recv_minimum_ext_payload16();
+void test_wslay_frame_recv_minimum_ext_payload64();
+void test_wslay_frame_send();
+void test_wslay_frame_send_fragmented();
+void test_wslay_frame_send_interleaved_ctrl_frame();
+void test_wslay_frame_send_1byte_masked();
+void test_wslay_frame_send_zero_payloadlen();
+void test_wslay_frame_send_too_large_payload();
+void test_wslay_frame_send_ctrl_frame_too_large_payload();
+
+#endif // WSLAY_FRAME_TEST_H
diff --git a/deps/wslay/tests/wslay_queue_test.c b/deps/wslay/tests/wslay_queue_test.c
new file mode 100644
index 00000000..1efdf4a8
--- /dev/null
+++ b/deps/wslay/tests/wslay_queue_test.c
@@ -0,0 +1,59 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "wslay_queue_test.h"
+
+#include <CUnit/CUnit.h>
+
+#include "wslay_queue.h"
+
+void test_wslay_queue()
+{
+ int ints[] = { 1, 2, 3, 4, 5 };
+ int i;
+ struct wslay_queue *queue = wslay_queue_new();
+ CU_ASSERT(wslay_queue_empty(queue));
+ for(i = 0; i < 5; ++i) {
+ wslay_queue_push(queue, &ints[i]);
+ CU_ASSERT_EQUAL(ints[0], *(int*)(wslay_queue_top(queue)));
+ CU_ASSERT(!wslay_queue_empty(queue));
+ }
+ for(i = 0; i < 5; ++i) {
+ CU_ASSERT_EQUAL(ints[i], *(int*)(wslay_queue_top(queue)));
+ wslay_queue_pop(queue);
+ }
+ CU_ASSERT(wslay_queue_empty(queue));
+
+ for(i = 0; i < 5; ++i) {
+ wslay_queue_push_front(queue, &ints[i]);
+ CU_ASSERT_EQUAL(ints[i], *(int*)(wslay_queue_top(queue)));
+ CU_ASSERT(!wslay_queue_empty(queue));
+ }
+ for(i = 4; i >= 0; --i) {
+ CU_ASSERT_EQUAL(ints[i], *(int*)(wslay_queue_top(queue)));
+ wslay_queue_pop(queue);
+ }
+ CU_ASSERT(wslay_queue_empty(queue));
+ wslay_queue_free(queue);
+}
diff --git a/deps/wslay/tests/wslay_queue_test.h b/deps/wslay/tests/wslay_queue_test.h
new file mode 100644
index 00000000..d75b5cf8
--- /dev/null
+++ b/deps/wslay/tests/wslay_queue_test.h
@@ -0,0 +1,30 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef WSLAY_QUEUE_TEST_H
+#define WSLAY_QUEUE_TEST_H
+
+void test_wslay_queue();
+
+#endif // WSLAY_QUEUE_TEST_H
diff --git a/deps/wslay/tests/wslay_session_test.c b/deps/wslay/tests/wslay_session_test.c
new file mode 100644
index 00000000..7f11a81d
--- /dev/null
+++ b/deps/wslay/tests/wslay_session_test.c
@@ -0,0 +1,27 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "wslay_session_test.h"
+
+#include <CUnit/CUnit.h>
diff --git a/deps/wslay/tests/wslay_session_test.h b/deps/wslay/tests/wslay_session_test.h
new file mode 100644
index 00000000..4e043a28
--- /dev/null
+++ b/deps/wslay/tests/wslay_session_test.h
@@ -0,0 +1,28 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef WSLAY_SESSION_TEST_H
+#define WSLAY_SESSION_TEST_H
+
+#endif // WSLAY_SESSION_TEST_H
diff --git a/deps/wslay/tests/wslay_stack_test.c b/deps/wslay/tests/wslay_stack_test.c
new file mode 100644
index 00000000..fd4c3daf
--- /dev/null
+++ b/deps/wslay/tests/wslay_stack_test.c
@@ -0,0 +1,48 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "wslay_stack_test.h"
+
+#include <CUnit/CUnit.h>
+
+#include "wslay_stack.h"
+
+void test_wslay_stack()
+{
+ int ints[] = { 1, 2, 3, 4, 5 };
+ int i;
+ struct wslay_stack *stack = wslay_stack_new();
+ CU_ASSERT(wslay_stack_empty(stack));
+ for(i = 0; i < 5; ++i) {
+ wslay_stack_push(stack, &ints[i]);
+ CU_ASSERT_EQUAL(ints[i], *(int*)(wslay_stack_top(stack)));
+ CU_ASSERT(!wslay_stack_empty(stack));
+ }
+ for(i = 4; i >= 0; --i) {
+ CU_ASSERT_EQUAL(ints[i], *(int*)(wslay_stack_top(stack)));
+ wslay_stack_pop(stack);
+ }
+ CU_ASSERT(wslay_stack_empty(stack));
+ wslay_stack_free(stack);
+}
diff --git a/deps/wslay/tests/wslay_stack_test.h b/deps/wslay/tests/wslay_stack_test.h
new file mode 100644
index 00000000..297f4ca3
--- /dev/null
+++ b/deps/wslay/tests/wslay_stack_test.h
@@ -0,0 +1,30 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef WSLAY_STACK_TEST_H
+#define WSLAY_STACK_TEST_H
+
+void test_wslay_stack();
+
+#endif // WSLAY_STACK_TEST_H