wswrapper: interpose select/pselect. Cleaup.

Interpose on select/pselect so that WebSockets sockets are only
reported as ready if they have enough to actually decode at least
1 byte of real data. This prevents hanging in read/recv after
WebSocket is reported as ready but is not actually ready because empty
frames or less than four base64 bytes have been received.

Split defines and constant defintions into wswrapper.h.

Cleanup debug output and add TRACE for more detailed tracing debug
output.

Major TODO is that select needs to timeout if WebSocket socket keeps
reporting ready but actually isn't ready. That condition will
currently hang forever because the select timeout value is not
adjusted when looping.
This commit is contained in:
Joel Martin 2010-12-27 13:15:59 -07:00
parent fce6ac5cb4
commit 6b900d25d0
3 changed files with 353 additions and 133 deletions

View File

@ -6,6 +6,7 @@ all: $(TARGETS)
wsproxy: wsproxy.o websocket.o md5.o
$(CC) $(LDFLAGS) $^ -lssl -lcrypto -lresolv -o $@
wswrapper.o: wswrapper.h
wswrapper.so: wswrapper.o md5.o
$(CC) $(LDFLAGS) $^ -shared -fPIC -ldl -lresolv -o $@

View File

@ -3,10 +3,15 @@
* Copyright 2010 Joel Martin
* Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
*
* Use wswrap to run a program using the wrapper.
* wswrapper.so is meant to be LD preloaded. Use wswrap to run a program using
* wswrapper.so.
*/
/* WARNING: multi-threaded programs may not work */
/*
* Limitations:
* - multi-threaded programs may not work
* - programs using ppoll or epoll will not work correctly
*/
#include <stdio.h>
#include <stdlib.h>
@ -14,55 +19,13 @@
#define __USE_GNU 1 // Pull in RTLD_NEXT
#include <dlfcn.h>
#include <poll.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <resolv.h> /* base64 encode/decode */
#include "md5.h"
//#define DO_DEBUG 1
#ifdef DO_DEBUG
#define DEBUG(...) \
if (DO_DEBUG) { \
fprintf(stderr, "wswrapper: "); \
fprintf(stderr, __VA_ARGS__); \
}
#else
#define DEBUG(...)
#endif
#define MSG(...) \
fprintf(stderr, "wswrapper: "); \
fprintf(stderr, __VA_ARGS__);
#define RET_ERROR(eno, ...) \
fprintf(stderr, "wswrapper error: "); \
fprintf(stderr, __VA_ARGS__); \
errno = eno; \
return -1;
const char _WS_response[] = "\
HTTP/1.1 101 Web Socket Protocol Handshake\r\n\
Upgrade: WebSocket\r\n\
Connection: Upgrade\r\n\
%sWebSocket-Origin: %s\r\n\
%sWebSocket-Location: %s://%s%s\r\n\
%sWebSocket-Protocol: sample\r\n\
\r\n%s";
#define WS_BUFSIZE 65536
/* Buffers and state for each wrapped WebSocket connection */
typedef struct {
char rbuf[WS_BUFSIZE];
char sbuf[WS_BUFSIZE];
int rcarry_cnt;
char rcarry[3];
int newframe;
} _WS_connection;
#include "wswrapper.h"
/*
* If WSWRAP_PORT environment variable is set then listen to the bind fd that
@ -70,7 +33,9 @@ typedef struct {
* called on.
*/
int _WS_listen_fd = -1;
_WS_connection *_WS_connections[65546];
int _WS_nfds = 0;
int _WS_fds[WS_MAX_FDS];
_WS_connection *_WS_connections[65536];
/*
@ -243,6 +208,71 @@ int _WS_handshake(int sockfd)
return ret;
}
/*
* Check WebSockets socket and return a positive value if there is enough data
* to base64 decode (a 4 byte chunk). If nonblock is not set then it will
* block until there is enough data (or until an error occurs).
*/
ssize_t _WS_ready(int sockfd, int nonblock)
{
_WS_connection *ws = _WS_connections[sockfd];
char buf[6];
int count, len, flags, i;
static void * (*rfunc)();
if (!rfunc) rfunc = (void *(*)()) dlsym(RTLD_NEXT, "recv");
TRACE(">> _WS_ready(%d, %d)\n", sockfd, nonblock);
count = 4 + ws->newframe;
flags = MSG_PEEK;
if (nonblock) {
flags |= MSG_DONTWAIT;
}
while (1) {
len = (int) rfunc(sockfd, buf, count, flags);
if (len < 1) {
TRACE("<< _WS_ready(%d, %d) len < 1, errno: %d\n",
sockfd, nonblock, errno);
return len;
}
if (len >= 2 && buf[0] == '\x00' && buf[1] == '\xff') {
/* Strip emtpy frame */
DEBUG("_WS_ready(%d, %d), strip empty\n", sockfd, nonblock);
len = (int) rfunc(sockfd, buf, 2, 0);
if (len < 2) {
MSG("Failed to strip empty frame headers\n");
TRACE("<< _WS_ready: failed to strip empty frame headers\n");
return len;
} else if (len == 2 && nonblock) {
errno = EAGAIN;
TRACE("<< _WS_ready(%d, %d), len == 2, EAGAIN\n",
sockfd, nonblock);
return -1;
}
continue;
}
if (len < count) {
if (nonblock) {
errno = EAGAIN;
TRACE("<< _WS_ready(%d, %d), len < count, EAGAIN\n",
sockfd, nonblock);
return -1;
} else {
fprintf(stderr, "_WS_ready(%d, %d), loop: len %d, buf:",
sockfd, nonblock, len, (unsigned char) buf[0]);
for (i = 0; i < len; i++) {
fprintf(stderr, "%d", (unsigned char) buf[i]);
}
fprintf(stderr, "\n");
continue;
}
}
TRACE("<< _WS_ready(%d, %d) len: %d\n", sockfd, nonblock, len);
return len;
}
}
/*
* WebSockets recv/read interposer routine
*/
@ -250,7 +280,8 @@ ssize_t _WS_recv(int recvf, int sockfd, const void *buf,
size_t len, int flags)
{
_WS_connection *ws = _WS_connections[sockfd];
int rawcount, deccount, left, rawlen, retlen, decodelen;
int rawcount, deccount, left, striplen, decodelen, ready;
ssize_t retlen, rawlen;
int sockflags;
int i;
char *fstart, *fend, *cstart;
@ -259,10 +290,6 @@ ssize_t _WS_recv(int recvf, int sockfd, const void *buf,
if (!rfunc) rfunc = (void *(*)()) dlsym(RTLD_NEXT, "recv");
if (!rfunc2) rfunc2 = (void *(*)()) dlsym(RTLD_NEXT, "read");
if (len == 0) {
return 0;
}
if (! ws) {
// Not our file descriptor, just pass through
if (recvf) {
@ -271,9 +298,20 @@ ssize_t _WS_recv(int recvf, int sockfd, const void *buf,
return (ssize_t) rfunc2(sockfd, buf, len);
}
}
DEBUG("_WS_recv(%d, _, %d) called\n", sockfd, len);
TRACE(">> _WS_recv(%d)\n", sockfd);
if (len == 0) {
TRACE("<< _WS_recv(%d) len == 0\n", sockfd);
return 0;
}
sockflags = fcntl(sockfd, F_GETFL, 0);
if (sockflags & O_NONBLOCK) {
TRACE("_WS_recv(%d, _, %d) with O_NONBLOCK\n", sockfd, len);
} else {
TRACE("_WS_recv(%d, _, %d) without O_NONBLOCK\n", sockfd, len);
}
left = len;
retlen = 0;
@ -309,54 +347,29 @@ ssize_t _WS_recv(int recvf, int sockfd, const void *buf,
RET_ERROR(ENOMEM, "recv of %d bytes is larger than buffer\n", rawcount);
}
i = 0;
while (1) {
/* Peek at everything available */
rawlen = (int) rfunc(sockfd, ws->rbuf, WS_BUFSIZE-1,
flags | MSG_PEEK);
if (rawlen <= 0) {
DEBUG("_WS_recv: returning because rawlen %d\n", rawlen);
return (ssize_t) rawlen;
}
fstart = ws->rbuf;
/* Strip empty frames */
if (rawlen >= 2 && fstart[0] == '\x00' && fstart[1] == '\xff') {
rawlen = (int) rfunc(sockfd, ws->rbuf, 2, flags);
if (rawlen != 2) {
RET_ERROR(EIO, "Could not strip empty frame headers\n");
}
continue;
}
fstart[rawlen] = '\x00';
if (rawlen - ws->newframe >= 4) {
/* We have enough to base64 decode at least 1 byte */
break;
}
/* Not enough to base64 decode */
if (sockflags & O_NONBLOCK) {
/* Just tell the caller to call again */
DEBUG("_WS_recv: returning because O_NONBLOCK, rawlen %d\n", rawlen);
errno = EAGAIN;
return -1;
}
/* Repeat until at least 1 byte (4 raw bytes) to decode */
i++;
if (i > 1000000) {
MSG("Could not send final part of frame\n");
ready = _WS_ready(sockfd, 0);
if (ready < 1) {
if (retlen) {
/* We had some carry over, don't error until next call */
errno = 0;
} else {
retlen = ready;
}
TRACE("<< _WS_recv(%d, _, %d) retlen %d\n", sockfd, len, retlen);
return retlen;
}
/*
DEBUG("_WS_recv, left: %d, len: %d, rawlen: %d, newframe: %d, raw: ",
left, len, rawlen, _WS_newframe);
for (i = 0; i < rawlen; i++) {
DEBUG("%u,", (unsigned char) ((char *) fstart)[i]);
/* We have enough data to return something */
/* Peek at everything available */
rawlen = (ssize_t) rfunc(sockfd, ws->rbuf, WS_BUFSIZE-1,
flags | MSG_PEEK);
if (rawlen <= 0) {
RET_ERROR(EPROTO, "Socket was ready but then had failure");
}
DEBUG("\n");
*/
fstart = ws->rbuf;
fstart[rawlen] = '\x00';
if (ws->newframe) {
if (fstart[0] != '\x00') {
@ -391,7 +404,7 @@ ssize_t _WS_recv(int recvf, int sockfd, const void *buf,
/* Now consume what was processed */
if (flags & MSG_PEEK) {
MSG("*** Got MSG_PEEK ***\n");
DEBUG("_WS_recv(%d, _, %d) MSG_PEEK, not consuming\n", sockfd, len);
} else {
rfunc(sockfd, ws->rbuf, fstart - ws->rbuf + deccount + ws->newframe, flags);
}
@ -423,19 +436,13 @@ ssize_t _WS_recv(int recvf, int sockfd, const void *buf,
DEBUG("Saving carry bytes: %u,%u\n", ws->rcarry[0],
ws->rcarry[1]);
} else {
MSG("Waah2!\n");
RET_ERRO(EPROTO, "Too many carry bytes!\n");
}
}
}
((char *) buf)[retlen] = '\x00';
/*
DEBUG("*** recv %s as ", fstart);
for (i = 0; i < retlen; i++) {
DEBUG("%u,", (unsigned char) ((char *) buf)[i]);
}
DEBUG(" (%d -> %d): %d\n", deccount, decodelen, retlen);
*/
TRACE("<< _WS_recv(%d) retlen %d\n", sockfd, retlen);
return retlen;
}
@ -451,7 +458,7 @@ ssize_t _WS_send(int sendf, int sockfd, const void *buf,
char * target;
int i;
static void * (*sfunc)(), * (*sfunc2)();
if (!sfunc) sfunc = (void *(*)()) dlsym(RTLD_NEXT, "send");
if (!sfunc) sfunc = (void *(*)()) dlsym(RTLD_NEXT, "send");
if (!sfunc2) sfunc2 = (void *(*)()) dlsym(RTLD_NEXT, "write");
if (! ws) {
@ -462,7 +469,7 @@ ssize_t _WS_send(int sendf, int sockfd, const void *buf,
return (ssize_t) sfunc2(sockfd, buf, len);
}
}
DEBUG("_WS_send(%d, _, %d) called\n", sockfd, len);
TRACE(">> _WS_send(%d, _, %d)\n", sockfd, len);
sockflags = fcntl(sockfd, F_GETFL, 0);
@ -484,7 +491,7 @@ ssize_t _WS_send(int sendf, int sockfd, const void *buf,
rlen = (int) sfunc(sockfd, ws->sbuf, rawlen, flags);
if (rlen <= 0) {
/* Couldn't send, just return */
TRACE("<< _WS_send(%d, _, %d) send failed, returning\n", sockfd, len);
return rlen;
} else if (rlen < rawlen) {
/* Spin until we can send a whole base64 chunck and frame end */
@ -503,13 +510,14 @@ ssize_t _WS_send(int sendf, int sockfd, const void *buf,
} else if (clen == 0) {
MSG("_WS_send: got clen %d\n", clen);
} else if (!(sockflags & O_NONBLOCK)) {
MSG("_WS_send: clen %d\n", clen);
MSG("<< _WS_send: clen %d\n", clen);
return clen;
}
if (i > 1000000) {
MSG("Could not send final part of frame\n");
RET_ERROR(EIO, "Could not send final part of frame\n");
}
} while (left > 0);
//DEBUG("_WS_send: spins until finished %d\n", i);
DEBUG("_WS_send: spins until finished %d\n", i);
}
@ -529,16 +537,114 @@ ssize_t _WS_send(int sendf, int sockfd, const void *buf,
/* Scale return value for base64 encoding size */
retlen = (retlen*3)/4;
/*
DEBUG("*** send ");
for (i = 0; i < retlen; i++) {
DEBUG("%u,", (unsigned char) ((char *)buf)[i]);
}
DEBUG(" as '%s' (%d)\n", ws->sbuf+1, rlen);
*/
TRACE(">> _WS_send(%d, _, %d) retlen %d\n", sockfd, len, retlen);
return (ssize_t) retlen;
}
int _WS_select(int mode, int nfds, fd_set *readfds,
fd_set *writefds, fd_set *exceptfds,
void *timeptr, const sigset_t *sigmask)
{
_WS_connection *ws;
fd_set carryfds, savefds;
struct timeval savetv;
struct timespec savets;
int carrycnt = 0;
int ret, i, ready, fd;
static void * (*func)(), * (*func2)();
if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "select");
if (!func2) func2 = (void *(*)()) dlsym(RTLD_NEXT, "pselect");
if ((_WS_listen_fd == -1) || (_WS_nfds == 0)) {
if (mode == 0) {
ret = (int) func(nfds, readfds, writefds, exceptfds,
(struct timeval *)timeptr);
} else if (mode == 1) {
ret = (int) func2(nfds, readfds, writefds, exceptfds,
(struct timespec *)timeptr, sigmask);
}
return ret;
}
TRACE(">> _WS_select(%d, %d, _, _, _, _) called\n", mode, nfds);
memcpy(&savetv, timeptr, sizeof(savetv));
memcpy(&savets, timeptr, sizeof(savets));
/* If we have carry-over return it right away */
FD_ZERO(&carryfds);
if (readfds) {
memcpy(&savefds, readfds, sizeof(savefds));
for (i = 0; i < _WS_nfds; i++) {
fd = _WS_fds[i];
ws = _WS_connections[fd];
if ((ws->rcarry_cnt) && (FD_ISSET(fd, readfds))) {
FD_SET(fd, &carryfds);
carrycnt++;
}
}
}
if (carrycnt) {
if (writefds) {
FD_ZERO(writefds);
}
if (exceptfds) {
FD_ZERO(writefds);
}
memcpy(readfds, &carryfds, sizeof(carryfds));
TRACE("<< _WS_select(%d, %d, _, _, _, _) carrycnt %d\n",
mode, nfds, carrycnt);
return carrycnt;
}
do {
if (mode == 0) {
ret = (int) func(nfds, readfds, writefds, exceptfds,
(struct timeval *)timeptr);
} else if (mode == 1) {
ret = (int) func2(nfds, readfds, writefds, exceptfds,
(struct timespec *)timeptr, sigmask);
}
if (! readfds) {
break;
}
if (ret <= 0) {
break;
}
for (i = 0; i < _WS_nfds; i++) {
fd = _WS_fds[i];
ws = _WS_connections[fd];
if (FD_ISSET(fd, readfds)) {
ready = _WS_ready(fd, 1);
if (ready == 0) {
/* 0 means EOF which is also a ready condition */
DEBUG("_WS_select: detected %d is closed\n", fd);
} else if (ready < 0) {
DEBUG("_WS_select: FD_CLR(%d,readfds) - not enough to decode\n", fd);
FD_CLR(fd, readfds);
ret--;
}
}
}
errno = 0; /* errno could be set by _WS_ready */
/*
* If all the ready readfds were WebSockets, but none of
* them were really ready (empty frames) then repeat.
*/
if (ret == 0) {
/* Restore original values*/
/* TODO: fix timeptr for repeat */
memcpy(timeptr, &savetv, sizeof(savetv));
memcpy(timeptr, &savets, sizeof(savets));
memcpy(readfds, &savefds, sizeof(savefds));
}
} while (ret == 0);
TRACE("<< _WS_select(%d, %d, _, _, _, _) ret %d, errno %d\n",
mode, nfds, ret, errno);
return ret;
}
/*
* Overload (LD_PRELOAD) standard library network routines
@ -562,37 +668,37 @@ int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
char * WSWRAP_PORT, * end;
int ret, envport, bindport = htons(addr_in->sin_port);
if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "bind");
DEBUG("bind(%d, _, %d) called\n", sockfd, addrlen);
TRACE(">> bind(%d, _, %d) called\n", sockfd, addrlen);
ret = (int) func(sockfd, addr, addrlen);
if (addr_in->sin_family != AF_INET) {
// TODO: handle IPv6
DEBUG("bind, ignoring non-IPv4 socket\n");
TRACE("<< bind, ignoring non-IPv4 socket\n");
return ret;
}
WSWRAP_PORT = getenv("WSWRAP_PORT");
if ((! WSWRAP_PORT) || (*WSWRAP_PORT == '\0')) {
// TODO: interpose on all sockets
DEBUG("bind, not interposing: WSWRAP_PORT is not set\n");
// TODO: interpose on all sockets when WSWRAP_PORT not set
TRACE("<< bind, not interposing: WSWRAP_PORT is not set\n");
return ret;
}
envport = strtol(WSWRAP_PORT, &end, 10);
if ((envport == 0) || (*end != '\0')) {
MSG("bind, not interposing: WSWRAP_PORT is not a number\n");
TRACE("<< bind, not interposing: WSWRAP_PORT is not a number\n");
return ret;
}
if (envport != bindport) {
DEBUG("bind, not interposing on port: %d (fd %d)\n", bindport, sockfd);
TRACE("<< bind, not interposing on port: %d (fd %d)\n", bindport, sockfd);
return ret;
}
MSG("bind, interposing on port: %d (fd %d)\n", envport, sockfd);
_WS_listen_fd = sockfd;
TRACE("<< bind, interposing on port: %d (fd %d)\n", envport, sockfd);
return ret;
}
@ -601,25 +707,28 @@ int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
int fd, ret, envfd;
static void * (*func)();
if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "accept");
DEBUG("accept(%d, _, _) called\n", sockfd);
TRACE("<< accept(%d, _, _) called\n", sockfd);
fd = (int) func(sockfd, addr, addrlen);
if (_WS_listen_fd == -1) {
DEBUG("not interposing\n");
TRACE("<< accept: not interposing\n");
return fd;
}
if (_WS_listen_fd != sockfd) {
DEBUG("not interposing on fd %d\n", sockfd);
TRACE("<< accept: not interposing on fd %d\n", sockfd);
return fd;
}
if (_WS_connections[fd]) {
MSG("error, already interposing on fd %d\n", fd);
RET_ERROR(EINVAL, "already interposing on fd %d\n", fd);
} else {
/* It's a port we're interposing on so allocate memory for it */
if (_WS_nfds >= WS_MAX_FDS) {
RET_ERROR(ENOMEM, "Too many interposer fds\n");
}
if (! (_WS_connections[fd] = malloc(sizeof(_WS_connection)))) {
RET_ERROR(ENOMEM, "Could not allocate interposer memory\n");
}
@ -627,11 +736,16 @@ int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
_WS_connections[fd]->rcarry[0] = '\0';
_WS_connections[fd]->newframe = 1;
/* Add to search list for select/pselect */
_WS_fds[_WS_nfds] = fd;
_WS_nfds++;
ret = _WS_handshake(fd);
if (ret < 0) {
free(_WS_connections[fd]);
_WS_connections[fd] = NULL;
errno = EPROTO;
TRACE("<< accept(%d, _, _): ret %d\n", sockfd, ret);
return ret;
}
MSG("interposing on fd %d (allocated memory)\n", fd);
@ -642,13 +756,28 @@ int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
int close(int fd)
{
int i;
static void * (*func)();
if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "close");
if (_WS_connections[fd]) {
TRACE(">> close(%d)\n", fd);
free(_WS_connections[fd]);
_WS_connections[fd] = NULL;
/* Remove from the search list for select/pselect */
for (i = 0; i < _WS_nfds; i++) {
if (_WS_fds[i] == fd) {
break;
}
}
if (_WS_nfds - i - 1 > 0) {
memmove(_WS_fds + i, _WS_fds + i + 1, _WS_nfds - i - 1);
}
_WS_nfds--;
MSG("finished interposing on fd %d (freed memory)\n", fd);
TRACE("<< close(%d)\n", fd);
}
return (int) func(fd);
}
@ -656,25 +785,50 @@ int close(int fd)
ssize_t read(int fd, void *buf, size_t count)
{
//DEBUG("read(%d, _, %d) called\n", fd, count);
//TRACE("read(%d, _, %d) called\n", fd, count);
return (ssize_t) _WS_recv(0, fd, buf, count, 0);
}
ssize_t write(int fd, const void *buf, size_t count)
{
//DEBUG("write(%d, _, %d) called\n", fd, count);
//TRACE("write(%d, _, %d) called\n", fd, count);
return (ssize_t) _WS_send(0, fd, buf, count, 0);
}
ssize_t recv(int sockfd, void *buf, size_t len, int flags)
{
//DEBUG("recv(%d, _, %d, %d) called\n", sockfd, len, flags);
//TRACE("recv(%d, _, %d, %d) called\n", sockfd, len, flags);
return (ssize_t) _WS_recv(1, sockfd, buf, len, flags);
}
ssize_t send(int sockfd, const void *buf, size_t len, int flags)
{
//DEBUG("send(%d, _, %d, %d) called\n", sockfd, len, flags);
//TRACE("send(%d, _, %d, %d) called\n", sockfd, len, flags);
return (ssize_t) _WS_send(1, sockfd, buf, len, flags);
}
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout)
{
//TRACE("select(%d, _, _, _, _) called\n", nfds);
return _WS_select(0, nfds, readfds, writefds, exceptfds,
(void *) timeout, NULL);
}
int pselect(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, const struct timespec *timeout,
const sigset_t *sigmask)
{
TRACE("pselect(%d, _, _, _, _, _) called\n", nfds);
return _WS_select(1, nfds, readfds, writefds, exceptfds,
(void *) timeout, sigmask);
}
int poll(struct pollfd *fds, nfds_t nfds, int timeout)
{
//TRACE("poll(_, %d, _) called\n", nfds);
static void * (*func)();
if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "poll");
return (int) func(fds, nfds, timeout);
}

65
utils/wswrapper.h Normal file
View File

@ -0,0 +1,65 @@
/*
* wswrap/wswrapper: Add WebSockets support to any service.
* Copyright 2010 Joel Martin
* Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
*
* wswrapper.so is meant to be LD preloaded. Use wswrap to run a program using
* wswrapper.so.
*/
//#define DO_DEBUG 1
//#define DO_TRACE 1
#ifdef DO_DEBUG
#define DEBUG(...) \
if (DO_DEBUG) { \
fprintf(stderr, "wswrapper: "); \
fprintf(stderr, __VA_ARGS__); \
}
#else
#define DEBUG(...)
#endif
#ifdef DO_TRACE
#define TRACE(...) \
if (DO_TRACE) { \
fprintf(stderr, "wswrapper: "); \
fprintf(stderr, __VA_ARGS__); \
}
#else
#define TRACE(...)
#endif
#define MSG(...) \
fprintf(stderr, "wswrapper: "); \
fprintf(stderr, __VA_ARGS__);
#define RET_ERROR(eno, ...) \
fprintf(stderr, "wswrapper error: "); \
fprintf(stderr, __VA_ARGS__); \
errno = eno; \
return -1;
const char _WS_response[] = "\
HTTP/1.1 101 Web Socket Protocol Handshake\r\n\
Upgrade: WebSocket\r\n\
Connection: Upgrade\r\n\
%sWebSocket-Origin: %s\r\n\
%sWebSocket-Location: %s://%s%s\r\n\
%sWebSocket-Protocol: sample\r\n\
\r\n%s";
#define WS_BUFSIZE 65536
#define WS_MAX_FDS 1024
/* Buffers and state for each wrapped WebSocket connection */
typedef struct {
char rbuf[WS_BUFSIZE];
char sbuf[WS_BUFSIZE];
int rcarry_cnt;
char rcarry[3];
int newframe;
} _WS_connection;