It needs some unixy environment that
may choke in other build contexts, this
lets you cleanly stop it being built
+
+--enable-x-google-mux Enable experimental x-google-mux support
+ in the build (see notes later in document)
Testing server with a browser
-----------------------------
appear in the callback for protocol 0 and allow interface code to
manage socket descriptors in other poll loops.
-2011-02-12 Andy Green <andy@warmcat.com>
+
+x-google-mux support
+--------------------
+
+Experimental and super-preliminary x-google-mux support is available if
+enabled in ./configure with --enable-x-google-mux. Note that when changing
+configurations, you will need to do a make distclean before, then the new
+configure and then make ; make install. Don't forget the necessary other
+flags for your platform as described at the top of the readme.
+
+It has the following notes:
+
+ 1) To enable it, reconfigure with --enable-x-google-mux
+
+ 2) It conflicts with deflate-stream, use the -u switch on
+ the test client to disable deflate-stream
+
+ 3) It deviates from the google standard by sending full
+ headers in the addchannel subcommand rather than just
+ changed ones from original connect
+
+ 4) Quota is not implemented yet
+
+ 5) Close of subchannel is not really implemented yet
+
+ 6) Google opcode 0xf is changed to 0x7 to account for
+ v7 protocol changes to opcode layout
+
+ However despite those caveats, in fact it can run the
+ test client reliably over one socket (both dumb-increment
+ and lws-mirror-protocol), you can open a browser on the
+ same test server too and see the circles, etc.
+
+
+2011-05-23 Andy Green <andy@warmcat.com>
NOPING_FALSE
NOPING_TRUE
clientcertdir
+EXT_GOOGLE_MUX_FALSE
+EXT_GOOGLE_MUX_TRUE
LIBCRYPTO_FALSE
LIBCRYPTO_TRUE
CPP
enable_openssl
enable_nofork
enable_libcrypto
+enable_x_google_mux
with_client_cert_dir
enable_noping
'
LDFLAGS
LIBS
CPPFLAGS
-CPP
-CPPFLAGS
-CC
-LDFLAGS
-LIBS
-CPPFLAGS'
+CPP'
# Initialize some variables set by options.
--enable-openssl Enables https support and needs openssl libs
--enable-nofork Disables fork-related options
--enable-libcrypto Use libcrypto MD5 and SHA1 implementations
+ --enable-x-google-mux Build experimental x-google-mux
--enable-noping Do not build ping test app, which has some unixy stuff in sources
Optional Packages:
#
#
#
+# Check whether --enable-x-google-mux was given.
+if test "${enable_x_google_mux+set}" = set; then :
+ enableval=$enable_x_google_mux; x_google_mux=yes
+
+fi
+
+if test "x$x_google_mux" = "xyes" ; then
+CFLAGS="$CFLAGS -DLWS_EXT_GOOGLE_MUX"
+fi
+ if test x$x_google_mux = xyes; then
+ EXT_GOOGLE_MUX_TRUE=
+ EXT_GOOGLE_MUX_FALSE='#'
+else
+ EXT_GOOGLE_MUX_TRUE='#'
+ EXT_GOOGLE_MUX_FALSE=
+fi
+
+
+
+#
+#
+#
# Check whether --with-client-cert-dir was given.
if test "${with_client_cert_dir+set}" = set; then :
as_fn_error $? "conditional \"LIBCRYPTO\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
+if test -z "${EXT_GOOGLE_MUX_TRUE}" && test -z "${EXT_GOOGLE_MUX_FALSE}"; then
+ as_fn_error $? "conditional \"EXT_GOOGLE_MUX\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
if test -z "${NOPING_TRUE}" && test -z "${NOPING_FALSE}"; then
as_fn_error $? "conditional \"NOPING\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
#
#
#
+AC_ARG_ENABLE(x-google-mux,
+ [ --enable-x-google-mux Build experimental x-google-mux],
+ [ x_google_mux=yes
+ ])
+if test "x$x_google_mux" = "xyes" ; then
+CFLAGS="$CFLAGS -DLWS_EXT_GOOGLE_MUX"
+fi
+AM_CONDITIONAL(EXT_GOOGLE_MUX, test x$x_google_mux = xyes)
+
+
+#
+#
+#
AC_ARG_WITH([client-cert-dir],
[AS_HELP_STRING([--with-client-cert-dir],[directory containing client certs, defaults to /etc/pki/tls/certs/])],
[clientcertdir=$withval],
extension.c \
extension-deflate-stream.c \
private-libwebsockets.h
+
+if EXT_GOOGLE_MUX
+dist_libwebsockets_la_SOURCES += extension-x-google-mux.c
+endif
+
if LIBCRYPTO
else
dist_libwebsockets_la_SOURCES += md5.c sha-1.c
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-@LIBCRYPTO_FALSE@am__append_1 = md5.c sha-1.c
+@EXT_GOOGLE_MUX_TRUE@am__append_1 = extension-x-google-mux.c
+@LIBCRYPTO_FALSE@am__append_2 = md5.c sha-1.c
subdir = lib
DIST_COMMON = $(include_HEADERS) $(srcdir)/Makefile.am \
$(srcdir)/Makefile.in
am__dist_libwebsockets_la_SOURCES_DIST = libwebsockets.c handshake.c \
parsers.c libwebsockets.h base64-decode.c client-handshake.c \
extension.c extension-deflate-stream.c private-libwebsockets.h \
- md5.c sha-1.c
-@LIBCRYPTO_FALSE@am__objects_1 = libwebsockets_la-md5.lo \
+ extension-x-google-mux.c md5.c sha-1.c
+@EXT_GOOGLE_MUX_TRUE@am__objects_1 = libwebsockets_la-extension-x-google-mux.lo
+@LIBCRYPTO_FALSE@am__objects_2 = libwebsockets_la-md5.lo \
@LIBCRYPTO_FALSE@ libwebsockets_la-sha-1.lo
dist_libwebsockets_la_OBJECTS = libwebsockets_la-libwebsockets.lo \
libwebsockets_la-handshake.lo libwebsockets_la-parsers.lo \
libwebsockets_la-base64-decode.lo \
libwebsockets_la-client-handshake.lo \
libwebsockets_la-extension.lo \
- libwebsockets_la-extension-deflate-stream.lo $(am__objects_1)
+ libwebsockets_la-extension-deflate-stream.lo $(am__objects_1) \
+ $(am__objects_2)
libwebsockets_la_OBJECTS = $(dist_libwebsockets_la_OBJECTS)
libwebsockets_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(libwebsockets_la_CFLAGS) \
dist_libwebsockets_la_SOURCES = libwebsockets.c handshake.c parsers.c \
libwebsockets.h base64-decode.c client-handshake.c extension.c \
extension-deflate-stream.c private-libwebsockets.h \
- $(am__append_1)
+ $(am__append_1) $(am__append_2)
libwebsockets_la_CFLAGS := -rdynamic -fPIC -Wall -Werror -std=gnu99 -pedantic -c \
-DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\"
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_la-base64-decode.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_la-client-handshake.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_la-extension-deflate-stream.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_la-extension-x-google-mux.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_la-extension.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_la-handshake.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_la-libwebsockets.Plo@am__quote@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_la_CFLAGS) $(CFLAGS) -c -o libwebsockets_la-extension-deflate-stream.lo `test -f 'extension-deflate-stream.c' || echo '$(srcdir)/'`extension-deflate-stream.c
+libwebsockets_la-extension-x-google-mux.lo: extension-x-google-mux.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_la_CFLAGS) $(CFLAGS) -MT libwebsockets_la-extension-x-google-mux.lo -MD -MP -MF $(DEPDIR)/libwebsockets_la-extension-x-google-mux.Tpo -c -o libwebsockets_la-extension-x-google-mux.lo `test -f 'extension-x-google-mux.c' || echo '$(srcdir)/'`extension-x-google-mux.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libwebsockets_la-extension-x-google-mux.Tpo $(DEPDIR)/libwebsockets_la-extension-x-google-mux.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='extension-x-google-mux.c' object='libwebsockets_la-extension-x-google-mux.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_la_CFLAGS) $(CFLAGS) -c -o libwebsockets_la-extension-x-google-mux.lo `test -f 'extension-x-google-mux.c' || echo '$(srcdir)/'`extension-x-google-mux.c
+
libwebsockets_la-md5.lo: md5.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_la_CFLAGS) $(CFLAGS) -MT libwebsockets_la-md5.lo -MD -MP -MF $(DEPDIR)/libwebsockets_la-md5.Tpo -c -o libwebsockets_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libwebsockets_la-md5.Tpo $(DEPDIR)/libwebsockets_la-md5.Plo
#include <netdb.h>
+struct libwebsocket * __libwebsocket_client_connect_2(
+ struct libwebsocket_context *context,
+ struct libwebsocket *wsi
+) {
+ struct pollfd pfd;
+ struct timeval tv;
+ struct hostent *server_hostent;
+ struct sockaddr_in server_addr;
+ int n;
+ int plen = 0;
+ char pkt[512];
+ int opt = 1;
+
+ fprintf(stderr, "__libwebsocket_client_connect_2\n");
+
+ wsi->candidate_children_list = NULL;
+
+ /*
+ * proxy?
+ */
+
+ if (context->http_proxy_port) {
+ plen = sprintf(pkt, "CONNECT %s:%u HTTP/1.0\x0d\x0a"
+ "User-agent: libwebsockets\x0d\x0a"
+/*Proxy-authorization: basic aGVsbG86d29ybGQ= */
+ "\x0d\x0a", wsi->c_address, wsi->c_port);
+
+ /* OK from now on we talk via the proxy */
+
+ free(wsi->c_address);
+ wsi->c_address = strdup(context->http_proxy_address);
+ wsi->c_port = context->http_proxy_port;
+ }
+
+ /*
+ * prepare the actual connection (to the proxy, if any)
+ */
+
+ server_hostent = gethostbyname(wsi->c_address);
+ if (server_hostent == NULL) {
+ fprintf(stderr, "Unable to get host name from %s\n",
+ wsi->c_address);
+ goto oom4;
+ }
+
+ wsi->sock = socket(AF_INET, SOCK_STREAM, 0);
+
+ if (wsi->sock < 0) {
+ fprintf(stderr, "Unable to open socket\n");
+ goto oom4;
+ }
+
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_port = htons(wsi->c_port);
+ server_addr.sin_addr = *((struct in_addr *)server_hostent->h_addr);
+ bzero(&server_addr.sin_zero, 8);
+
+ /* Disable Nagle */
+ setsockopt(wsi->sock, SOL_TCP, TCP_NODELAY, &opt, sizeof(opt));
+
+ /* Set receiving timeout */
+ tv.tv_sec = 0;
+ tv.tv_usec = 100 * 1000;
+ setsockopt(wsi->sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof tv);
+
+ if (connect(wsi->sock, (struct sockaddr *)&server_addr,
+ sizeof(struct sockaddr)) == -1) {
+ fprintf(stderr, "Connect failed\n");
+ goto oom4;
+ }
+
+ fprintf(stderr, "connected\n");
+
+ /* into fd -> wsi hashtable */
+
+ insert_wsi(context, wsi);
+
+ /* into internal poll list */
+
+ context->fds[context->fds_count].fd = wsi->sock;
+ context->fds[context->fds_count].revents = 0;
+ context->fds[context->fds_count++].events = POLLIN;
+
+ /* external POLL support via protocol 0 */
+ context->protocols[0].callback(context, wsi,
+ LWS_CALLBACK_ADD_POLL_FD,
+ (void *)(long)wsi->sock, NULL, POLLIN);
+
+ /* we are connected to server, or proxy */
+
+ if (context->http_proxy_port) {
+
+ n = send(wsi->sock, pkt, plen, 0);
+ if (n < 0) {
+#ifdef WIN32
+ closesocket(wsi->sock);
+#else
+ close(wsi->sock);
+#endif
+ fprintf(stderr, "ERROR writing to proxy socket\n");
+ goto bail1;
+ }
+
+ libwebsocket_set_timeout(wsi,
+ PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE, 5);
+
+ wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY;
+
+ return wsi;
+ }
+
+ /*
+ * provoke service to issue the handshake directly
+ * we need to do it this way because in the proxy case, this is the
+ * next state and executed only if and when we get a good proxy
+ * response inside the state machine
+ */
+
+ wsi->mode = LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE;
+ pfd.fd = wsi->sock;
+ pfd.revents = POLLIN;
+ libwebsocket_service_fd(context, &pfd);
+
+ return wsi;
+
+oom4:
+ if (wsi->c_protocol)
+ free(wsi->c_protocol);
+
+ if (wsi->c_origin)
+ free(wsi->c_origin);
+
+ free(wsi->c_host);
+ free(wsi->c_path);
+
+bail1:
+ free(wsi);
+
+ return NULL;
+}
+
/**
* libwebsocket_client_connect() - Connect to another websocket server
* @context: Websocket context
const char *protocol,
int ietf_version_or_minus_one)
{
- struct hostent *server_hostent;
- struct sockaddr_in server_addr;
- char pkt[512];
- struct pollfd pfd;
struct libwebsocket *wsi;
int n;
- int opt = 1;
- int plen = 0;
+ int m;
+ struct libwebsocket_extension *ext;
+ int handled;
#ifndef LWS_OPENSSL_SUPPORT
if (ssl_connection) {
fprintf(stderr, "libwebsockets not configured for ssl\n");
wsi->use_ssl = ssl_connection;
#endif
+ wsi->c_port = port;
+ wsi->c_address = strdup(address);
+
/* copy parameters over so state machine has access */
wsi->c_path = malloc(strlen(path) + 1);
}
/*
- * proxy?
+ * Check with each extension if it is able to route and proxy this
+ * connection for us. For example, an extension like x-google-mux
+ * can handle this and then we don't need an actual socket for this
+ * connection.
*/
- if (context->http_proxy_port) {
- plen = sprintf(pkt, "CONNECT %s:%u HTTP/1.0\x0d\x0a"
- "User-agent: libwebsockets\x0d\x0a"
-/*Proxy-authorization: basic aGVsbG86d29ybGQ= */
- "\x0d\x0a", address, port);
+ handled = 0;
+ ext = context->extensions;
+ n = 0;
- /* OK from now on we talk via the proxy */
-
- address = context->http_proxy_address;
- port = context->http_proxy_port;
- }
+ while (ext && ext->callback && !handled) {
+ m = ext->callback(context, ext, wsi,
+ LWS_EXT_CALLBACK_CAN_PROXY_CLIENT_CONNECTION,
+ (void *)(long)n, (void *)address, port);
+ if (m)
+ handled = 1;
- /*
- * prepare the actual connection (to the proxy, if any)
- */
-
- server_hostent = gethostbyname(address);
- if (server_hostent == NULL) {
- fprintf(stderr, "Unable to get host name from %s\n", address);
- goto oom4;
+ ext++;
+ n++;
}
- wsi->sock = socket(AF_INET, SOCK_STREAM, 0);
-
- if (wsi->sock < 0) {
- fprintf(stderr, "Unable to open socket\n");
- goto oom4;
- }
-
- server_addr.sin_family = AF_INET;
- server_addr.sin_port = htons(port);
- server_addr.sin_addr = *((struct in_addr *)server_hostent->h_addr);
- bzero(&server_addr.sin_zero, 8);
-
- /* Disable Nagle */
- setsockopt(wsi->sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
-
- if (connect(wsi->sock, (struct sockaddr *)&server_addr,
- sizeof(struct sockaddr)) == -1) {
- fprintf(stderr, "Connect failed\n");
- goto oom4;
- }
-
- /* into fd -> wsi hashtable */
-
- insert_wsi(context, wsi);
-
- /* into internal poll list */
-
- context->fds[context->fds_count].fd = wsi->sock;
- context->fds[context->fds_count].revents = 0;
- context->fds[context->fds_count++].events = POLLIN;
-
- /* external POLL support via protocol 0 */
- context->protocols[0].callback(context, wsi,
- LWS_CALLBACK_ADD_POLL_FD,
- (void *)(long)wsi->sock, NULL, POLLIN);
-
- /* we are connected to server, or proxy */
-
- if (context->http_proxy_port) {
-
- n = send(wsi->sock, pkt, plen, 0);
- if (n < 0) {
-#ifdef WIN32
- closesocket(wsi->sock);
-#else
- close(wsi->sock);
-#endif
- fprintf(stderr, "ERROR writing to proxy socket\n");
- goto bail1;
- }
+ if (handled) {
+ fprintf(stderr, "libwebsocket_client_connect: "
+ "ext handling conn\n");
libwebsocket_set_timeout(wsi,
- PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE, 5);
-
- wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY;
+ PENDING_TIMEOUT_AWAITING_EXTENSION_CONNECT_RESPONSE, 5);
+ wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT;
return wsi;
}
- /*
- * provoke service to issue the handshake directly
- * we need to do it this way because in the proxy case, this is the
- * next state and executed only if and when we get a good proxy
- * response inside the state machine
- */
+ fprintf(stderr, "libwebsocket_client_connect: direct conn\n");
- wsi->mode = LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE;
- pfd.fd = wsi->sock;
- pfd.revents = POLLIN;
- libwebsocket_service_fd(context, &pfd);
+ return __libwebsocket_client_connect_2(context, wsi);
- return wsi;
oom4:
if (wsi->c_protocol)
/* we don't need calling again until new input data comes */
- return 0;
+ return 0;
+
+ default:
+ break;
}
return 0;
--- /dev/null
+#include "private-libwebsockets.h"
+#include "extension-x-google-mux.h"
+
+static int ongoing_subchannel;
+static struct libwebsocket * tag_with_parent = NULL;
+
+static int lws_addheader_mux_opcode(unsigned char *pb, int len)
+{
+ unsigned char *start = pb;
+
+ *pb++ = LWS_WS_OPCODE_07__NOSPEC__MUX | 0x80;
+ if (len < 126)
+ *pb++ = len;
+ else {
+ if (len > 65535) {
+ *pb++ = 127;
+ *pb++ = 0;
+ *pb++ = 0;
+ *pb++ = 0;
+ *pb++ = 0;
+ *pb++ = (len ) >> 24;
+ *pb++ = (len) >> 16;
+ *pb++ = (len) >> 8;
+ *pb++ = (len) >> 0;
+ } else {
+ *pb++ = 126;
+ *pb++ = (len) >> 8;
+ *pb++ = (len) >> 0;
+ }
+ }
+
+ return pb - start;
+}
+
+static int lws_mux_subcommand_header(int cmd, int channel, unsigned char *pb, int len)
+{
+ unsigned char *start = pb;
+
+ *pb++ = ((channel >> 8) << 3) | cmd;
+ *pb++ = channel;
+
+ if (len <= 253)
+ *pb++ = len;
+ else {
+ *pb++ = 254;
+ *pb++ = len >> 8;
+ *pb++ = len;
+ }
+
+ return pb - start;
+}
+
+static int lws_ext_x_google_mux__send_addchannel(
+ struct libwebsocket_context *context,
+ struct libwebsocket *wsi,
+ struct libwebsocket *wsi_child,
+ int channel,
+ const char *url
+) {
+
+ unsigned char send_buf[LWS_SEND_BUFFER_PRE_PADDING + 2048 +
+ LWS_SEND_BUFFER_POST_PADDING];
+ unsigned char *pb = &send_buf[LWS_SEND_BUFFER_PRE_PADDING];
+ char *p;
+ char delta_headers[1536];
+ int delta_headers_len;
+ int subcommand_length;
+
+ wsi_child->ietf_spec_revision = wsi->ietf_spec_revision;
+
+ p = libwebsockets_generate_client_handshake(context, wsi_child, delta_headers);
+ delta_headers_len = p - delta_headers;
+
+ subcommand_length = lws_mux_subcommand_header(LWS_EXT_XGM_OPC__ADDCHANNEL, channel, pb, delta_headers_len);
+
+ pb += lws_addheader_mux_opcode(pb, subcommand_length + delta_headers_len);
+ pb += lws_mux_subcommand_header(LWS_EXT_XGM_OPC__ADDCHANNEL, channel, pb, delta_headers_len);
+
+// n = sprintf((char *)pb, "%s\x0d\x0a", url);
+// pb += n;
+
+ if (delta_headers_len)
+ memcpy(pb, delta_headers, delta_headers_len);
+
+ pb += delta_headers_len;
+
+ muxdebug("add channel sends %ld\n",
+ pb - &send_buf[LWS_SEND_BUFFER_PRE_PADDING]);
+
+ /* send the request to the server */
+
+ return lws_issue_raw(wsi, &send_buf[LWS_SEND_BUFFER_PRE_PADDING],
+ pb - &send_buf[LWS_SEND_BUFFER_PRE_PADDING]);
+}
+
+/**
+ * lws_extension_x_google_mux_parser(): Parse mux buffer headers coming in
+ * from a muxed connection into subchannel
+ * specific actions
+ * @wsi: muxed websocket instance
+ * @conn: x-google-mux private data bound to that @wsi
+ * @c: next character in muxed stream
+ */
+
+static int
+lws_extension_x_google_mux_parser(struct libwebsocket_context *context,
+ struct libwebsocket *wsi,
+ struct libwebsocket_extension *this_ext,
+ struct lws_ext_x_google_mux_conn *conn, unsigned char c)
+{
+ struct libwebsocket *wsi_child = NULL;
+ struct libwebsocket_extension *ext;
+ struct lws_ext_x_google_mux_conn *child_conn = NULL;
+ int n;
+ void *v;
+
+// fprintf(stderr, "XRX: %02X %d %d\n", c, conn->state, conn->length);
+
+ /*
+ * [ <Channel ID b12.. b8> <Mux Opcode b2..b0> ]
+ * [ <Channel ID b7.. b0> ]
+ */
+
+ switch (conn->state) {
+
+ case LWS_EXT_XGM_STATE__MUX_BLOCK_1:
+ muxdebug("LWS_EXT_XGM_STATE__MUX_BLOCK_1: opc=%d\n", c & 7);
+ conn->block_subopcode = c & 7;
+ conn->block_subchannel = (c << 5) & ~0xff;
+ conn->state = LWS_EXT_XGM_STATE__MUX_BLOCK_2;
+ break;
+
+ case LWS_EXT_XGM_STATE__MUX_BLOCK_2:
+ conn->block_subchannel |= c;
+ muxdebug("LWS_EXT_XGM_STATE__MUX_BLOCK_2: subchannel=%d\n", conn->block_subchannel);
+
+ ongoing_subchannel = ongoing_subchannel;
+
+ /*
+ * convert the subchannel index to a child wsi
+ */
+
+ /* act on the muxing opcode */
+
+ switch (conn->block_subopcode) {
+ case LWS_EXT_XGM_OPC__DATA:
+ conn->state = LWS_EXT_XGM_STATE__DATA;
+ break;
+ case LWS_EXT_XGM_OPC__ADDCHANNEL:
+ conn->state = LWS_EXT_XGM_STATE__ADDCHANNEL_LEN;
+ switch (wsi->mode) {
+
+ /* client: parse accepted headers returned by server */
+
+ case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY:
+ case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE:
+ case LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY:
+ case LWS_CONNMODE_WS_CLIENT:
+ wsi_child = conn->wsi_children[conn->block_subchannel];
+ wsi_child->state = WSI_STATE_HTTP_HEADERS;
+ wsi_child->parser_state = WSI_TOKEN_NAME_PART;
+ break;
+ default:
+ wsi_child = libwebsocket_create_new_server_wsi(context);
+ conn->wsi_children[conn->block_subchannel] = wsi_child;
+ wsi_child->state = WSI_STATE_HTTP_HEADERS;
+ wsi_child->parser_state = WSI_TOKEN_NAME_PART;
+ wsi_child->extension_handles = wsi;
+ muxdebug("MUX LWS_EXT_XGM_OPC__ADDCHANNEL... "
+ "created child subchannel %d\n", conn->block_subchannel);
+ break;
+ }
+ break;
+ case LWS_EXT_XGM_OPC__DROPCHANNEL:
+ conn->state = LWS_EXT_XGM_STATE__MUX_BLOCK_1;
+ break;
+ case LWS_EXT_XGM_OPC__FLOWCONTROL:
+ conn->state = LWS_EXT_XGM_STATE__FLOWCONTROL_1;
+ break;
+ default:
+ fprintf(stderr, "xgm: unknown subopcode\n");
+ return -1;
+ }
+ break;
+
+ case LWS_EXT_XGM_STATE__ADDCHANNEL_LEN:
+ switch (c) {
+ case 254:
+ conn->state = LWS_EXT_XGM_STATE__ADDCHANNEL_LEN16_1;
+ break;
+ case 255:
+ conn->state = LWS_EXT_XGM_STATE__ADDCHANNEL_LEN32_1;
+ break;
+ default:
+ conn->length = c;
+ conn->state = LWS_EXT_XGM_STATE__ADDCHANNEL_HEADERS;
+ break;
+ }
+ break;
+
+ case LWS_EXT_XGM_STATE__ADDCHANNEL_LEN16_1:
+ conn->length = c << 8;
+ conn->state = LWS_EXT_XGM_STATE__ADDCHANNEL_LEN16_2;
+ break;
+
+ case LWS_EXT_XGM_STATE__ADDCHANNEL_LEN16_2:
+ conn->length |= c;
+ conn->state = LWS_EXT_XGM_STATE__ADDCHANNEL_HEADERS;
+ muxdebug("conn->length in mux block is %d\n", conn->length);
+ break;
+
+ case LWS_EXT_XGM_STATE__ADDCHANNEL_LEN32_1:
+ conn->length = c << 24;
+ conn->state = LWS_EXT_XGM_STATE__ADDCHANNEL_LEN32_2;
+ break;
+
+ case LWS_EXT_XGM_STATE__ADDCHANNEL_LEN32_2:
+ conn->length |= c << 16;
+ conn->state = LWS_EXT_XGM_STATE__ADDCHANNEL_LEN32_3;
+ break;
+
+ case LWS_EXT_XGM_STATE__ADDCHANNEL_LEN32_3:
+ conn->length |= c << 8;
+ conn->state = LWS_EXT_XGM_STATE__ADDCHANNEL_LEN32_4;
+ break;
+
+ case LWS_EXT_XGM_STATE__ADDCHANNEL_LEN32_4:
+ conn->length |= c;
+ conn->state = LWS_EXT_XGM_STATE__ADDCHANNEL_HEADERS;
+ break;
+
+ case LWS_EXT_XGM_STATE__ADDCHANNEL_HEADERS:
+
+ switch (wsi->mode) {
+
+ /* client: parse accepted headers returned by server */
+
+ case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY:
+ case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE:
+ case LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY:
+ case LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT:
+ case LWS_CONNMODE_WS_CLIENT:
+
+ muxdebug("Client LWS_EXT_XGM_STATE__ADDCHANNEL_HEADERS in %c\n", c);
+ wsi_child = conn->wsi_children[conn->block_subchannel];
+
+ libwebsocket_parse(wsi_child, c);
+
+ if (--conn->length)
+ return 0;
+
+ /* it's here we create the actual ext conn via callback */
+ tag_with_parent = wsi;
+ lws_client_interpret_server_handshake(context, wsi_child);
+ tag_with_parent = NULL;
+
+ // if (wsi->parser_state != WSI_PARSING_COMPLETE)
+// break;
+
+ /* client: we received all server's ADD ack */
+
+ child_conn = lws_get_extension_user_matching_ext(wsi_child, this_ext);
+ muxdebug("Received server's ADD Channel ACK for subchannel %d child_conn=%p!\n", conn->block_subchannel, (void *)child_conn);
+
+ wsi_child->xor_mask = xor_no_mask;
+ wsi_child->ietf_spec_revision = wsi->ietf_spec_revision;
+
+ wsi_child->mode = LWS_CONNMODE_WS_CLIENT;
+ wsi_child->state = WSI_STATE_ESTABLISHED;
+
+ conn->state = LWS_EXT_XGM_STATE__MUX_BLOCK_1;
+ child_conn->state = LWS_EXT_XGM_STATE__MUX_BLOCK_1;
+
+ /* allocate the per-connection user memory (if any) */
+
+ if (wsi_child->protocol->per_session_data_size) {
+ wsi_child->user_space = malloc(
+ wsi_child->protocol->per_session_data_size);
+ if (wsi_child->user_space == NULL) {
+ fprintf(stderr, "Out of memory for "
+ "conn user space\n");
+ goto bail2;
+ }
+ } else
+ wsi_child->user_space = NULL;
+
+ /* clear his proxy connection timeout */
+
+ libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+
+ /* mark him as being alive */
+
+ wsi_child->state = WSI_STATE_ESTABLISHED;
+ wsi_child->mode = LWS_CONNMODE_WS_CLIENT;
+
+ if (wsi_child->protocol)
+ fprintf(stderr, "mux handshake OK for protocol %s\n",
+ wsi_child->protocol->name);
+ else
+ fprintf(stderr, "mux child handshake ends up with no protocol!\n");
+
+ /*
+ * inform all extensions, not just active ones since they
+ * already know
+ */
+
+ ext = context->extensions;
+
+ while (ext && ext->callback) {
+ v = NULL;
+ for (n = 0; n < wsi_child->count_active_extensions; n++)
+ if (wsi_child->active_extensions[n] == ext) {
+ v = wsi_child->active_extensions_user[n];
+ }
+
+ ext->callback(context, ext, wsi_child,
+ LWS_EXT_CALLBACK_ANY_WSI_ESTABLISHED, v, NULL, 0);
+ ext++;
+ }
+
+ /* call him back to inform him he is up */
+
+ wsi->protocol->callback(context, wsi_child,
+ LWS_CALLBACK_CLIENT_ESTABLISHED,
+ wsi_child->user_space,
+ NULL, 0);
+
+ return 0;
+
+bail2:
+ exit(1);
+
+ /* server: parse proposed changed headers from client */
+
+ default:
+ break;
+ }
+
+ /*
+ * SERVER
+ */
+
+ wsi_child = conn->wsi_children[conn->block_subchannel];
+
+ muxdebug("Server LWS_EXT_XGM_STATE__ADDCHANNEL_HEADERS in\n");
+
+ libwebsocket_read(context, wsi_child, &c, 1);
+
+ if (--conn->length >= 0)
+ break;
+
+ muxdebug("Server LWS_EXT_XGM_STATE__ADDCHANNEL_HEADERS done\n");
+
+ /*
+ * server: header diffs are all seen, we must process
+ * the add action
+ */
+
+ /* reply with ADDCHANNEL to ack it */
+
+ wsi->xor_mask = xor_no_mask;
+
+
+// lws_ext_x_google_mux__send_addchannel(context, wsi, wsi_child,
+// conn->block_subchannel, "url-parsing-not-done-yet");
+
+ wsi_child->mode = LWS_CONNMODE_WS_SERVING;
+ wsi_child->state = WSI_STATE_ESTABLISHED;
+ wsi_child->lws_rx_parse_state = LWS_RXPS_NEW;
+ wsi_child->rx_packet_length = 0;
+
+ /* allocate the per-connection user memory (if any) */
+
+ if (wsi_child->protocol->per_session_data_size) {
+ wsi_child->user_space = malloc(
+ wsi_child->protocol->per_session_data_size);
+ if (wsi_child->user_space == NULL) {
+ fprintf(stderr, "Out of memory for "
+ "conn user space\n");
+ break;
+ }
+ } else
+ wsi_child->user_space = NULL;
+
+
+ conn->wsi_children[conn->block_subchannel] = wsi_child;
+ if (conn->count_children <= conn->block_subchannel)
+ conn->count_children = conn->block_subchannel + 1;
+
+
+ /* notify user code that we're ready to roll */
+
+ if (wsi_child->protocol->callback)
+ wsi_child->protocol->callback(wsi_child->protocol->owning_server,
+ wsi_child, LWS_CALLBACK_ESTABLISHED, wsi_child->user_space,
+ NULL, 0);
+
+ muxdebug("setting conn state to LWS_EXT_XGM_STATE__MUX_BLOCK_1\n");
+ conn->state = LWS_EXT_XGM_STATE__MUX_BLOCK_1;
+ break;
+
+ case LWS_EXT_XGM_STATE__FLOWCONTROL_1:
+ conn->length = c << 24;
+ conn->state = LWS_EXT_XGM_STATE__FLOWCONTROL_2;
+ break;
+
+ case LWS_EXT_XGM_STATE__FLOWCONTROL_2:
+ conn->length |= c << 16;
+ conn->state = LWS_EXT_XGM_STATE__FLOWCONTROL_3;
+ break;
+
+ case LWS_EXT_XGM_STATE__FLOWCONTROL_3:
+ conn->length |= c << 8;
+ conn->state = LWS_EXT_XGM_STATE__FLOWCONTROL_4;
+ break;
+
+ case LWS_EXT_XGM_STATE__FLOWCONTROL_4:
+ conn->length |= c;
+ conn->state = LWS_EXT_XGM_STATE__MUX_BLOCK_1;
+ break;
+
+ case LWS_EXT_XGM_STATE__DATA:
+
+// fprintf(stderr, "LWS_EXT_XGM_STATE__DATA in\n");
+
+ /*
+ * we have cooked websocket frame content following just like
+ * it went on the wire without mux, including masking and any
+ * other extensions (including this guy can himself be another
+ * level of channel mux, there's no restriction).
+ *
+ * We deal with it by just feeding it to the child wsi's rx
+ * state machine. The only issue is, we need that state machine
+ * to tell us when it ate a full frame, so we watch its state
+ * afterwards
+ */
+ if (conn->block_subchannel > conn->count_children) {
+ fprintf(stderr, "Illegal subchannel\n");
+ return -1;
+ }
+
+ wsi_child = conn->wsi_children[conn->block_subchannel];
+
+ switch (wsi_child->mode) {
+
+ /* client receives something */
+
+ case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY:
+ case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE:
+ case LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY:
+ case LWS_CONNMODE_WS_CLIENT:
+// fprintf(stderr, " client\n");
+ libwebsocket_client_rx_sm(wsi_child, c);
+
+ return 0;
+
+ /* server is receiving from client */
+
+ default:
+// fprintf(stderr, " server\n");
+ if (libwebsocket_rx_sm(wsi_child, c) < 0)
+ fprintf(stderr, "probs\n");
+
+ break;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+
+
+int lws_extension_callback_x_google_mux(
+ struct libwebsocket_context *context,
+ struct libwebsocket_extension *ext,
+ struct libwebsocket *wsi,
+ enum libwebsocket_extension_callback_reasons reason,
+ void *user, void *in, size_t len)
+{
+ unsigned char send_buf[LWS_SEND_BUFFER_PRE_PADDING + 4096 +
+ LWS_SEND_BUFFER_POST_PADDING];
+ struct lws_ext_x_google_mux_conn *conn =
+ (struct lws_ext_x_google_mux_conn *)user;
+ struct lws_ext_x_google_mux_conn *parent_conn;
+ struct lws_ext_x_google_mux_conn *child_conn;
+ int n;
+ struct lws_tokens *eff_buf = (struct lws_tokens *)in;
+ unsigned char *p = NULL;
+ struct lws_ext_x_google_mux_context *mux_ctx =
+ ext->per_context_private_data;
+ struct libwebsocket *wsi_parent;
+ struct libwebsocket *wsi_temp;
+ unsigned char *pin = (unsigned char *)in;
+ unsigned char *basepin;
+ int m;
+ int done = 0;
+ unsigned char *pb = &send_buf[LWS_SEND_BUFFER_PRE_PADDING];
+ int subcommand_length;
+
+ if (eff_buf)
+ p = (unsigned char *)eff_buf->token;
+
+ switch (reason) {
+
+ /* these guys are once per context */
+
+ case LWS_EXT_CALLBACK_SERVER_CONTEXT_CONSTRUCT:
+ case LWS_EXT_CALLBACK_CLIENT_CONTEXT_CONSTRUCT:
+
+ ext->per_context_private_data = malloc(
+ sizeof (struct lws_ext_x_google_mux_context));
+ mux_ctx = (struct lws_ext_x_google_mux_context *)
+ ext->per_context_private_data;
+ mux_ctx->active_conns = 0;
+ break;
+
+ case LWS_EXT_CALLBACK_SERVER_CONTEXT_DESTRUCT:
+ case LWS_EXT_CALLBACK_CLIENT_CONTEXT_DESTRUCT:
+
+ if (mux_ctx) {
+ for (n = 0; n < mux_ctx->active_conns; n++)
+ if (mux_ctx->wsi_muxconns[n]) {
+ libwebsocket_close_and_free_session(
+ context,
+ mux_ctx->wsi_muxconns[n],
+ LWS_CLOSE_STATUS_GOINGAWAY);
+ mux_ctx->wsi_muxconns[n] = NULL;
+ }
+
+ free(mux_ctx);
+ }
+ break;
+
+ /*
+ * channel management
+ */
+
+ case LWS_EXT_CALLBACK_CAN_PROXY_CLIENT_CONNECTION:
+
+ muxdebug("LWS_EXT_CALLBACK_CAN_PROXY_CLIENT_CONNECTION %s:%u\n", (char *)in, (unsigned int)len);
+
+ /*
+ * Does a physcial connection to the same server:port already
+ * exist so we can piggyback on it?
+ */
+
+ for (n = 0; n < mux_ctx->active_conns && !done; n++) {
+
+ wsi_parent = mux_ctx->wsi_muxconns[n];
+ if (!wsi_parent)
+ continue;
+
+ muxdebug(" %s / %s\n", wsi_parent->c_address, (char *)in);
+ if (strcmp(wsi_parent->c_address, in))
+ continue;
+ muxdebug(" %u / %u\n", wsi_parent->c_port, (unsigned int)len);
+
+ if (wsi_parent->c_port != (unsigned int)len)
+ continue;
+
+ /*
+ * does this potential parent already have an
+ * x-google-mux conn associated with him?
+ */
+
+ parent_conn = NULL;
+ for (m = 0; m < wsi_parent->count_active_extensions; m++)
+ if (ext == wsi_parent->active_extensions[m])
+ parent_conn = (struct lws_ext_x_google_mux_conn *)
+ wsi_parent->active_extensions_user[m];
+
+ if (parent_conn == NULL) {
+
+ /*
+ * he doesn't -- see if that's just because it
+ * is early in his connection sequence or if we
+ * should give up on him
+ */
+
+ switch (wsi_parent->mode) {
+ case LWS_CONNMODE_WS_SERVING:
+ case LWS_CONNMODE_WS_CLIENT:
+ continue;
+ default:
+ break;
+ }
+
+ /*
+ * our putative parent is still connecting
+ * himself, we have to become a candidate child
+ * and find out our final fate when the parent
+ * completes connection
+ */
+
+ wsi->candidate_children_list = wsi_parent->candidate_children_list;
+ wsi_parent->candidate_children_list = wsi;
+ wsi->mode = LWS_CONNMODE_WS_CLIENT_PENDING_CANDIDATE_CHILD;
+
+ done = 1;
+ continue;
+ }
+
+ if (parent_conn->count_children >=
+ sizeof(parent_conn->wsi_children) /
+ sizeof(parent_conn->wsi_children[0]))
+ continue;
+ /*
+ * this established connection will do, bind them
+ * from now on child will only operate through parent
+ * connection
+ */
+
+ conn->wsi_parent = wsi_parent;
+ parent_conn->wsi_children[
+ parent_conn->count_children] = wsi;
+
+ /*
+ * and now we have to ask the server to allow us to do
+ * this
+ */
+
+ if (lws_ext_x_google_mux__send_addchannel(context,
+ wsi_parent, wsi, parent_conn->count_children,
+ (const char *)in) < 0) {
+ fprintf(stderr, "Addchannel failed\n");
+ continue;
+ }
+
+ parent_conn->count_children++;
+
+ fprintf(stderr, "!x-google-mux: muxing connection! CHILD ADD %d to %p\n", parent_conn->count_children - 1, (void *)wsi);
+
+ done = 1;
+ n = mux_ctx->active_conns;
+ }
+
+ /*
+ * either way, note the existence of this connection in case
+ * he will become a possible mux parent later
+ */
+
+ mux_ctx->wsi_muxconns[mux_ctx->active_conns++] = wsi;
+ if (done)
+ return 1;
+
+ fprintf(stderr, "x-google-mux: unable to mux connection\n");
+
+ break;
+
+ /* these guys are once per connection */
+
+ case LWS_EXT_CALLBACK_CLIENT_CONSTRUCT:
+ muxdebug("LWS_EXT_CALLBACK_CLIENT_CONSTRUCT: setting parent = %p\n", (void *)tag_with_parent);
+ conn->state = LWS_EXT_XGM_STATE__MUX_BLOCK_1;
+ conn->wsi_parent = tag_with_parent;
+ break;
+
+ case LWS_EXT_CALLBACK_CONSTRUCT:
+ muxdebug("LWS_EXT_CALLBACK_CONSTRUCT\n");
+ conn->state = LWS_EXT_XGM_STATE__MUX_BLOCK_1;
+ break;
+
+ case LWS_EXT_CALLBACK_DESTROY:
+ muxdebug("LWS_EXT_CALLBACK_DESTROY\n");
+ break;
+
+ case LWS_EXT_CALLBACK_DESTROY_ANY_WSI_CLOSING:
+ muxdebug("LWS_EXT_CALLBACK_DESTROY_ANY_WSI_CLOSING\n");
+
+ for (n = 0; n < mux_ctx->active_conns; n++)
+ if (mux_ctx->wsi_muxconns[n] == wsi) {
+ while (n++ < mux_ctx->active_conns)
+ mux_ctx->wsi_muxconns[n - 1] =
+ mux_ctx->wsi_muxconns[n];
+ mux_ctx->active_conns--;
+ return 0;
+ }
+
+ /*
+ * liberate any candidate children otherwise imprisoned
+ */
+
+ wsi_parent = wsi->candidate_children_list;
+ while (wsi_parent) {
+ wsi_temp = wsi_parent->candidate_children_list;
+ /* let them each connect privately then */
+ __libwebsocket_client_connect_2(context, wsi_parent);
+ wsi_parent = wsi_temp;
+ }
+
+ break;
+
+ case LWS_EXT_CALLBACK_ANY_WSI_ESTABLISHED:
+ muxdebug("LWS_EXT_CALLBACK_ANY_WSI_ESTABLISHED\n");
+
+ /*
+ * did this putative parent get x-google-mux authorized in the
+ * end?
+ */
+
+ if (!conn) {
+
+ muxdebug(" Putative parent didn't get mux extension, let them go it alone\n");
+
+ /*
+ * no, we can't be a parent for mux children. Let
+ * them all go it alone
+ */
+
+ wsi_parent = wsi->candidate_children_list;
+ while (wsi_parent) {
+ wsi_temp = wsi_parent->candidate_children_list;
+ /* let them each connect privately then */
+ __libwebsocket_client_connect_2(context, wsi_parent);
+ wsi_parent = wsi_temp;
+ }
+
+ break;
+ }
+
+ /*
+ * we did get mux extension authorized by server, in that case
+ * if we have any candidate children let's try to attach them
+ * as mux subchannel real children
+ */
+
+ wsi_parent = wsi->candidate_children_list;
+ while (wsi_parent) {
+
+ muxdebug(" using mux addchannel action for candidate child\n");
+
+ wsi_temp = wsi_parent->candidate_children_list;
+ /* let them each connect privately then */
+ lws_ext_x_google_mux__send_addchannel(context, wsi,
+ wsi_parent,
+ conn->count_children, wsi->c_path);
+
+ conn->wsi_children[conn->count_children++] = wsi_parent;
+ muxdebug("Setting CHILD LIST entry %d to %p\n", conn->count_children - 1, (void *)wsi_parent);
+ wsi_parent = wsi_temp;
+ }
+ wsi->candidate_children_list = NULL;
+ break;
+
+ /*
+ * whenever we receive something on a muxed link
+ */
+
+ case LWS_EXT_CALLBACK_EXTENDED_PAYLOAD_RX:
+
+ muxdebug("LWS_EXT_CALLBACK_EXTENDED_PAYLOAD_RX\n");
+
+ if (wsi->opcode != LWS_WS_OPCODE_07__NOSPEC__MUX)
+ return 0; /* unhandled */
+
+ conn->state = LWS_EXT_XGM_STATE__MUX_BLOCK_1;
+
+ n = eff_buf->token_len;
+ while (n--)
+ lws_extension_x_google_mux_parser(context, wsi, ext, conn, *p++);
+
+ return 1; /* handled */
+
+ /*
+ * when something might need sending on our transport
+ */
+
+ case LWS_EXT_CALLBACK_PACKET_TX_DO_SEND:
+
+ muxdebug("LWS_EXT_CALLBACK_PACKET_TX_DO_SEND: %p\n", (void *)conn->wsi_parent);
+
+ pin = *((unsigned char **)in);
+
+ /*
+ * he's not a child connection of a mux
+ */
+
+ if (!conn->wsi_parent)
+ return 0;
+
+ /*
+ * get parent / transport mux context
+ */
+
+ parent_conn = lws_get_extension_user_matching_ext(conn->wsi_parent, ext);
+ if (parent_conn == 0) {
+ fprintf(stderr, "failed to get parent conn\n");
+ return 0;
+ }
+
+ /*
+ * mux transport is in singular mode, let the caller send it
+ * no more muxified than it already is
+ */
+
+ if (parent_conn->count_children == 0)
+ return 0;
+
+ /*
+ * otherwise we need to take care of the sending action using
+ * mux protocol. Prepend the channel + opcode
+ */
+
+ pin -= lws_addheader_mux_opcode(send_buf, len + 2) + 2;
+ basepin = pin;
+ pin += lws_addheader_mux_opcode(pin, len + 2);
+
+ *pin++ = (conn->subchannel >> 8) | LWS_EXT_XGM_OPC__DATA;
+ *pin++ = conn->subchannel;
+
+ /*
+ * recurse to allow nesting
+ */
+
+ lws_issue_raw(conn->wsi_parent, basepin, (pin - basepin) + len);
+
+ return 1; /* handled */
+
+ case LWS_EXT_CALLBACK_1HZ:
+ /*
+ * if we have children, service their timeouts using the same
+ * handler as toplevel guys to allow recursion
+ */
+ for (n = 0; n < conn->count_children; n++)
+ libwebsocket_service_timeout_check(context,
+ conn->wsi_children[n], len);
+ break;
+
+ case LWS_EXT_CALLBACK_REQUEST_ON_WRITEABLE:
+ /*
+ * if a mux child is asking for callback on writable, we have
+ * to pass it up to his parent
+ */
+
+ muxdebug("LWS_EXT_CALLBACK_REQUEST_ON_WRITEABLE %s\n", wsi->protocol->name);
+
+ if (conn->wsi_parent == NULL) {
+ muxdebug(" no parent\n");
+ break;
+ }
+
+ if (!conn->awaiting_POLLOUT) {
+
+ muxdebug(" !conn->awaiting_POLLOUT\n");
+
+ conn->awaiting_POLLOUT = 1;
+ parent_conn = NULL;
+ for (m = 0; m < conn->wsi_parent->count_active_extensions; m++)
+ if (ext == conn->wsi_parent->active_extensions[m])
+ parent_conn = (struct lws_ext_x_google_mux_conn *)
+ conn->wsi_parent->active_extensions_user[m];
+
+ if (parent_conn != NULL) {
+ parent_conn->count_children_needing_POLLOUT++;
+ muxdebug(" count_children_needing_POLLOUT bumped\n");
+ } else
+ fprintf(stderr, "unable to identify parent conn\n");
+ }
+ muxdebug(" requesting on parent %p\n", (void *)conn->wsi_parent);
+ libwebsocket_callback_on_writable(context, conn->wsi_parent);
+
+ return 1;
+
+ case LWS_EXT_CALLBACK_HANDSHAKE_REPLY_TX:
+
+ fprintf(stderr, "LWS_EXT_CALLBACK_HANDSHAKE_REPLY_TX %p\n", (void *)wsi->extension_handles);
+
+ /* send raw if we're not a child */
+
+ if (!wsi->extension_handles)
+ return 0;
+
+ subcommand_length = lws_mux_subcommand_header(LWS_EXT_XGM_OPC__ADDCHANNEL, ongoing_subchannel, pb, len);
+
+ pb += lws_addheader_mux_opcode(pb, subcommand_length + len);
+ pb += lws_mux_subcommand_header(LWS_EXT_XGM_OPC__ADDCHANNEL, ongoing_subchannel, pb, len);
+ memcpy(pb, in, len);
+ pb += len;
+
+ lws_issue_raw(wsi->extension_handles, &send_buf[LWS_SEND_BUFFER_PRE_PADDING],
+ pb - &send_buf[LWS_SEND_BUFFER_PRE_PADDING]);
+
+
+ return 1; /* handled */
+
+ case LWS_EXT_CALLBACK_IS_WRITEABLE:
+ /*
+ * we are writable, inform children if any care
+ */
+ muxdebug("LWS_EXT_CALLBACK_IS_WRITEABLE: %s\n", wsi->protocol->name);
+
+ if (!conn->count_children_needing_POLLOUT) {
+ muxdebug(" no children need POLLOUT\n");
+ return 0;
+ }
+
+ for (n = 0; n < conn->count_children; n++) {
+
+ child_conn = NULL;
+ for (m = 0; m < conn->wsi_children[n]->count_active_extensions; m++)
+ if (ext == conn->wsi_children[n]->active_extensions[m])
+ child_conn = (struct lws_ext_x_google_mux_conn *)
+ conn->wsi_children[n]->active_extensions_user[m];
+
+ if (!child_conn) {
+ fprintf(stderr, "unable to identify child conn\n");
+ continue;
+ }
+
+ if (!child_conn->awaiting_POLLOUT)
+ continue;
+
+ child_conn->awaiting_POLLOUT = 0;
+ conn->count_children_needing_POLLOUT--;
+ lws_handle_POLLOUT_event(context, conn->wsi_children[n], NULL);
+ if (!conn->count_children_needing_POLLOUT)
+ return 2; /* all handled */
+ else
+ return 1; /* handled but need more */
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
--- /dev/null
+
+#if 0
+#ifdef WIN32
+static
+#else
+static inline
+#endif
+void muxdebug(const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap);
+}
+#else
+#ifdef WIN32
+static
+#else
+static inline
+#endif
+void muxdebug(const char *format, ...)
+{
+}
+#endif
+
+#define MAX_XGM_SUBCHANNELS 8192
+
+enum lws_ext_x_google_mux__parser_states {
+ LWS_EXT_XGM_STATE__MUX_BLOCK_1,
+ LWS_EXT_XGM_STATE__MUX_BLOCK_2,
+ LWS_EXT_XGM_STATE__ADDCHANNEL_LEN,
+ LWS_EXT_XGM_STATE__ADDCHANNEL_LEN16_1,
+ LWS_EXT_XGM_STATE__ADDCHANNEL_LEN16_2,
+ LWS_EXT_XGM_STATE__ADDCHANNEL_LEN32_1,
+ LWS_EXT_XGM_STATE__ADDCHANNEL_LEN32_2,
+ LWS_EXT_XGM_STATE__ADDCHANNEL_LEN32_3,
+ LWS_EXT_XGM_STATE__ADDCHANNEL_LEN32_4,
+ LWS_EXT_XGM_STATE__ADDCHANNEL_HEADERS,
+ LWS_EXT_XGM_STATE__FLOWCONTROL_1,
+ LWS_EXT_XGM_STATE__FLOWCONTROL_2,
+ LWS_EXT_XGM_STATE__FLOWCONTROL_3,
+ LWS_EXT_XGM_STATE__FLOWCONTROL_4,
+ LWS_EXT_XGM_STATE__DATA,
+};
+
+enum lws_ext_x_goole_mux__mux_opcodes {
+ LWS_EXT_XGM_OPC__DATA,
+ LWS_EXT_XGM_OPC__ADDCHANNEL,
+ LWS_EXT_XGM_OPC__DROPCHANNEL,
+ LWS_EXT_XGM_OPC__FLOWCONTROL,
+ LWS_EXT_XGM_OPC__RESERVED_4,
+ LWS_EXT_XGM_OPC__RESERVED_5,
+ LWS_EXT_XGM_OPC__RESERVED_6,
+ LWS_EXT_XGM_OPC__RESERVED_7,
+};
+
+/* one of these per context (server or client) */
+
+struct lws_ext_x_google_mux_context {
+ /*
+ * these are listing physical connections, not children sharing a
+ * parent mux physical connection
+ */
+ struct libwebsocket *wsi_muxconns[MAX_CLIENTS];
+ /*
+ * when this is < 2, we do not do any mux blocks
+ * just pure websockets
+ */
+ int active_conns;
+};
+
+inline int use_mux_blocks(struct lws_ext_x_google_mux_context * mux_context) { \
+ return !!(mux_context->active_conns > 1); }
+
+/* one of these per connection (server or client) */
+
+struct lws_ext_x_google_mux_conn {
+ enum lws_ext_x_goole_mux__mux_opcodes block_subopcode;
+ int block_subchannel;
+ unsigned int length;
+ enum lws_ext_x_google_mux__parser_states state;
+ /* child points to the mux wsi using this */
+ struct libwebsocket *wsi_parent;
+ int subchannel;
+ struct libwebsocket *wsi_children[MAX_CLIENTS];
+ int count_children;
+ char awaiting_POLLOUT;
+ int count_children_needing_POLLOUT;
+};
+
+extern int
+lws_extension_callback_x_google_mux(struct libwebsocket_context *context,
+ struct libwebsocket_extension *ext,
+ struct libwebsocket *wsi,
+ enum libwebsocket_extension_callback_reasons reason,
+ void *user, void *in, size_t len);
#include "private-libwebsockets.h"
#include "extension-deflate-stream.h"
+#include "extension-x-google-mux.h"
struct libwebsocket_extension libwebsocket_internal_extensions[] = {
+#ifdef LWS_EXT_GOOGLE_MUX
+ {
+ "x-google-mux",
+ lws_extension_callback_x_google_mux,
+ sizeof (struct lws_ext_x_google_mux_conn)
+ },
+#endif
{
"deflate-stream",
lws_extension_callback_deflate_stream,
static int
-handshake_00(struct libwebsocket *wsi)
+handshake_00(struct libwebsocket_context *context, struct libwebsocket *wsi)
{
unsigned long key1, key2;
unsigned char sum[16];
*/
static int
-handshake_0405(struct libwebsocket *wsi)
+handshake_0405(struct libwebsocket_context *context, struct libwebsocket *wsi)
{
static const char *websocket_magic_guid_04 =
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
*/
c = wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token;
+ fprintf(stderr, "wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token = %s\n", wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token);
+ wsi->count_active_extensions = 0;
n = 0;
while (more) {
-
- if (*c && *c != ',') {
+
+ if (*c && (*c != ',' && *c != ' ' && *c != '\t')) {
ext_name[n] = *c++;
if (n < sizeof(ext_name) - 1)
n++;
ext_name[n] = '\0';
if (!*c)
more = 0;
+ else {
+ c++;
+ if (!n)
+ continue;
+ }
/* check a client's extension against our support */
wsi->active_extensions_user[
wsi->count_active_extensions] =
malloc(ext->per_session_data_size);
+ memset(wsi->active_extensions_user[
+ wsi->count_active_extensions], 0,
+ ext->per_session_data_size);
+
wsi->active_extensions[
wsi->count_active_extensions] = ext;
wsi->count_active_extensions], NULL, 0);
wsi->count_active_extensions++;
+ fprintf(stderr, "wsi->count_active_extensions <- %d", wsi->count_active_extensions);
ext++;
}
wsi->masking_key_04);
}
- /* okay send the handshake response accepting the connection */
+ if (!lws_any_extension_handled(context, wsi,
+ LWS_EXT_CALLBACK_HANDSHAKE_REPLY_TX,
+ response, p - response)) {
+
+ /* okay send the handshake response accepting the connection */
+
+ debug("issuing response packet %d len\n", (int)(p - response));
+ #ifdef DEBUG
+ fwrite(response, 1, p - response, stderr);
+ #endif
+ n = libwebsocket_write(wsi, (unsigned char *)response,
+ p - response, LWS_WRITE_HTTP);
+ if (n < 0) {
+ fprintf(stderr, "ERROR writing to socket");
+ goto bail;
+ }
- debug("issuing response packet %d len\n", (int)(p - response));
-#ifdef DEBUG
- fwrite(response, 1, p - response, stderr);
-#endif
- n = libwebsocket_write(wsi, (unsigned char *)response,
- p - response, LWS_WRITE_HTTP);
- if (n < 0) {
- fprintf(stderr, "ERROR writing to socket");
- goto bail;
}
/* alright clean up and set ourselves into established state */
#endif
switch (wsi->mode) {
+ case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY:
+ case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE:
+ case LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY:
+ case LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT:
case LWS_CONNMODE_WS_CLIENT:
for (n = 0; n < len; n++)
libwebsocket_client_rx_sm(wsi, *buf++);
if (wsi->parser_state != WSI_PARSING_COMPLETE)
break;
- debug("libwebsocket_parse sees parsing complete\n");
+ fprintf(stderr, "seem to be serving, mode is %d\n", wsi->mode);
+
+ fprintf(stderr, "libwebsocket_parse sees parsing complete\n");
/* is this websocket protocol or normal http 1.0? */
return 0;
}
+ if (!wsi->protocol) {
+ fprintf(stderr, "NULL protocol coming on libwebsocket_read\n");
+ }
+
/*
* It's websocket
*
switch (wsi->ietf_spec_revision) {
case 0: /* applies to 76 and 00 */
wsi->xor_mask = xor_no_mask;
- if (handshake_00(wsi))
+ if (handshake_00(context, wsi))
goto bail;
break;
case 4: /* 04 */
wsi->xor_mask = xor_mask_04;
debug("libwebsocket_parse calling handshake_04\n");
- if (handshake_0405(wsi))
+ if (handshake_0405(context, wsi))
goto bail;
break;
case 5:
case 7:
wsi->xor_mask = xor_mask_05;
debug("libwebsocket_parse calling handshake_04\n");
- if (handshake_0405(wsi))
+ if (handshake_0405(context, wsi))
goto bail;
break;
int ret;
int m;
struct lws_tokens eff_buf;
+ struct libwebsocket_extension *ext;
if (!wsi)
return;
* remove this fd from wsi mapping hashtable
*/
- delete_from_fd(context, wsi->sock);
+ if (wsi->sock)
+ delete_from_fd(context, wsi->sock);
/* delete it from the internal poll list if still present */
}
/* remove also from external POLL support via protocol 0 */
-
- context->protocols[0].callback(context, wsi,
+ if (wsi->sock)
+ context->protocols[0].callback(context, wsi,
LWS_CALLBACK_DEL_POLL_FD, (void *)(long)wsi->sock, NULL, 0);
wsi->state = WSI_STATE_DEAD_SOCKET;
free(wsi->active_extensions_user[n]);
}
+ /*
+ * inform all extensions in case they tracked this guy out of band
+ * even though not active on him specifically
+ */
+
+ ext = context->extensions;
+ while (ext && ext->callback) {
+ ext->callback(context, ext, wsi,
+ LWS_EXT_CALLBACK_DESTROY_ANY_WSI_CLOSING,
+ NULL, NULL, 0);
+ ext++;
+ }
+
/* free up his parsing allocations */
for (n = 0; n < WSI_TOKEN_COUNT; n++)
if (wsi->utf8_token[n].token)
free(wsi->utf8_token[n].token);
+ if (wsi->c_address)
+ free(wsi->c_address);
+
/* fprintf(stderr, "closing fd=%d\n", wsi->sock); */
#ifdef LWS_OPENSSL_SUPPORT
return 0;
}
-static int
+int
lws_handle_POLLOUT_event(struct libwebsocket_context *context,
struct libwebsocket *wsi, struct pollfd *pollfd)
{
int n;
int ret;
int m;
+ int handled = 0;
+
+ for (n = 0; n < wsi->count_active_extensions; n++) {
+ if (!wsi->active_extensions[n]->callback)
+ continue;
+
+ m = wsi->active_extensions[n]->callback(context,
+ wsi->active_extensions[n], wsi,
+ LWS_EXT_CALLBACK_IS_WRITEABLE,
+ wsi->active_extensions_user[n], NULL, 0);
+ if (m > handled)
+ handled = m;
+ }
+
+ if (handled == 1)
+ goto notify_action;
- if (!wsi->extension_data_pending)
+ if (!wsi->extension_data_pending || handled == 2)
goto user_service;
/*
user_service:
/* one shot */
- pollfd->events &= ~POLLOUT;
+ if (pollfd) {
+ pollfd->events &= ~POLLOUT;
- /* external POLL support via protocol 0 */
- context->protocols[0].callback(context, wsi,
- LWS_CALLBACK_CLEAR_MODE_POLL_FD,
- (void *)(long)wsi->sock, NULL, POLLOUT);
+ /* external POLL support via protocol 0 */
+ context->protocols[0].callback(context, wsi,
+ LWS_CALLBACK_CLEAR_MODE_POLL_FD,
+ (void *)(long)wsi->sock, NULL, POLLOUT);
+ }
+
+notify_action:
if (wsi->mode == LWS_CONNMODE_WS_CLIENT)
n = LWS_CALLBACK_CLIENT_WRITEABLE;
+void
+libwebsocket_service_timeout_check(struct libwebsocket_context *context,
+ struct libwebsocket *wsi, unsigned int sec)
+{
+ int n;
+
+ /*
+ * if extensions want in on it (eg, we are a mux parent)
+ * give them a chance to service child timeouts
+ */
+
+ for (n = 0; n < wsi->count_active_extensions; n++)
+ wsi->active_extensions[n]->callback(
+ context, wsi->active_extensions[n],
+ wsi, LWS_EXT_CALLBACK_1HZ,
+ wsi->active_extensions_user[n], NULL, sec);
+
+ if (!wsi->pending_timeout)
+ return;
+
+ /*
+ * if we went beyond the allowed time, kill the
+ * connection
+ */
+
+ if (sec > wsi->pending_timeout_limit) {
+ fprintf(stderr, "TIMEDOUT WAITING\n");
+ libwebsocket_close_and_free_session(context,
+ wsi, LWS_CLOSE_STATUS_NOSTATUS);
+ }
+}
+
+struct libwebsocket *
+libwebsocket_create_new_server_wsi(struct libwebsocket_context *context)
+{
+ struct libwebsocket *new_wsi;
+ int n;
+
+ new_wsi = malloc(sizeof(struct libwebsocket));
+ if (new_wsi == NULL) {
+ fprintf(stderr, "Out of memory for new connection\n");
+ return NULL;
+ }
+
+ memset(new_wsi, 0, sizeof (struct libwebsocket));
+ new_wsi->count_active_extensions = 0;
+ new_wsi->pending_timeout = NO_PENDING_TIMEOUT;
+
+ /* intialize the instance struct */
+
+ new_wsi->state = WSI_STATE_HTTP;
+ new_wsi->name_buffer_pos = 0;
+ new_wsi->mode = LWS_CONNMODE_WS_SERVING;
+
+ for (n = 0; n < WSI_TOKEN_COUNT; n++) {
+ new_wsi->utf8_token[n].token = NULL;
+ new_wsi->utf8_token[n].token_len = 0;
+ }
+
+ /*
+ * these can only be set once the protocol is known
+ * we set an unestablished connection's protocol pointer
+ * to the start of the supported list, so it can look
+ * for matching ones during the handshake
+ */
+ new_wsi->protocol = context->protocols;
+ new_wsi->user_space = NULL;
+
+ /*
+ * Default protocol is 76 / 00
+ * After 76, there's a header specified to inform which
+ * draft the client wants, when that's seen we modify
+ * the individual connection's spec revision accordingly
+ */
+ new_wsi->ietf_spec_revision = 0;
+
+ return new_wsi;
+}
+
+char *
+libwebsockets_generate_client_handshake(struct libwebsocket_context *context,
+ struct libwebsocket *wsi, char *pkt)
+{
+ char hash[20];
+ char *p = pkt;
+ int n;
+ struct libwebsocket_extension *ext;
+ int ext_count = 0;
+ unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 1 + MAX_BROADCAST_PAYLOAD +
+ LWS_SEND_BUFFER_POST_PADDING];
+ static const char magic_websocket_guid[] =
+ "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+
+ /*
+ * create the random key
+ */
+
+ n = libwebsockets_get_random(context, hash, 16);
+ if (n != 16) {
+ fprintf(stderr, "Unable to read from random dev %s\n",
+ SYSTEM_RANDOM_FILEPATH);
+ free(wsi->c_path);
+ free(wsi->c_host);
+ if (wsi->c_origin)
+ free(wsi->c_origin);
+ if (wsi->c_protocol)
+ free(wsi->c_protocol);
+ libwebsocket_close_and_free_session(context, wsi,
+ LWS_CLOSE_STATUS_NOSTATUS);
+ return NULL;
+ }
+
+ lws_b64_encode_string(hash, 16, wsi->key_b64,
+ sizeof wsi->key_b64);
+
+ /*
+ * 00 example client handshake
+ *
+ * GET /socket.io/websocket HTTP/1.1
+ * Upgrade: WebSocket
+ * Connection: Upgrade
+ * Host: 127.0.0.1:9999
+ * Origin: http://127.0.0.1
+ * Sec-WebSocket-Key1: 1 0 2#0W 9 89 7 92 ^
+ * Sec-WebSocket-Key2: 7 7Y 4328 B2v[8(z1
+ * Cookie: socketio=websocket
+ *
+ * (Á®Ä0¶†≥
+ *
+ * 04 example client handshake
+ *
+ * GET /chat HTTP/1.1
+ * Host: server.example.com
+ * Upgrade: websocket
+ * Connection: Upgrade
+ * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
+ * Sec-WebSocket-Origin: http://example.com
+ * Sec-WebSocket-Protocol: chat, superchat
+ * Sec-WebSocket-Version: 4
+ */
+
+ p += sprintf(p, "GET %s HTTP/1.1\x0d\x0a", wsi->c_path);
+
+ if (wsi->ietf_spec_revision == 0) {
+ unsigned char spaces_1, spaces_2;
+ unsigned int max_1, max_2;
+ unsigned int num_1, num_2;
+ unsigned long product_1, product_2;
+ char key_1[40];
+ char key_2[40];
+ unsigned int seed;
+ unsigned int count;
+ char challenge[16];
+
+ libwebsockets_get_random(context, &spaces_1,
+ sizeof(char));
+ libwebsockets_get_random(context, &spaces_2,
+ sizeof(char));
+
+ spaces_1 = (spaces_1 % 12) + 1;
+ spaces_2 = (spaces_2 % 12) + 1;
+
+ max_1 = 4294967295 / spaces_1;
+ max_2 = 4294967295 / spaces_2;
+
+ libwebsockets_get_random(context, &num_1, sizeof(int));
+ libwebsockets_get_random(context, &num_2, sizeof(int));
+
+ num_1 = (num_1 % max_1);
+ num_2 = (num_2 % max_2);
+
+ challenge[0] = num_1 >> 24;
+ challenge[1] = num_1 >> 16;
+ challenge[2] = num_1 >> 8;
+ challenge[3] = num_1;
+ challenge[4] = num_2 >> 24;
+ challenge[5] = num_2 >> 16;
+ challenge[6] = num_2 >> 8;
+ challenge[7] = num_2;
+
+ product_1 = num_1 * spaces_1;
+ product_2 = num_2 * spaces_2;
+
+ sprintf(key_1, "%lu", product_1);
+ sprintf(key_2, "%lu", product_2);
+
+ libwebsockets_get_random(context, &seed, sizeof(int));
+ libwebsockets_get_random(context, &count, sizeof(int));
+
+ libwebsockets_00_spam(key_1, (count % 12) + 1, seed);
+
+ libwebsockets_get_random(context, &seed, sizeof(int));
+ libwebsockets_get_random(context, &count, sizeof(int));
+
+ libwebsockets_00_spam(key_2, (count % 12) + 1, seed);
+
+ libwebsockets_get_random(context, &seed, sizeof(int));
+
+ libwebsockets_00_spaceout(key_1, spaces_1, seed);
+ libwebsockets_00_spaceout(key_2, spaces_2, seed >> 16);
+
+ p += sprintf(p, "Upgrade: WebSocket\x0d\x0a"
+ "Connection: Upgrade\x0d\x0aHost: %s\x0d\x0a",
+ wsi->c_host);
+ if (wsi->c_origin)
+ p += sprintf(p, "Origin: %s\x0d\x0a",
+ wsi->c_origin);
+
+ if (wsi->c_protocol)
+ p += sprintf(p, "Sec-WebSocket-Protocol: %s"
+ "\x0d\x0a", wsi->c_protocol);
+
+ p += sprintf(p, "Sec-WebSocket-Key1: %s\x0d\x0a",
+ key_1);
+ p += sprintf(p, "Sec-WebSocket-Key2: %s\x0d\x0a",
+ key_2);
+
+ /* give userland a chance to append, eg, cookies */
+
+ context->protocols[0].callback(context, wsi,
+ LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
+ NULL, &p, (pkt + sizeof(pkt)) - p - 12);
+
+ p += sprintf(p, "\x0d\x0a");
+
+ if (libwebsockets_get_random(context, p, 8) != 8)
+ return NULL;
+ memcpy(&challenge[8], p, 8);
+ p += 8;
+
+ /* precompute what we want to see from the server */
+
+ MD5((unsigned char *)challenge, 16,
+ (unsigned char *)wsi->initial_handshake_hash_base64);
+
+ goto issue_hdr;
+ }
+
+ p += sprintf(p, "Host: %s\x0d\x0a", wsi->c_host);
+ p += sprintf(p, "Upgrade: websocket\x0d\x0a");
+ p += sprintf(p, "Connection: Upgrade\x0d\x0a"
+ "Sec-WebSocket-Key: ");
+ strcpy(p, wsi->key_b64);
+ p += strlen(wsi->key_b64);
+ p += sprintf(p, "\x0d\x0a");
+ if (wsi->c_origin)
+ p += sprintf(p, "Sec-WebSocket-Origin: %s\x0d\x0a",
+ wsi->c_origin);
+ if (wsi->c_protocol)
+ p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a",
+ wsi->c_protocol);
+
+ /* tell the server what extensions we could support */
+
+ p += sprintf(p, "Sec-WebSocket-Extensions: ");
+
+ ext =context->extensions;
+ while (ext && ext->callback) {
+
+ n = 0;
+ n = context->protocols[0].callback(context, wsi,
+ LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED,
+ wsi->user_space, (char *)ext->name, 0);
+
+ /*
+ * zero return from callback means
+ * go ahead and allow the extension,
+ * it's what we get if the callback is
+ * unhandled
+ */
+
+ if (n) {
+ ext++;
+ continue;
+ }
+
+ /* apply it */
+
+ if (ext_count)
+ *p++ = ',';
+ p += sprintf(p, "%s", ext->name);
+ ext_count++;
+
+ ext++;
+ }
+
+ p += sprintf(p, "\x0d\x0a");
+
+ if (wsi->ietf_spec_revision)
+ p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a",
+ wsi->ietf_spec_revision);
+
+ /* give userland a chance to append, eg, cookies */
+
+ context->protocols[0].callback(context, wsi,
+ LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
+ NULL, &p, (pkt + sizeof(pkt)) - p - 12);
+
+ p += sprintf(p, "\x0d\x0a");
+
+ /* prepare the expected server accept response */
+
+ strcpy((char *)buf, wsi->key_b64);
+ strcpy((char *)&buf[strlen((char *)buf)], magic_websocket_guid);
+
+ SHA1(buf, strlen((char *)buf), (unsigned char *)hash);
+
+ lws_b64_encode_string(hash, 20,
+ wsi->initial_handshake_hash_base64,
+ sizeof wsi->initial_handshake_hash_base64);
+
+issue_hdr:
+
+ /* done with these now */
+
+ free(wsi->c_path);
+ free(wsi->c_host);
+ if (wsi->c_origin)
+ free(wsi->c_origin);
+
+ return p;
+}
+
+int
+lws_client_interpret_server_handshake(struct libwebsocket_context *context,
+ struct libwebsocket *wsi)
+{
+ unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 1 + MAX_BROADCAST_PAYLOAD +
+ LWS_SEND_BUFFER_POST_PADDING];
+ char pkt[1024];
+ char *p = &pkt[0];
+ const char *pc;
+ const char *c;
+ int more = 1;
+ int okay = 0;
+ char ext_name[128];
+ struct libwebsocket_extension *ext;
+ void *v;
+ int len;
+ int n;
+ static const char magic_websocket_04_masking_guid[] =
+ "61AC5F19-FBBA-4540-B96F-6561F1AB40A8";
+
+ /*
+ * 00 / 76 -->
+ *
+ * HTTP/1.1 101 WebSocket Protocol Handshake
+ * Upgrade: WebSocket
+ * Connection: Upgrade
+ * Sec-WebSocket-Origin: http://127.0.0.1
+ * Sec-WebSocket-Location: ws://127.0.0.1:9999/socket.io/websocket
+ *
+ * xxxxxxxxxxxxxxxx
+ */
+
+ if (wsi->ietf_spec_revision == 0) {
+ if (!wsi->utf8_token[WSI_TOKEN_HTTP].token_len ||
+ !wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len ||
+ !wsi->utf8_token[WSI_TOKEN_CHALLENGE].token_len ||
+ !wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len ||
+ (!wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len &&
+ wsi->c_protocol != NULL)) {
+ fprintf(stderr, "libwebsocket_client_handshake "
+ "missing required header(s)\n");
+ pkt[len] = '\0';
+ fprintf(stderr, "%s", pkt);
+ goto bail3;
+ }
+
+ strtolower(wsi->utf8_token[WSI_TOKEN_HTTP].token);
+ if (strcmp(wsi->utf8_token[WSI_TOKEN_HTTP].token,
+ "101 websocket protocol handshake")) {
+ fprintf(stderr, "libwebsocket_client_handshake "
+ "server sent bad HTTP response '%s'\n",
+ wsi->utf8_token[WSI_TOKEN_HTTP].token);
+ goto bail3;
+ }
+
+ if (wsi->utf8_token[WSI_TOKEN_CHALLENGE].token_len <
+ 16) {
+ fprintf(stderr, "libwebsocket_client_handshake "
+ "challenge reply too short %d\n",
+ wsi->utf8_token[
+ WSI_TOKEN_CHALLENGE].token_len);
+ pkt[len] = '\0';
+ fprintf(stderr, "%s", pkt);
+ goto bail3;
+
+ }
+
+ goto select_protocol;
+ }
+
+ /*
+ * well, what the server sent looked reasonable for syntax.
+ * Now let's confirm it sent all the necessary headers
+ */
+#if 0
+ fprintf(stderr, "WSI_TOKEN_HTTP: %d\n", wsi->utf8_token[WSI_TOKEN_HTTP].token_len);
+ fprintf(stderr, "WSI_TOKEN_UPGRADE: %d\n", wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len);
+ fprintf(stderr, "WSI_TOKEN_CONNECTION: %d\n", wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len);
+ fprintf(stderr, "WSI_TOKEN_ACCEPT: %d\n", wsi->utf8_token[WSI_TOKEN_ACCEPT].token_len);
+ fprintf(stderr, "WSI_TOKEN_NONCE: %d\n", wsi->utf8_token[WSI_TOKEN_NONCE].token_len);
+ fprintf(stderr, "WSI_TOKEN_PROTOCOL: %d\n", wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len);
+#endif
+ if (!wsi->utf8_token[WSI_TOKEN_HTTP].token_len ||
+ !wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len ||
+ !wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len ||
+ !wsi->utf8_token[WSI_TOKEN_ACCEPT].token_len ||
+ (!wsi->utf8_token[WSI_TOKEN_NONCE].token_len &&
+ wsi->ietf_spec_revision == 4) ||
+ (!wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len &&
+ wsi->c_protocol != NULL)) {
+ fprintf(stderr, "libwebsocket_client_handshake "
+ "missing required header(s)\n");
+ pkt[len] = '\0';
+ fprintf(stderr, "%s", pkt);
+ goto bail3;
+ }
+
+ /*
+ * Everything seems to be there, now take a closer look at what
+ * is in each header
+ */
+
+ strtolower(wsi->utf8_token[WSI_TOKEN_HTTP].token);
+ if (strcmp(wsi->utf8_token[WSI_TOKEN_HTTP].token,
+ "101 switching protocols")) {
+ fprintf(stderr, "libwebsocket_client_handshake "
+ "server sent bad HTTP response '%s'\n",
+ wsi->utf8_token[WSI_TOKEN_HTTP].token);
+ goto bail3;
+ }
+
+ strtolower(wsi->utf8_token[WSI_TOKEN_UPGRADE].token);
+ if (strcmp(wsi->utf8_token[WSI_TOKEN_UPGRADE].token,
+ "websocket")) {
+ fprintf(stderr, "libwebsocket_client_handshake server "
+ "sent bad Upgrade header '%s'\n",
+ wsi->utf8_token[WSI_TOKEN_UPGRADE].token);
+ goto bail3;
+ }
+
+ strtolower(wsi->utf8_token[WSI_TOKEN_CONNECTION].token);
+ if (strcmp(wsi->utf8_token[WSI_TOKEN_CONNECTION].token,
+ "upgrade")) {
+ fprintf(stderr, "libwebsocket_client_handshake server "
+ "sent bad Connection hdr '%s'\n",
+ wsi->utf8_token[WSI_TOKEN_CONNECTION].token);
+ goto bail3;
+ }
+
+select_protocol:
+ pc = wsi->c_protocol;
+ if (pc == NULL)
+ fprintf(stderr, "lws_client_interpret_server_handshake: NULL c_protocol\n");
+ else
+ fprintf(stderr, "lws_client_interpret_server_handshake: cPprotocol='%s'\n", pc);
+
+ /*
+ * confirm the protocol the server wants to talk was in the list
+ * of protocols we offered
+ */
+
+ if (!wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len) {
+
+ fprintf(stderr, "lws_client_interpret_server_handshake WSI_TOKEN_PROTOCOL is null\n");
+ /*
+ * no protocol name to work from,
+ * default to first protocol
+ */
+ wsi->protocol = &context->protocols[0];
+
+ free(wsi->c_protocol);
+
+ goto check_accept;
+ }
+
+ while (*pc && !okay) {
+ if ((!strncmp(pc,
+ wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
+ wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len)) &&
+ (pc[wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len] == ',' ||
+ pc[wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len] == '\0')) {
+ okay = 1;
+ continue;
+ }
+ while (*pc && *pc != ',')
+ pc++;
+ while (*pc && *pc != ' ')
+ pc++;
+ }
+
+ /* done with him now */
+
+ if (wsi->c_protocol)
+ free(wsi->c_protocol);
+
+
+ if (!okay) {
+ fprintf(stderr, "libwebsocket_client_handshake server "
+ "sent bad protocol '%s'\n",
+ wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
+ goto bail2;
+ }
+
+ /*
+ * identify the selected protocol struct and set it
+ */
+ n = 0;
+ wsi->protocol = NULL;
+ while (context->protocols[n].callback) {
+ if (strcmp(wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
+ context->protocols[n].name) == 0)
+ wsi->protocol = &context->protocols[n];
+ n++;
+ }
+
+ if (wsi->protocol == NULL) {
+ fprintf(stderr, "libwebsocket_client_handshake server "
+ "requested protocol '%s', which we "
+ "said we supported but we don't!\n",
+ wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
+ goto bail2;
+ }
+
+
+ /* instantiate the accepted extensions */
+
+ if (!wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token_len) {
+ fprintf(stderr, "no client extenstions allowed by server \n");
+ goto check_accept;
+ }
+
+ /*
+ * break down the list of server accepted extensions
+ * and go through matching them or identifying bogons
+ */
+
+ c = wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token;
+ n = 0;
+ while (more) {
+
+ if (*c && (*c != ',' && *c != ' ' && *c != '\t')) {
+ ext_name[n] = *c++;
+ if (n < sizeof(ext_name) - 1)
+ n++;
+ continue;
+ }
+ ext_name[n] = '\0';
+ if (!*c)
+ more = 0;
+ else {
+ c++;
+ if (!n)
+ continue;
+ }
+
+ /* check we actually support it */
+
+ fprintf(stderr, "checking client ext %s\n", ext_name);
+
+ n = 0;
+ ext = wsi->protocol->owning_server->extensions;
+ while (ext && ext->callback) {
+
+ if (strcmp(ext_name, ext->name)) {
+ ext++;
+ continue;
+ }
+
+ n = 1;
+
+ fprintf(stderr, "instantiating client ext %s\n", ext_name);
+
+ /* instantiate the extension on this conn */
+
+ wsi->active_extensions_user[
+ wsi->count_active_extensions] =
+ malloc(ext->per_session_data_size);
+ wsi->active_extensions[
+ wsi->count_active_extensions] = ext;
+
+ /* allow him to construct his context */
+
+ ext->callback(wsi->protocol->owning_server,
+ ext, wsi,
+ LWS_EXT_CALLBACK_CLIENT_CONSTRUCT,
+ wsi->active_extensions_user[
+ wsi->count_active_extensions],
+ NULL, 0);
+
+ wsi->count_active_extensions++;
+
+ ext++;
+ }
+
+ if (n == 0) {
+ fprintf(stderr, "Server said we should use"
+ "an unknown extension '%s'!\n", ext_name);
+ goto bail2;
+ }
+
+ n = 0;
+ }
+
+
+check_accept:
+
+ if (wsi->ietf_spec_revision == 0) {
+
+ if (memcmp(wsi->initial_handshake_hash_base64,
+ wsi->utf8_token[WSI_TOKEN_CHALLENGE].token, 16)) {
+ fprintf(stderr, "libwebsocket_client_handshake "
+ "failed 00 challenge compare\n");
+ pkt[len] = '\0';
+ fprintf(stderr, "%s", pkt);
+ goto bail2;
+ }
+
+ goto accept_ok;
+ }
+
+ /*
+ * Confirm his accept token is the one we precomputed
+ */
+
+ if (strcmp(wsi->utf8_token[WSI_TOKEN_ACCEPT].token,
+ wsi->initial_handshake_hash_base64)) {
+ fprintf(stderr, "libwebsocket_client_handshake server "
+ "sent bad ACCEPT '%s' vs computed '%s'\n",
+ wsi->utf8_token[WSI_TOKEN_ACCEPT].token,
+ wsi->initial_handshake_hash_base64);
+ goto bail2;
+ }
+
+ if (wsi->ietf_spec_revision == 4) {
+ /*
+ * Calculate the 04 masking key to use when
+ * sending data to server
+ */
+
+ strcpy((char *)buf, wsi->key_b64);
+ p = (char *)buf + strlen(wsi->key_b64);
+ strcpy(p, wsi->utf8_token[WSI_TOKEN_NONCE].token);
+ p += wsi->utf8_token[WSI_TOKEN_NONCE].token_len;
+ strcpy(p, magic_websocket_04_masking_guid);
+ SHA1(buf, strlen((char *)buf), wsi->masking_key_04);
+ }
+ accept_ok:
+
+ /* allocate the per-connection user memory (if any) */
+
+ if (wsi->protocol->per_session_data_size) {
+ wsi->user_space = malloc(
+ wsi->protocol->per_session_data_size);
+ if (wsi->user_space == NULL) {
+ fprintf(stderr, "Out of memory for "
+ "conn user space\n");
+ goto bail2;
+ }
+ } else
+ wsi->user_space = NULL;
+
+ /* clear his proxy connection timeout */
+
+ libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+
+ /* mark him as being alive */
+
+ wsi->state = WSI_STATE_ESTABLISHED;
+ wsi->mode = LWS_CONNMODE_WS_CLIENT;
+
+ fprintf(stderr, "handshake OK for protocol %s\n",
+ wsi->protocol->name);
+
+ /* call him back to inform him he is up */
+
+ wsi->protocol->callback(context, wsi,
+ LWS_CALLBACK_CLIENT_ESTABLISHED,
+ wsi->user_space,
+ NULL, 0);
+
+ /*
+ * inform all extensions, not just active ones since they
+ * already know
+ */
+
+ ext = context->extensions;
+
+ while (ext && ext->callback) {
+ v = NULL;
+ for (n = 0; n < wsi->count_active_extensions; n++)
+ if (wsi->active_extensions[n] == ext)
+ v = wsi->active_extensions_user[n];
+
+ ext->callback(context, ext, wsi,
+ LWS_EXT_CALLBACK_ANY_WSI_ESTABLISHED, v, NULL, 0);
+ ext++;
+ }
+
+ return 0;
+
+bail3:
+ if (wsi->c_protocol)
+ free(wsi->c_protocol);
+
+bail2:
+ libwebsocket_close_and_free_session(context, wsi,
+ LWS_CLOSE_STATUS_NOSTATUS);
+ return 1;
+}
+
+
+
/**
* libwebsocket_service_fd() - Service polled socket with something waiting
* @context: Websocket context
unsigned int clilen;
struct sockaddr_in cli_addr;
struct timeval tv;
- static const char magic_websocket_guid[] =
- "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
- static const char magic_websocket_04_masking_guid[] =
- "61AC5F19-FBBA-4540-B96F-6561F1AB40A8";
- char hash[20];
char pkt[1024];
char *p = &pkt[0];
- const char *pc;
- const char *c;
int more = 1;
- int okay = 0;
- char ext_name[128];
struct lws_tokens eff_buf;
- int ext_count = 0;
- struct libwebsocket_extension *ext;
int opt = 1;
#ifdef LWS_OPENSSL_SUPPORT
for (n = 0; n < context->fds_count; n++) {
wsi = wsi_from_fd(context, context->fds[n].fd);
- if (!wsi->pending_timeout)
- continue;
-
- /*
- * if we went beyond the allowed time, kill the
- * connection
- */
- if (tv.tv_sec > wsi->pending_timeout_limit) {
- fprintf(stderr, "TIMEDOUT WAITING\n");
- libwebsocket_close_and_free_session(context,
- wsi, LWS_CLOSE_STATUS_NOSTATUS);
- }
+ libwebsocket_service_timeout_check(context, wsi,
+ tv.tv_sec);
}
}
/* accepting connection to main listener */
- new_wsi = malloc(sizeof(struct libwebsocket));
- if (new_wsi == NULL) {
- fprintf(stderr, "Out of memory for new connection\n");
+ new_wsi = libwebsocket_create_new_server_wsi(context);
+ if (new_wsi == NULL)
break;
- }
- memset(new_wsi, 0, sizeof (struct libwebsocket));
new_wsi->sock = accept_fd;
- new_wsi->count_active_extensions = 0;
- new_wsi->pending_timeout = NO_PENDING_TIMEOUT;
+
#ifdef LWS_OPENSSL_SUPPORT
new_wsi->ssl = NULL;
debug("accepted new conn port %u on fd=%d\n",
ntohs(cli_addr.sin_port), accept_fd);
- /* intialize the instance struct */
-
- new_wsi->state = WSI_STATE_HTTP;
- new_wsi->name_buffer_pos = 0;
- new_wsi->mode = LWS_CONNMODE_WS_SERVING;
-
- for (n = 0; n < WSI_TOKEN_COUNT; n++) {
- new_wsi->utf8_token[n].token = NULL;
- new_wsi->utf8_token[n].token_len = 0;
- }
-
- /*
- * these can only be set once the protocol is known
- * we set an unestablished connection's protocol pointer
- * to the start of the supported list, so it can look
- * for matching ones during the handshake
- */
- new_wsi->protocol = context->protocols;
- new_wsi->user_space = NULL;
-
- /*
- * Default protocol is 76 / 00
- * After 76, there's a header specified to inform which
- * draft the client wants, when that's seen we modify
- * the individual connection's spec revision accordingly
- */
- new_wsi->ietf_spec_revision = 0;
-
insert_wsi(context, new_wsi);
/*
/* clear his proxy connection timeout */
- libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
-
- /* fallthru */
-
- case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE:
-
- #ifdef LWS_OPENSSL_SUPPORT
- if (wsi->use_ssl) {
-
- wsi->ssl = SSL_new(context->ssl_client_ctx);
- wsi->client_bio = BIO_new_socket(wsi->sock,
- BIO_NOCLOSE);
- SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio);
-
- SSL_set_ex_data(wsi->ssl,
- openssl_websocket_private_data_index,
- context);
-
- if (SSL_connect(wsi->ssl) <= 0) {
- fprintf(stderr, "SSL connect error %s\n",
- ERR_error_string(ERR_get_error(),
- ssl_err_buf));
- libwebsocket_close_and_free_session(context, wsi,
- LWS_CLOSE_STATUS_NOSTATUS);
- return 1;
- }
-
- n = SSL_get_verify_result(wsi->ssl);
- if ((n != X509_V_OK) && (
- n != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
- wsi->use_ssl != 2)) {
-
- fprintf(stderr, "server's cert didn't "
- "look good %d\n", n);
- libwebsocket_close_and_free_session(context,
- wsi, LWS_CLOSE_STATUS_NOSTATUS);
- return 1;
- }
- } else {
- wsi->ssl = NULL;
- #endif
-
-
- #ifdef LWS_OPENSSL_SUPPORT
- }
- #endif
-
- /*
- * create the random key
- */
-
- n = libwebsockets_get_random(context, hash, 16);
- if (n != 16) {
- fprintf(stderr, "Unable to read from random dev %s\n",
- SYSTEM_RANDOM_FILEPATH);
- free(wsi->c_path);
- free(wsi->c_host);
- if (wsi->c_origin)
- free(wsi->c_origin);
- if (wsi->c_protocol)
- free(wsi->c_protocol);
- libwebsocket_close_and_free_session(context, wsi,
- LWS_CLOSE_STATUS_NOSTATUS);
- return 1;
- }
-
- lws_b64_encode_string(hash, 16, wsi->key_b64,
- sizeof wsi->key_b64);
-
- /*
- * 00 example client handshake
- *
- * GET /socket.io/websocket HTTP/1.1
- * Upgrade: WebSocket
- * Connection: Upgrade
- * Host: 127.0.0.1:9999
- * Origin: http://127.0.0.1
- * Sec-WebSocket-Key1: 1 0 2#0W 9 89 7 92 ^
- * Sec-WebSocket-Key2: 7 7Y 4328 B2v[8(z1
- * Cookie: socketio=websocket
- *
- * (Á®Ä0¶†≥
- *
- * 04 example client handshake
- *
- * GET /chat HTTP/1.1
- * Host: server.example.com
- * Upgrade: websocket
- * Connection: Upgrade
- * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
- * Sec-WebSocket-Origin: http://example.com
- * Sec-WebSocket-Protocol: chat, superchat
- * Sec-WebSocket-Version: 4
- */
-
- p += sprintf(p, "GET %s HTTP/1.1\x0d\x0a", wsi->c_path);
-
- if (wsi->ietf_spec_revision == 0) {
- unsigned char spaces_1, spaces_2;
- unsigned int max_1, max_2;
- unsigned int num_1, num_2;
- unsigned long product_1, product_2;
- char key_1[40];
- char key_2[40];
- unsigned int seed;
- unsigned int count;
- char challenge[16];
-
- libwebsockets_get_random(context, &spaces_1,
- sizeof(char));
- libwebsockets_get_random(context, &spaces_2,
- sizeof(char));
-
- spaces_1 = (spaces_1 % 12) + 1;
- spaces_2 = (spaces_2 % 12) + 1;
-
- max_1 = 4294967295 / spaces_1;
- max_2 = 4294967295 / spaces_2;
-
- libwebsockets_get_random(context, &num_1, sizeof(int));
- libwebsockets_get_random(context, &num_2, sizeof(int));
-
- num_1 = (num_1 % max_1);
- num_2 = (num_2 % max_2);
-
- challenge[0] = num_1 >> 24;
- challenge[1] = num_1 >> 16;
- challenge[2] = num_1 >> 8;
- challenge[3] = num_1;
- challenge[4] = num_2 >> 24;
- challenge[5] = num_2 >> 16;
- challenge[6] = num_2 >> 8;
- challenge[7] = num_2;
-
- product_1 = num_1 * spaces_1;
- product_2 = num_2 * spaces_2;
-
- sprintf(key_1, "%lu", product_1);
- sprintf(key_2, "%lu", product_2);
-
- libwebsockets_get_random(context, &seed, sizeof(int));
- libwebsockets_get_random(context, &count, sizeof(int));
-
- libwebsockets_00_spam(key_1, (count % 12) + 1, seed);
-
- libwebsockets_get_random(context, &seed, sizeof(int));
- libwebsockets_get_random(context, &count, sizeof(int));
-
- libwebsockets_00_spam(key_2, (count % 12) + 1, seed);
-
- libwebsockets_get_random(context, &seed, sizeof(int));
-
- libwebsockets_00_spaceout(key_1, spaces_1, seed);
- libwebsockets_00_spaceout(key_2, spaces_2, seed >> 16);
-
- p += sprintf(p, "Upgrade: WebSocket\x0d\x0a"
- "Connection: Upgrade\x0d\x0aHost: %s\x0d\x0a",
- wsi->c_host);
- if (wsi->c_origin)
- p += sprintf(p, "Origin: %s\x0d\x0a",
- wsi->c_origin);
-
- if (wsi->c_protocol)
- p += sprintf(p, "Sec-WebSocket-Protocol: %s"
- "\x0d\x0a", wsi->c_protocol);
-
- p += sprintf(p, "Sec-WebSocket-Key1: %s\x0d\x0a",
- key_1);
- p += sprintf(p, "Sec-WebSocket-Key2: %s\x0d\x0a",
- key_2);
-
- /* give userland a chance to append, eg, cookies */
-
- context->protocols[0].callback(context, wsi,
- LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
- NULL, &p, (pkt + sizeof(pkt)) - p - 12);
-
- p += sprintf(p, "\x0d\x0a");
-
- if (libwebsockets_get_random(context, p, 8) != 8)
- return -1;
- memcpy(&challenge[8], p, 8);
- p += 8;
-
- /* precompute what we want to see from the server */
-
- MD5((unsigned char *)challenge, 16,
- (unsigned char *)wsi->initial_handshake_hash_base64);
-
- goto issue_hdr;
- }
-
- p += sprintf(p, "Host: %s\x0d\x0a", wsi->c_host);
- p += sprintf(p, "Upgrade: websocket\x0d\x0a");
- p += sprintf(p, "Connection: Upgrade\x0d\x0a"
- "Sec-WebSocket-Key: ");
- strcpy(p, wsi->key_b64);
- p += strlen(wsi->key_b64);
- p += sprintf(p, "\x0d\x0a");
- if (wsi->c_origin)
- p += sprintf(p, "Sec-WebSocket-Origin: %s\x0d\x0a",
- wsi->c_origin);
- if (wsi->c_protocol)
- p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a",
- wsi->c_protocol);
-
- /* tell the server what extensions we could support */
-
- p += sprintf(p, "Sec-WebSocket-Extensions: ");
-
- ext =context->extensions;
- while (ext && ext->callback) {
-
- n = 0;
- n = context->protocols[0].callback(context, wsi,
- LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED,
- wsi->user_space, (char *)ext->name, 0);
-
- /*
- * zero return from callback means
- * go ahead and allow the extension,
- * it's what we get if the callback is
- * unhandled
- */
-
- if (n) {
- ext++;
- continue;
- }
+ libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
- /* apply it */
-
- if (ext_count)
- *p++ = ',';
- p += sprintf(p, "%s", ext->name);
- ext_count++;
+ /* fallthru */
- ext++;
- }
+ case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE:
- p += sprintf(p, "\x0d\x0a");
+ #ifdef LWS_OPENSSL_SUPPORT
+ if (wsi->use_ssl) {
- if (wsi->ietf_spec_revision)
- p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a",
- wsi->ietf_spec_revision);
+ wsi->ssl = SSL_new(context->ssl_client_ctx);
+ wsi->client_bio = BIO_new_socket(wsi->sock,
+ BIO_NOCLOSE);
+ SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio);
- /* give userland a chance to append, eg, cookies */
-
- context->protocols[0].callback(context, wsi,
- LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
- NULL, &p, (pkt + sizeof(pkt)) - p - 12);
-
- p += sprintf(p, "\x0d\x0a");
+ SSL_set_ex_data(wsi->ssl,
+ openssl_websocket_private_data_index,
+ context);
- /* prepare the expected server accept response */
+ if (SSL_connect(wsi->ssl) <= 0) {
+ fprintf(stderr, "SSL connect error %s\n",
+ ERR_error_string(ERR_get_error(),
+ ssl_err_buf));
+ libwebsocket_close_and_free_session(context, wsi,
+ LWS_CLOSE_STATUS_NOSTATUS);
+ return 1;
+ }
- strcpy((char *)buf, wsi->key_b64);
- strcpy((char *)&buf[strlen((char *)buf)], magic_websocket_guid);
+ n = SSL_get_verify_result(wsi->ssl);
+ if ((n != X509_V_OK) && (
+ n != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
+ wsi->use_ssl != 2)) {
- SHA1(buf, strlen((char *)buf), (unsigned char *)hash);
+ fprintf(stderr, "server's cert didn't "
+ "look good %d\n", n);
+ libwebsocket_close_and_free_session(context,
+ wsi, LWS_CLOSE_STATUS_NOSTATUS);
+ return 1;
+ }
+ } else {
+ wsi->ssl = NULL;
+ #endif
- lws_b64_encode_string(hash, 20,
- wsi->initial_handshake_hash_base64,
- sizeof wsi->initial_handshake_hash_base64);
-issue_hdr:
-
- /* done with these now */
-
- free(wsi->c_path);
- free(wsi->c_host);
- if (wsi->c_origin)
- free(wsi->c_origin);
+ #ifdef LWS_OPENSSL_SUPPORT
+ }
+ #endif
+
+ p = libwebsockets_generate_client_handshake(context, wsi, p);
+ if (p ==NULL)
+ return 1;
/* send our request to the server */
if (wsi->parser_state != WSI_PARSING_COMPLETE)
break;
- /*
- * 00 / 76 -->
- *
- * HTTP/1.1 101 WebSocket Protocol Handshake
- * Upgrade: WebSocket
- * Connection: Upgrade
- * Sec-WebSocket-Origin: http://127.0.0.1
- * Sec-WebSocket-Location: ws://127.0.0.1:9999/socket.io/websocket
- *
- * xxxxxxxxxxxxxxxx
- */
-
- if (wsi->ietf_spec_revision == 0) {
- if (!wsi->utf8_token[WSI_TOKEN_HTTP].token_len ||
- !wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len ||
- !wsi->utf8_token[WSI_TOKEN_CHALLENGE].token_len ||
- !wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len ||
- (!wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len &&
- wsi->c_protocol != NULL)) {
- fprintf(stderr, "libwebsocket_client_handshake "
- "missing required header(s)\n");
- pkt[len] = '\0';
- fprintf(stderr, "%s", pkt);
- goto bail3;
- }
-
- strtolower(wsi->utf8_token[WSI_TOKEN_HTTP].token);
- if (strcmp(wsi->utf8_token[WSI_TOKEN_HTTP].token,
- "101 websocket protocol handshake")) {
- fprintf(stderr, "libwebsocket_client_handshake "
- "server sent bad HTTP response '%s'\n",
- wsi->utf8_token[WSI_TOKEN_HTTP].token);
- goto bail3;
- }
-
- if (wsi->utf8_token[WSI_TOKEN_CHALLENGE].token_len <
- 16) {
- fprintf(stderr, "libwebsocket_client_handshake "
- "challenge reply too short %d\n",
- wsi->utf8_token[
- WSI_TOKEN_CHALLENGE].token_len);
- pkt[len] = '\0';
- fprintf(stderr, "%s", pkt);
- goto bail3;
-
- }
-
- goto select_protocol;
- }
-
- /*
- * well, what the server sent looked reasonable for syntax.
- * Now let's confirm it sent all the necessary headers
- */
-
- if (!wsi->utf8_token[WSI_TOKEN_HTTP].token_len ||
- !wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len ||
- !wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len ||
- !wsi->utf8_token[WSI_TOKEN_ACCEPT].token_len ||
- (!wsi->utf8_token[WSI_TOKEN_NONCE].token_len &&
- wsi->ietf_spec_revision == 4) ||
- (!wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len &&
- wsi->c_protocol != NULL)) {
- fprintf(stderr, "libwebsocket_client_handshake "
- "missing required header(s)\n");
- pkt[len] = '\0';
- fprintf(stderr, "%s", pkt);
- goto bail3;
- }
-
- /*
- * Everything seems to be there, now take a closer look at what
- * is in each header
- */
-
- strtolower(wsi->utf8_token[WSI_TOKEN_HTTP].token);
- if (strcmp(wsi->utf8_token[WSI_TOKEN_HTTP].token,
- "101 switching protocols")) {
- fprintf(stderr, "libwebsocket_client_handshake "
- "server sent bad HTTP response '%s'\n",
- wsi->utf8_token[WSI_TOKEN_HTTP].token);
- goto bail3;
- }
-
- strtolower(wsi->utf8_token[WSI_TOKEN_UPGRADE].token);
- if (strcmp(wsi->utf8_token[WSI_TOKEN_UPGRADE].token,
- "websocket")) {
- fprintf(stderr, "libwebsocket_client_handshake server "
- "sent bad Upgrade header '%s'\n",
- wsi->utf8_token[WSI_TOKEN_UPGRADE].token);
- goto bail3;
- }
-
- strtolower(wsi->utf8_token[WSI_TOKEN_CONNECTION].token);
- if (strcmp(wsi->utf8_token[WSI_TOKEN_CONNECTION].token,
- "upgrade")) {
- fprintf(stderr, "libwebsocket_client_handshake server "
- "sent bad Connection hdr '%s'\n",
- wsi->utf8_token[WSI_TOKEN_CONNECTION].token);
- goto bail3;
- }
-
-select_protocol:
- pc = wsi->c_protocol;
-
- /*
- * confirm the protocol the server wants to talk was in the list
- * of protocols we offered
- */
-
- if (!wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len) {
-
- /*
- * no protocol name to work from,
- * default to first protocol
- */
- wsi->protocol = &context->protocols[0];
-
- free(wsi->c_protocol);
-
- goto check_accept;
- }
-
- while (*pc && !okay) {
- if ((!strncmp(pc,
- wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
- wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len)) &&
- (pc[wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len] == ',' ||
- pc[wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len] == '\0')) {
- okay = 1;
- continue;
- }
- while (*pc && *pc != ',')
- pc++;
- while (*pc && *pc != ' ')
- pc++;
- }
-
- /* done with him now */
+ return lws_client_interpret_server_handshake(context, wsi);
+bail3:
if (wsi->c_protocol)
free(wsi->c_protocol);
+ libwebsocket_close_and_free_session(context, wsi,
+ LWS_CLOSE_STATUS_NOSTATUS);
+ return 1;
-
- if (!okay) {
- fprintf(stderr, "libwebsocket_client_handshake server "
- "sent bad protocol '%s'\n",
- wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
- goto bail2;
- }
-
- /*
- * identify the selected protocol struct and set it
- */
- n = 0;
- wsi->protocol = NULL;
- while (context->protocols[n].callback) {
- if (strcmp(wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
- context->protocols[n].name) == 0)
- wsi->protocol = &context->protocols[n];
- n++;
- }
-
- if (wsi->protocol == NULL) {
- fprintf(stderr, "libwebsocket_client_handshake server "
- "requested protocol '%s', which we "
- "said we supported but we don't!\n",
- wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
- goto bail2;
- }
-
-
- /* instantiate the accepted extensions */
-
- if (!wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token_len)
- goto check_accept;
-
- /*
- * break down the list of server accepted extensions
- * and go through matching them or identifying bogons
- */
-
- c = wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token;
- n = 0;
- while (more) {
-
- if (*c && *c != ',') {
- ext_name[n] = *c++;
- if (n < sizeof(ext_name) - 1)
- n++;
- continue;
- }
- ext_name[n] = '\0';
- if (!*c)
- more = 0;
-
- /* check we actually support it */
-
- n = 0;
- ext = wsi->protocol->owning_server->extensions;
- while (ext && ext->callback) {
-
- if (strcmp(ext_name, ext->name)) {
- ext++;
- continue;
- }
-
- n = 1;
-
- /* instantiate the extension on this conn */
-
- wsi->active_extensions_user[
- wsi->count_active_extensions] =
- malloc(ext->per_session_data_size);
- wsi->active_extensions[
- wsi->count_active_extensions] = ext;
-
- /* allow him to construct his context */
-
- ext->callback(wsi->protocol->owning_server,
- ext, wsi,
- LWS_EXT_CALLBACK_CLIENT_CONSTRUCT,
- wsi->active_extensions_user[
- wsi->count_active_extensions],
- NULL, 0);
-
- wsi->count_active_extensions++;
-
- ext++;
- }
-
- if (n == 0) {
- fprintf(stderr, "Server said we should use"
- "an unknown extension '%s'!\n", ext_name);
- goto bail2;
- }
-
- n = 0;
- }
-
-
- check_accept:
-
- if (wsi->ietf_spec_revision == 0) {
-
- if (memcmp(wsi->initial_handshake_hash_base64,
- wsi->utf8_token[WSI_TOKEN_CHALLENGE].token, 16)) {
- fprintf(stderr, "libwebsocket_client_handshake "
- "failed 00 challenge compare\n");
- pkt[len] = '\0';
- fprintf(stderr, "%s", pkt);
- goto bail2;
- }
-
- goto accept_ok;
- }
-
- /*
- * Confirm his accept token is the one we precomputed
- */
-
- if (strcmp(wsi->utf8_token[WSI_TOKEN_ACCEPT].token,
- wsi->initial_handshake_hash_base64)) {
- fprintf(stderr, "libwebsocket_client_handshake server "
- "sent bad ACCEPT '%s' vs computed '%s'\n",
- wsi->utf8_token[WSI_TOKEN_ACCEPT].token,
- wsi->initial_handshake_hash_base64);
- goto bail2;
- }
-
- if (wsi->ietf_spec_revision == 4) {
- /*
- * Calculate the 04 masking key to use when
- * sending data to server
- */
-
- strcpy((char *)buf, wsi->key_b64);
- p = (char *)buf + strlen(wsi->key_b64);
- strcpy(p, wsi->utf8_token[WSI_TOKEN_NONCE].token);
- p += wsi->utf8_token[WSI_TOKEN_NONCE].token_len;
- strcpy(p, magic_websocket_04_masking_guid);
- SHA1(buf, strlen((char *)buf), wsi->masking_key_04);
- }
-accept_ok:
-
- /* allocate the per-connection user memory (if any) */
-
- if (wsi->protocol->per_session_data_size) {
- wsi->user_space = malloc(
- wsi->protocol->per_session_data_size);
- if (wsi->user_space == NULL) {
- fprintf(stderr, "Out of memory for "
- "conn user space\n");
- goto bail2;
- }
- } else
- wsi->user_space = NULL;
-
- /* clear his proxy connection timeout */
-
- libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
-
- /* mark him as being alive */
-
- wsi->state = WSI_STATE_ESTABLISHED;
- wsi->mode = LWS_CONNMODE_WS_CLIENT;
-
- fprintf(stderr, "handshake OK for protocol %s\n",
- wsi->protocol->name);
-
- /* call him back to inform him he is up */
-
- wsi->protocol->callback(context, wsi,
- LWS_CALLBACK_CLIENT_ESTABLISHED,
- wsi->user_space,
- NULL, 0);
-
+ case LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT:
+ fprintf(stderr, "LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT\n");
break;
-bail3:
- if (wsi->c_protocol)
- free(wsi->c_protocol);
+ case LWS_CONNMODE_WS_CLIENT_PENDING_CANDIDATE_CHILD:
+ fprintf(stderr, "LWS_CONNMODE_WS_CLIENT_PENDING_CANDIDATE_CHILD\n");
+ break;
-bail2:
- libwebsocket_close_and_free_session(context, wsi,
- LWS_CLOSE_STATUS_NOSTATUS);
- return 1;
-
case LWS_CONNMODE_WS_SERVING:
case LWS_CONNMODE_WS_CLIENT:
int n;
int m;
struct libwebsocket *wsi;
+ struct libwebsocket_extension *ext;
for (n = 0; n < FD_HASHTABLE_MODULUS; n++)
for (m = 0; m < context->fd_hashtable[n].length; m++) {
LWS_CLOSE_STATUS_GOINGAWAY);
}
+ /*
+ * give all extensions a chance to clean up any per-context
+ * allocations they might have made
+ */
+
+ ext = context->extensions;
+ m = LWS_EXT_CALLBACK_CLIENT_CONTEXT_DESTRUCT;
+ if (context->listen_port)
+ m = LWS_EXT_CALLBACK_SERVER_CONTEXT_DESTRUCT;
+ while (ext->callback) {
+ ext->callback(context, ext, NULL, m, NULL, NULL, 0);
+ ext++;
+ }
+
#ifdef WIN32
#else
close(context->fd_random);
return 0;
}
+int
+lws_any_extension_handled(struct libwebsocket_context *context,
+ struct libwebsocket *wsi,
+ enum libwebsocket_extension_callback_reasons r,
+ void *v, size_t len)
+{
+ int n;
+ int handled = 0;
+
+ /* maybe an extension will take care of it for us */
+
+ for (n = 0; n < wsi->count_active_extensions && !handled; n++) {
+ if (!wsi->active_extensions[n]->callback)
+ continue;
+
+ handled |= wsi->active_extensions[n]->callback(context,
+ wsi->active_extensions[n], wsi,
+ r, wsi->active_extensions_user[n], v, len);
+ }
+
+ return handled;
+}
+
+
+void *
+lws_get_extension_user_matching_ext(struct libwebsocket *wsi,
+ struct libwebsocket_extension * ext)
+{
+ int n = 0;
+
+ while (n < wsi->count_active_extensions) {
+ if (wsi->active_extensions[n] != ext) {
+ n++;
+ continue;
+ }
+ return wsi->active_extensions_user[n];
+ }
+
+ return NULL;
+}
+
/**
* libwebsocket_callback_on_writable() - Request a callback when this socket
* becomes able to be written to without
struct libwebsocket *wsi)
{
int n;
+ int handled = 0;
+
+ /* maybe an extension will take care of it for us */
+
+ for (n = 0; n < wsi->count_active_extensions; n++) {
+ if (!wsi->active_extensions[n]->callback)
+ continue;
+
+ handled |= wsi->active_extensions[n]->callback(context,
+ wsi->active_extensions[n], wsi,
+ LWS_EXT_CALLBACK_REQUEST_ON_WRITEABLE,
+ wsi->active_extensions_user[n], NULL, 0);
+ }
+
+ if (handled)
+ return 1;
for (n = 0; n < context->fds_count; n++)
if (context->fds[n].fd == wsi->sock) {
context->fds[n].events |= POLLOUT;
- n = context->fds_count;
+ n = context->fds_count + 1;
}
+ if (n == context->fds_count)
+ fprintf(stderr, "libwebsocket_callback_on_writable: failed to find socket %d\n", wsi->sock);
+
/* external POLL support via protocol 0 */
context->protocols[0].callback(context, wsi,
LWS_CALLBACK_SET_MODE_POLL_FD,
LWS_CALLBACK_CLEAR_MODE_POLL_FD,
(void *)(long)wsi->sock, NULL, POLLIN);
-
- fprintf(stderr, "libwebsocket_callback_on_writable "
+#if 0
+ fprintf(stderr, "libwebsocket_rx_flow_control "
"unable to find socket\n");
+#endif
return 1;
}
int gid, int uid, unsigned int options)
{
int n;
+ int m;
int sockfd = 0;
int fd;
struct sockaddr_in serv_addr, cli_addr;
(void *)(long)fd, NULL, POLLIN);
}
+ /*
+ * give all extensions a chance to create any per-context
+ * allocations they need
+ */
+
+ m = LWS_EXT_CALLBACK_CLIENT_CONTEXT_CONSTRUCT;
+ if (port)
+ m = LWS_EXT_CALLBACK_SERVER_CONTEXT_CONSTRUCT;
+ while (extensions->callback) {
+ extensions->callback(context, extensions,
+ NULL, m, NULL, NULL, 0);
+ extensions++;
+ }
+
return context;
}
#endif
#define CONTEXT_PORT_NO_LISTEN 0
-
+#define MAX_MUX_RECURSION 2
enum libwebsocket_context_options {
LWS_SERVER_OPTION_DEFEAT_CLIENT_MASK = 1,
};
enum libwebsocket_extension_callback_reasons {
+ LWS_EXT_CALLBACK_SERVER_CONTEXT_CONSTRUCT,
+ LWS_EXT_CALLBACK_CLIENT_CONTEXT_CONSTRUCT,
+ LWS_EXT_CALLBACK_SERVER_CONTEXT_DESTRUCT,
+ LWS_EXT_CALLBACK_CLIENT_CONTEXT_DESTRUCT,
LWS_EXT_CALLBACK_CONSTRUCT,
LWS_EXT_CALLBACK_CLIENT_CONSTRUCT,
LWS_EXT_CALLBACK_DESTROY,
+ LWS_EXT_CALLBACK_DESTROY_ANY_WSI_CLOSING,
+ LWS_EXT_CALLBACK_ANY_WSI_ESTABLISHED,
LWS_EXT_CALLBACK_PACKET_RX_PREPARSE,
LWS_EXT_CALLBACK_PACKET_TX_PRESEND,
+ LWS_EXT_CALLBACK_PACKET_TX_DO_SEND,
+ LWS_EXT_CALLBACK_HANDSHAKE_REPLY_TX,
LWS_EXT_CALLBACK_FLUSH_PENDING_TX,
+ LWS_EXT_CALLBACK_EXTENDED_PAYLOAD_RX,
+ LWS_EXT_CALLBACK_CAN_PROXY_CLIENT_CONNECTION,
+ LWS_EXT_CALLBACK_1HZ,
+ LWS_EXT_CALLBACK_REQUEST_ON_WRITEABLE,
+ LWS_EXT_CALLBACK_IS_WRITEABLE,
};
enum libwebsocket_write_protocol {
WSI_TOKEN_ACCEPT,
WSI_TOKEN_NONCE,
WSI_TOKEN_HTTP,
+ WSI_TOKEN_MUXURL,
/* always last real token index*/
WSI_TOKEN_COUNT,
WSI_TOKEN_NAME_PART,
WSI_TOKEN_SKIPPING,
WSI_TOKEN_SKIPPING_SAW_CR,
- WSI_PARSING_COMPLETE
+ WSI_PARSING_COMPLETE,
+ WSI_INIT_TOKEN_MUXURL,
};
/*
enum libwebsocket_extension_callback_reasons reason,
void *user, void *in, size_t len);
size_t per_session_data_size;
+ void * per_context_private_data;
};
/*
* this is the frame nonce plus two header plus 8 length
+ * there's an additional two for mux extension per mux nesting level
* 2 byte prepend on close will already fit because control frames cannot use
* the big length style
*/
-#define LWS_SEND_BUFFER_PRE_PADDING (4 + 10)
+#define LWS_SEND_BUFFER_PRE_PADDING (4 + 10 + (2 * MAX_MUX_RECURSION))
#define LWS_SEND_BUFFER_POST_PADDING 1
extern int
/* [WSI_TOKEN_ACCEPT] = */{ "Sec-WebSocket-Accept:", 21 },
/* [WSI_TOKEN_NONCE] = */{ "Sec-WebSocket-Nonce:", 20 },
/* [WSI_TOKEN_HTTP] = */{ "HTTP/1.1 ", 9 },
+/* [WSI_TOKEN_MUXURL] = */{ "", -1 },
};
case WSI_TOKEN_NONCE:
case WSI_TOKEN_EXTENSIONS:
case WSI_TOKEN_HTTP:
+ case WSI_TOKEN_MUXURL:
+
debug("WSI_TOKEN_(%d) '%c'\n", wsi->parser_state, c);
/* collect into malloc'd buffers */
wsi->utf8_token[wsi->parser_state].token[
wsi->utf8_token[wsi->parser_state].token_len] = '\0';
wsi->parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
+ fprintf(stderr, "*\n");
break;
}
wsi->parser_state = WSI_PARSING_COMPLETE;
break;
+ case WSI_INIT_TOKEN_MUXURL:
+ wsi->parser_state = WSI_TOKEN_MUXURL;
+ wsi->current_alloc_len = LWS_INITIAL_HDR_ALLOC;
+
+ wsi->utf8_token[wsi->parser_state].token =
+ malloc(wsi->current_alloc_len);
+ wsi->utf8_token[wsi->parser_state].token_len = 0;
+ break;
+
/* collecting and checking a name part */
case WSI_TOKEN_NAME_PART:
debug("WSI_TOKEN_NAME_PART '%c'\n", c);
-static int libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c)
+int
+libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c)
{
int n;
unsigned char buf[20 + 4];
+ struct lws_tokens eff_buf;
+ int handled;
+ int m;
+
+// fprintf(stderr, "RX: %02X ", c);
switch (wsi->lws_rx_parse_state) {
case LWS_RXPS_NEW:
* layer? If so service it and hide it from the user callback
*/
+ debug("spill on %s\n", wsi->protocol->name);
+
switch (wsi->opcode) {
case LWS_WS_OPCODE_07__CLOSE:
/* is this an acknowledgement of our close? */
wsi->rx_user_buffer_head = 0;
return 0;
- default:
+ case LWS_WS_OPCODE_07__TEXT_FRAME:
+ case LWS_WS_OPCODE_07__BINARY_FRAME:
break;
+
+ default:
+
+ debug("passing opcode %x up to exts\n", wsi->opcode);
+
+ /*
+ * It's something special we can't understand here.
+ * Pass the payload up to the extension's parsing
+ * state machine.
+ */
+
+ eff_buf.token = &wsi->rx_user_buffer[
+ LWS_SEND_BUFFER_PRE_PADDING];
+ eff_buf.token_len = wsi->rx_user_buffer_head;
+
+ handled = 0;
+ for (n = 0; n < wsi->count_active_extensions; n++) {
+ m = wsi->active_extensions[n]->callback(
+ wsi->protocol->owning_server,
+ wsi->active_extensions[n], wsi,
+ LWS_EXT_CALLBACK_EXTENDED_PAYLOAD_RX,
+ wsi->active_extensions_user[n],
+ &eff_buf, 0);
+ if (m)
+ handled = 1;
+ }
+
+ if (!handled) {
+ /* kill the connection */
+ fprintf(stderr, "Unhandled extended opcode "
+ "0x%x\n", wsi->opcode);
+ return -1;
+ }
+
+ wsi->rx_user_buffer_head = 0;
+ return 0;
}
/*
wsi->user_space,
&wsi->rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
wsi->rx_user_buffer_head);
+ else
+ fprintf(stderr, "No callback on payload spill!\n");
+
wsi->rx_user_buffer_head = 0;
break;
}
int n;
unsigned char buf[20 + 4];
int callback_action = LWS_CALLBACK_CLIENT_RECEIVE;
+ int handled;
+ struct lws_tokens eff_buf;
+ int m;
+
+ debug(" CRX: %02X %d\n", c, wsi->lws_rx_parse_state);
switch (wsi->lws_rx_parse_state) {
case LWS_RXPS_NEW:
if (wsi->rx_user_buffer_head != MAX_USER_RX_BUFFER)
break;
spill:
+
+ handled = 0;
+
/*
* is this frame a control packet we should take care of at this
* layer? If so service it and hide it from the user callback
callback_action = LWS_CALLBACK_CLIENT_RECEIVE_PONG;
break;
+ case LWS_WS_OPCODE_07__CONTINUATION:
+ case LWS_WS_OPCODE_07__TEXT_FRAME:
+ case LWS_WS_OPCODE_07__BINARY_FRAME:
+ break;
+
default:
+
+ fprintf(stderr, "Reserved opcode 0x%2X\n", wsi->opcode);
+ /*
+ * It's something special we can't understand here.
+ * Pass the payload up to the extension's parsing
+ * state machine.
+ */
+
+ eff_buf.token = &wsi->rx_user_buffer[
+ LWS_SEND_BUFFER_PRE_PADDING];
+ eff_buf.token_len = wsi->rx_user_buffer_head;
+
+ for (n = 0; n < wsi->count_active_extensions; n++) {
+ m = wsi->active_extensions[n]->callback(
+ wsi->protocol->owning_server,
+ wsi->active_extensions[n], wsi,
+ LWS_EXT_CALLBACK_EXTENDED_PAYLOAD_RX,
+ wsi->active_extensions_user[n],
+ &eff_buf, 0);
+ if (m)
+ handled = 1;
+ }
+
+ if (!handled) {
+ /* kill the connection */
+ fprintf(stderr, "Unhandled extended opcode "
+ "0x%x\n", wsi->opcode);
+ return -1;
+ }
+
break;
}
* so it can be sent straight out again using libwebsocket_write
*/
- if (wsi->protocol->callback)
+ if (!handled && wsi->protocol->callback)
wsi->protocol->callback(wsi->protocol->owning_server,
wsi, callback_action,
wsi->user_space,
return 0;
}
+void lws_stderr_hexdump(unsigned char *buf, size_t len)
+{
+ int n;
+ int m;
+ int start;
+
+ fprintf(stderr, "\n");
+
+ for (n = 0; n < len;) {
+ start = n;
+
+ fprintf(stderr, "%04X: ", start);
+
+ for (m = 0; m < 16 && n < len; m++)
+ fprintf(stderr, "%02X ", buf[n++]);
+ while (m++ < 16)
+ fprintf(stderr, " ");
+
+ fprintf(stderr, " ");
+
+ for (m = 0; m < 16 && (start + m) < len; m++) {
+ if (buf[start + m] >= ' ' && buf[start + m] <= 127)
+ fprintf(stderr, "%c", buf[start + m]);
+ else
+ fprintf(stderr, ".");
+ }
+ while (m++ < 16)
+ fprintf(stderr, " ");
+
+ fprintf(stderr, "\n");
+ }
+ fprintf(stderr, "\n");
+}
+
int lws_issue_raw(struct libwebsocket *wsi, unsigned char *buf, size_t len)
{
int n;
+ int m;
+
+ /*
+ * one of the extensions is carrying our data itself? Like mux?
+ */
+
+ for (n = 0; n < wsi->count_active_extensions; n++) {
+ /*
+ * there can only be active extensions after handshake completed
+ * so we can rely on protocol being set already in here
+ */
+ m = wsi->active_extensions[n]->callback(
+ wsi->protocol->owning_server,
+ wsi->active_extensions[n], wsi,
+ LWS_EXT_CALLBACK_PACKET_TX_DO_SEND,
+ wsi->active_extensions_user[n], &buf, len);
+ if (m < 0) {
+ fprintf(stderr, "Extension reports fatal error\n");
+ return -1;
+ }
+ if (m) /* handled */
+ return 0;
+ }
+
+ /*
+ * nope, send it on the socket directly
+ */
+
+#if 0
+ fprintf(stderr, " TX: ");
+ lws_stderr_hexdump(buf, len);
+#endif
#ifdef LWS_OPENSSL_SUPPORT
if (wsi->ssl) {
int shift = 7;
struct lws_tokens eff_buf;
int ret;
- int masked7 = wsi->mode == LWS_CONNMODE_WS_CLIENT;
+ int masked7 = wsi->mode == LWS_CONNMODE_WS_CLIENT && wsi->xor_mask != xor_no_mask;
unsigned char *dropmask = NULL;
unsigned char is_masked_bit = 0;
* to control the raw packet payload content
*/
- if (!(protocol & LWS_WRITE_CLIENT_IGNORE_XOR_MASK)) {
+ if (!(protocol & LWS_WRITE_CLIENT_IGNORE_XOR_MASK) && wsi->xor_mask != xor_no_mask) {
if (libwebsocket_0405_frame_mask_generate(wsi)) {
fprintf(stderr, "libwebsocket_write: "
buf[2 - pre] = 0;
buf[3 - pre] = 0;
} else {
- dropmask[0] = 0;
- dropmask[1] = 0;
- dropmask[2] = 0;
- dropmask[3] = 0;
+ if (dropmask && wsi->xor_mask != xor_no_mask) {
+ dropmask[0] = 0;
+ dropmask[1] = 0;
+ dropmask[2] = 0;
+ dropmask[3] = 0;
+ }
}
}
LWS_WS_OPCODE_04__PONG = 3,
LWS_WS_OPCODE_04__TEXT_FRAME = 4,
LWS_WS_OPCODE_04__BINARY_FRAME = 5,
+
+ LWS_WS_OPCODE_04__RESERVED_6 = 6,
+ LWS_WS_OPCODE_04__RESERVED_7 = 7,
+ LWS_WS_OPCODE_04__RESERVED_8 = 8,
+ LWS_WS_OPCODE_04__RESERVED_9 = 9,
+ LWS_WS_OPCODE_04__RESERVED_A = 0xa,
+ LWS_WS_OPCODE_04__RESERVED_B = 0xb,
+ LWS_WS_OPCODE_04__RESERVED_C = 0xc,
+ LWS_WS_OPCODE_04__RESERVED_D = 0xd,
+ LWS_WS_OPCODE_04__RESERVED_E = 0xe,
+ LWS_WS_OPCODE_04__RESERVED_F = 0xf,
};
enum lws_websocket_opcodes_07 {
LWS_WS_OPCODE_07__CONTINUATION = 0,
LWS_WS_OPCODE_07__TEXT_FRAME = 1,
LWS_WS_OPCODE_07__BINARY_FRAME = 2,
+
+ LWS_WS_OPCODE_07__NOSPEC__MUX = 7,
+
+ /* control extensions 8+ */
+
LWS_WS_OPCODE_07__CLOSE = 8,
LWS_WS_OPCODE_07__PING = 9,
LWS_WS_OPCODE_07__PONG = 0xa,
};
+
enum lws_connection_states {
WSI_STATE_HTTP,
WSI_STATE_HTTP_HEADERS,
WSI_STATE_ESTABLISHED,
WSI_STATE_CLIENT_UNCONNECTED,
WSI_STATE_RETURNED_CLOSE_ALREADY,
- WSI_STATE_AWAITING_CLOSE_ACK
+ WSI_STATE_AWAITING_CLOSE_ACK,
};
enum lws_rx_parse_state {
LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY,
LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE,
LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY,
+ LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT,
+ LWS_CONNMODE_WS_CLIENT_PENDING_CANDIDATE_CHILD,
/* special internal types */
LWS_CONNMODE_SERVER_LISTENER,
PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
PENDING_TIMEOUT_AWAITING_PING,
PENDING_TIMEOUT_CLOSE_ACK,
+ PENDING_TIMEOUT_AWAITING_EXTENSION_CONNECT_RESPONSE,
};
enum lws_rx_parse_state lws_rx_parse_state;
char extension_data_pending;
+ struct libwebsocket *candidate_children_list;
+ struct libwebsocket *extension_handles;
/* 04 protocol specific */
char *c_origin;
char *c_protocol;
+ char *c_address;
+ int c_port;
+
+
#ifdef LWS_OPENSSL_SUPPORT
SSL *ssl;
BIO *client_bio;
lws_issue_raw(struct libwebsocket *wsi, unsigned char *buf, size_t len);
+extern void
+libwebsocket_service_timeout_check(struct libwebsocket_context *context,
+ struct libwebsocket *wsi, unsigned int sec);
+
+extern struct libwebsocket * __libwebsocket_client_connect_2(
+ struct libwebsocket_context *context,
+ struct libwebsocket *wsi);
+
+extern struct libwebsocket *
+libwebsocket_create_new_server_wsi(struct libwebsocket_context *context);
+
+extern char *
+libwebsockets_generate_client_handshake(struct libwebsocket_context *context,
+ struct libwebsocket *wsi, char *pkt);
+
+extern int
+lws_handle_POLLOUT_event(struct libwebsocket_context *context,
+ struct libwebsocket *wsi, struct pollfd *pollfd);
+
+extern int
+lws_any_extension_handled(struct libwebsocket_context *context,
+ struct libwebsocket *wsi,
+ enum libwebsocket_extension_callback_reasons r,
+ void *v, size_t len);
+
+extern void *
+lws_get_extension_user_matching_ext(struct libwebsocket *wsi,
+ struct libwebsocket_extension * ext);
+
+extern int
+lws_client_interpret_server_handshake(struct libwebsocket_context *context,
+ struct libwebsocket *wsi);
+
+extern int
+libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c);
+
#ifndef LWS_OPENSSL_SUPPORT
unsigned char *
dump_handshake_info(struct lws_tokens *lwst)
{
int n;
- static const char *token_names[] = {
+ static const char *token_names[WSI_TOKEN_COUNT] = {
/*[WSI_TOKEN_GET_URI] =*/ "GET URI",
/*[WSI_TOKEN_HOST] =*/ "Host",
/*[WSI_TOKEN_CONNECTION] =*/ "Connection",
/*[WSI_TOKEN_ACCEPT] =*/ "Accept",
/*[WSI_TOKEN_NONCE] =*/ "Nonce",
/*[WSI_TOKEN_HTTP] =*/ "Http",
+ /*[WSI_TOKEN_MUXURL] =*/ "MuxURL",
};
for (n = 0; n < WSI_TOKEN_COUNT; n++) {
switch (reason) {
case LWS_CALLBACK_ESTABLISHED:
+ fprintf(stderr, "callback_dumb_increment: LWS_CALLBACK_ESTABLISHED\n");
pss->number = 0;
break;
switch (reason) {
case LWS_CALLBACK_ESTABLISHED:
+ fprintf(stderr, "callback_lws_mirror: LWS_CALLBACK_ESTABLISHED\n");
pss->ringbuffer_tail = ringbuffer_head;
pss->wsi = wsi;
break;