소스 검색

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.
Tatsuhiro Tsujikawa 13 년 전
부모
커밋
86ecf36abb
41개의 변경된 파일6186개의 추가작업 그리고 0개의 파일을 삭제
  1. 29 0
      deps/wslay/.gitignore
  2. 1 0
      deps/wslay/AUTHORS
  3. 22 0
      deps/wslay/COPYING
  4. 0 0
      deps/wslay/ChangeLog
  5. 30 0
      deps/wslay/Makefile.am
  6. 41 0
      deps/wslay/NEWS
  7. 1 0
      deps/wslay/README
  8. 32 0
      deps/wslay/README.rst
  9. 104 0
      deps/wslay/configure.ac
  10. 3 0
      deps/wslay/examples/.gitignore
  11. 606 0
      deps/wslay/examples/echoserv.cc
  12. 426 0
      deps/wslay/examples/fork-echoserv.c
  13. 524 0
      deps/wslay/examples/testclient.cc
  14. 42 0
      deps/wslay/lib/Makefile.am
  15. 23 0
      deps/wslay/lib/includes/Makefile.am
  16. 736 0
      deps/wslay/lib/includes/wslay/wslay.h
  17. 31 0
      deps/wslay/lib/includes/wslay/wslayver.h.in
  18. 33 0
      deps/wslay/lib/libwslay.pc.in
  19. 984 0
      deps/wslay/lib/wslay_event.c
  20. 140 0
      deps/wslay/lib/wslay_event.h
  21. 342 0
      deps/wslay/lib/wslay_frame.c
  22. 76 0
      deps/wslay/lib/wslay_frame.h
  23. 62 0
      deps/wslay/lib/wslay_net.c
  24. 63 0
      deps/wslay/lib/wslay_net.h
  25. 116 0
      deps/wslay/lib/wslay_queue.c
  26. 53 0
      deps/wslay/lib/wslay_queue.h
  27. 85 0
      deps/wslay/lib/wslay_stack.c
  28. 50 0
      deps/wslay/lib/wslay_stack.h
  29. 1 0
      deps/wslay/m4/README
  30. 44 0
      deps/wslay/tests/Makefile.am
  31. 126 0
      deps/wslay/tests/main.c
  32. 491 0
      deps/wslay/tests/wslay_event_test.c
  33. 41 0
      deps/wslay/tests/wslay_event_test.h
  34. 560 0
      deps/wslay/tests/wslay_frame_test.c
  35. 46 0
      deps/wslay/tests/wslay_frame_test.h
  36. 59 0
      deps/wslay/tests/wslay_queue_test.c
  37. 30 0
      deps/wslay/tests/wslay_queue_test.h
  38. 27 0
      deps/wslay/tests/wslay_session_test.c
  39. 28 0
      deps/wslay/tests/wslay_session_test.h
  40. 48 0
      deps/wslay/tests/wslay_stack_test.c
  41. 30 0
      deps/wslay/tests/wslay_stack_test.h

+ 29 - 0
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

+ 1 - 0
deps/wslay/AUTHORS

@@ -0,0 +1 @@
+Tatsuhiro Tsujikawa <t-tujikawa at users dot sourceforge dot net>

+ 22 - 0
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.

+ 0 - 0
deps/wslay/ChangeLog


+ 30 - 0
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

+ 41 - 0
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.

+ 1 - 0
deps/wslay/README

@@ -0,0 +1 @@
+See README.rst

+ 32 - 0
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>`_.

+ 104 - 0
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}
+])

+ 3 - 0
deps/wslay/examples/.gitignore

@@ -0,0 +1,3 @@
+fork-echoserv
+echoserv
+testclient

+ 606 - 0
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);
+}

+ 426 - 0
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;
+}

+ 524 - 0
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);
+  }
+}

+ 42 - 0
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)

+ 23 - 0
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

+ 736 - 0
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 */

+ 31 - 0
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 */

+ 33 - 0
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}

+ 984 - 0
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;
+}

+ 140 - 0
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 */

+ 342 - 0
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;
+}

+ 76 - 0
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 */

+ 62 - 0
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 */

+ 63 - 0
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 */

+ 116 - 0
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;
+}

+ 53 - 0
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 */

+ 85 - 0
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;
+}

+ 50 - 0
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 */

+ 1 - 0
deps/wslay/m4/README

@@ -0,0 +1 @@
+Empty m4 directory to make `autoreconf -i` happy.

+ 44 - 0
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

+ 126 - 0
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();
+}

+ 491 - 0
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);
+}

+ 41 - 0
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

+ 560 - 0
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);
+}

+ 46 - 0
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

+ 59 - 0
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);
+}

+ 30 - 0
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

+ 27 - 0
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>

+ 28 - 0
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

+ 48 - 0
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);
+}

+ 30 - 0
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