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