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