diff --git a/README-test-server b/README-test-server
index aeb5d217913906f0470f0c9705b56ebcd2db49c0..9c8475f06222978fc52e6a23038f066eca5ca866 100644
--- a/README-test-server
+++ b/README-test-server
@@ -48,6 +48,9 @@ There are a couple of other possible configure options
 			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
 -----------------------------
@@ -246,5 +249,39 @@ LWS_CALLBACK_SET_MODE_POLL_FD and LWS_CALLBACK_CLEAR_MODE_POLL_FD
 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>
 
diff --git a/configure b/configure
index 5470e9b58a46e1e42c1c3f5779bfea42c43e2eaf..063a6686e429689b0509eea550743bf1e9c9f741 100755
--- a/configure
+++ b/configure
@@ -619,6 +619,8 @@ LIBOBJS
 NOPING_FALSE
 NOPING_TRUE
 clientcertdir
+EXT_GOOGLE_MUX_FALSE
+EXT_GOOGLE_MUX_TRUE
 LIBCRYPTO_FALSE
 LIBCRYPTO_TRUE
 CPP
@@ -742,6 +744,7 @@ enable_libtool_lock
 enable_openssl
 enable_nofork
 enable_libcrypto
+enable_x_google_mux
 with_client_cert_dir
 enable_noping
 '
@@ -753,12 +756,7 @@ CFLAGS
 LDFLAGS
 LIBS
 CPPFLAGS
-CPP
-CPPFLAGS
-CC
-LDFLAGS
-LIBS
-CPPFLAGS'
+CPP'
 
 
 # Initialize some variables set by options.
@@ -1389,6 +1387,7 @@ Optional Features:
   --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:
@@ -12294,6 +12293,28 @@ fi
 
 
 
+#
+#
+#
+# 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
+
+
+
 #
 #
 #
@@ -12645,6 +12666,10 @@ if test -z "${LIBCRYPTO_TRUE}" && test -z "${LIBCRYPTO_FALSE}"; 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
diff --git a/configure.ac b/configure.ac
index 057f42c43a0be10837d021d1fa230f4f231db52b..e3124857764387ab5bb0ae00a68f854ab49a0c5e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -60,6 +60,19 @@ fi
 AM_CONDITIONAL(LIBCRYPTO, test x$libcrypto = xyes)
 
 
+#
+#
+#
+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)
+
+
 #
 #
 #
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 9cd8a08fbbe326237da25ea46914bc2d01da41f2..e8d064c0aeaa10959f79d31177d542db4ff013c3 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -9,6 +9,11 @@ dist_libwebsockets_la_SOURCES=libwebsockets.c \
 				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
diff --git a/lib/Makefile.in b/lib/Makefile.in
index af9be59bc3024fbf2a85e26ef8cd51a9b384a47a..b98c997b3d0bd3db31ead5c09dbd904a1eabf486 100644
--- a/lib/Makefile.in
+++ b/lib/Makefile.in
@@ -35,7 +35,8 @@ PRE_UNINSTALL = :
 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
@@ -74,15 +75,17 @@ libwebsockets_la_LIBADD =
 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) \
@@ -225,7 +228,7 @@ include_HEADERS = libwebsockets.h
 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@\"
 
@@ -307,6 +310,7 @@ distclean-compile:
 @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@
@@ -384,6 +388,13 @@ libwebsockets_la-extension-deflate-stream.lo: extension-deflate-stream.c
 @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
diff --git a/lib/client-handshake.c b/lib/client-handshake.c
index adcee07a6868ff6f52e3907e1b1de5802573c7db..d41777ce15940a5d831ea23f378f2d36be58f806 100644
--- a/lib/client-handshake.c
+++ b/lib/client-handshake.c
@@ -2,6 +2,147 @@
 #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
@@ -32,14 +173,11 @@ libwebsocket_client_connect(struct libwebsocket_context *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");
@@ -70,6 +208,9 @@ libwebsocket_client_connect(struct libwebsocket_context *context,
 	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);
@@ -129,103 +270,42 @@ libwebsocket_client_connect(struct libwebsocket_context *context,
 	}
 
 	/*
-	 * 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)
diff --git a/lib/extension-deflate-stream.c b/lib/extension-deflate-stream.c
index 3505e9509e8b0f6517a1f1985eaecd2f116051ca..3c20fe2a9a0519b96053c74a2beb59a6c4a053b9 100644
--- a/lib/extension-deflate-stream.c
+++ b/lib/extension-deflate-stream.c
@@ -140,7 +140,10 @@ int lws_extension_callback_deflate_stream(
 
 		/* we don't need calling again until new input data comes */
 
-		return 0;	
+		return 0;
+
+	default:
+		break;
 	}
 
 	return 0;
diff --git a/lib/extension-x-google-mux.c b/lib/extension-x-google-mux.c
new file mode 100644
index 0000000000000000000000000000000000000000..1f56994dc68b2765d9fa87317e61e52dca93bd33
--- /dev/null
+++ b/lib/extension-x-google-mux.c
@@ -0,0 +1,929 @@
+#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;
+}
diff --git a/lib/extension-x-google-mux.h b/lib/extension-x-google-mux.h
new file mode 100644
index 0000000000000000000000000000000000000000..a2cbb211785760f8ff45eb5f9f62c2cacfc39c30
--- /dev/null
+++ b/lib/extension-x-google-mux.h
@@ -0,0 +1,94 @@
+
+#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);
diff --git a/lib/extension.c b/lib/extension.c
index df62bd5cb56f35c22e09a593884a9b8b4f49f728..890930872a055e797fb44d6be1f28ed0ff3cd685 100644
--- a/lib/extension.c
+++ b/lib/extension.c
@@ -1,8 +1,16 @@
 #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,
diff --git a/lib/handshake.c b/lib/handshake.c
index 46197ff06fea90ce0bdddcff4e1d15bc579a2c87..14adbba4d38c5f275fd1a578278cb114f6bfb14d 100644
--- a/lib/handshake.c
+++ b/lib/handshake.c
@@ -72,7 +72,7 @@ interpret_key(const char *key, unsigned long *result)
 
 
 static int
-handshake_00(struct libwebsocket *wsi)
+handshake_00(struct libwebsocket_context *context, struct libwebsocket *wsi)
 {
 	unsigned long key1, key2;
 	unsigned char sum[16];
@@ -223,7 +223,7 @@ bail:
  */
 
 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";
@@ -367,10 +367,12 @@ handshake_0405(struct libwebsocket *wsi)
 		 */
 
 		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++;
@@ -379,6 +381,11 @@ handshake_0405(struct libwebsocket *wsi)
 			ext_name[n] = '\0';
 			if (!*c)
 				more = 0;
+			else {
+				c++;
+				if (!n)
+					continue;
+			}
 
 			/* check a client's extension against our support */
 
@@ -429,6 +436,10 @@ handshake_0405(struct libwebsocket *wsi)
 				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;
 
@@ -441,6 +452,7 @@ handshake_0405(struct libwebsocket *wsi)
 					wsi->count_active_extensions], NULL, 0);
 
 				wsi->count_active_extensions++;
+				fprintf(stderr, "wsi->count_active_extensions <- %d", wsi->count_active_extensions);
 
 				ext++;
 			}
@@ -479,17 +491,23 @@ handshake_0405(struct libwebsocket *wsi)
 							   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 */
@@ -565,6 +583,10 @@ libwebsocket_read(struct libwebsocket_context *context, struct libwebsocket *wsi
 #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++);
@@ -582,7 +604,9 @@ libwebsocket_read(struct libwebsocket_context *context, struct libwebsocket *wsi
 		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? */
 
@@ -596,6 +620,10 @@ libwebsocket_read(struct libwebsocket_context *context, struct libwebsocket *wsi
 			return 0;
 		}
 
+		if (!wsi->protocol) {
+			fprintf(stderr, "NULL protocol coming on libwebsocket_read\n");
+		}
+
 		/*
 		 * It's websocket
 		 *
@@ -659,13 +687,13 @@ libwebsocket_read(struct libwebsocket_context *context, struct libwebsocket *wsi
 		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:
@@ -673,7 +701,7 @@ libwebsocket_read(struct libwebsocket_context *context, struct libwebsocket *wsi
 		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;
 
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index 95d124976efdba0850f4401bcde8c369b19474f5..82696a5e9e78049c390e59301a2589b95df1a00d 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -153,6 +153,7 @@ libwebsocket_close_and_free_session(struct libwebsocket_context *context,
 	int ret;
 	int m;
 	struct lws_tokens eff_buf;
+	struct libwebsocket_extension *ext;
 
 	if (!wsi)
 		return;
@@ -250,7 +251,8 @@ just_kill_connection:
 	 * 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 */
 
@@ -267,8 +269,8 @@ just_kill_connection:
 	}
 
 	/* 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;
@@ -294,12 +296,28 @@ just_kill_connection:
 		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
@@ -508,7 +526,7 @@ int lws_send_pipe_choked(struct libwebsocket *wsi)
 	return 0;
 }
 
-static int
+int
 lws_handle_POLLOUT_event(struct libwebsocket_context *context,
 				struct libwebsocket *wsi, struct pollfd *pollfd)
 {
@@ -516,8 +534,24 @@ lws_handle_POLLOUT_event(struct libwebsocket_context *context,
 	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;
 
 	/*
@@ -597,12 +631,16 @@ lws_handle_POLLOUT_event(struct libwebsocket_context *context,
 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;
@@ -616,6 +654,721 @@ user_service:
 
 
 
+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
@@ -642,21 +1395,10 @@ libwebsocket_service_fd(struct libwebsocket_context *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
@@ -677,19 +1419,9 @@ libwebsocket_service_fd(struct libwebsocket_context *context,
 
 		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);
 		}
 	}
 
@@ -758,16 +1490,12 @@ libwebsocket_service_fd(struct libwebsocket_context *context,
 
 		/* 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;
@@ -813,34 +1541,6 @@ libwebsocket_service_fd(struct libwebsocket_context *context,
 			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);
 
 		/*
@@ -1026,279 +1726,56 @@ libwebsocket_service_fd(struct libwebsocket_context *context,
 
 		/* 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 */
 
@@ -1373,334 +1850,23 @@ issue_hdr:
 		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:
@@ -1822,6 +1988,7 @@ libwebsocket_context_destroy(struct libwebsocket_context *context)
 	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++) {
@@ -1830,6 +1997,20 @@ libwebsocket_context_destroy(struct libwebsocket_context *context)
 						    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);
@@ -1917,6 +2098,47 @@ libwebsocket_service(struct libwebsocket_context *context, int timeout_ms)
 	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
@@ -1931,13 +2153,32 @@ libwebsocket_callback_on_writable(struct libwebsocket_context *context,
 						       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,
@@ -2053,9 +2294,10 @@ libwebsocket_rx_flow_control(struct libwebsocket *wsi, int enable)
 			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;
 }
 
@@ -2172,6 +2414,7 @@ libwebsocket_create_context(int port, const char *interf,
 			       int gid, int uid, unsigned int options)
 {
 	int n;
+	int m;
 	int sockfd = 0;
 	int fd;
 	struct sockaddr_in serv_addr, cli_addr;
@@ -2570,6 +2813,20 @@ libwebsocket_create_context(int port, const char *interf,
 			(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;
 }
 
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index 95a7ae0e06d34b6b16ee8bb5475c862c3a39719f..e92f8929f4e773594d43cd64201b6d6dea2bb60d 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -40,7 +40,7 @@ extern "C" {
 #endif
 
 #define CONTEXT_PORT_NO_LISTEN 0
-
+#define MAX_MUX_RECURSION 2
 
 enum libwebsocket_context_options {
 	LWS_SERVER_OPTION_DEFEAT_CLIENT_MASK = 1,
@@ -74,12 +74,25 @@ enum libwebsocket_callback_reasons {
 };
 
 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 {
@@ -141,6 +154,7 @@ enum lws_token_indexes {
 	WSI_TOKEN_ACCEPT,
 	WSI_TOKEN_NONCE,
 	WSI_TOKEN_HTTP,
+	WSI_TOKEN_MUXURL,
 
 	/* always last real token index*/
 	WSI_TOKEN_COUNT,
@@ -148,7 +162,8 @@ enum lws_token_indexes {
 	WSI_TOKEN_NAME_PART,
 	WSI_TOKEN_SKIPPING,
 	WSI_TOKEN_SKIPPING_SAW_CR,
-	WSI_PARSING_COMPLETE
+	WSI_PARSING_COMPLETE,
+	WSI_INIT_TOKEN_MUXURL,
 };
 
 /*
@@ -523,6 +538,7 @@ struct libwebsocket_extension {
 			enum libwebsocket_extension_callback_reasons reason,
 					      void *user, void *in, size_t len);
 	size_t per_session_data_size;
+	void * per_context_private_data;
 };
 
 
@@ -574,11 +590,12 @@ libwebsocket_service_fd(struct libwebsocket_context *context,
 
 /*
  * 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
diff --git a/lib/parsers.c b/lib/parsers.c
index 165f758c38ef5fdb2cd09db84dd593a223e32274..f67af4f3ce3a868e6f0e1b7e783bf14dc83c6cc0 100644
--- a/lib/parsers.c
+++ b/lib/parsers.c
@@ -45,6 +45,7 @@ const struct lws_tokens lws_tokens[WSI_TOKEN_COUNT] = {
 /*	[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 },
 
 };
 
@@ -70,6 +71,8 @@ int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c)
 	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 */
@@ -107,6 +110,7 @@ int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c)
 			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;
 		}
 
@@ -151,6 +155,15 @@ int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c)
 		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);
@@ -269,10 +282,16 @@ xor_mask_05(struct libwebsocket *wsi, unsigned char 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:
@@ -693,6 +712,8 @@ spill:
 		 * 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? */
@@ -729,8 +750,45 @@ spill:
 			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;
 		}
 
 		/*
@@ -748,6 +806,9 @@ spill:
 						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;
 	}
@@ -768,6 +829,11 @@ int libwebsocket_client_rx_sm(struct libwebsocket *wsi, unsigned char c)
 	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:
@@ -1083,6 +1149,9 @@ issue:
 		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
@@ -1124,7 +1193,42 @@ spill:
 			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;
 		}
 
@@ -1134,7 +1238,7 @@ spill:
 		 * 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,
@@ -1223,9 +1327,75 @@ libwebsocket_0405_frame_mask_generate(struct libwebsocket *wsi)
 	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) {
@@ -1284,7 +1454,7 @@ int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf,
 	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;
 
@@ -1490,7 +1660,7 @@ int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf,
 		 * 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: "
@@ -1536,10 +1706,12 @@ int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf,
 				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;
+				}
 			}
 		}
 
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index 1593a76b2f15ac927faeaf1e611b70278f768984..bf2865da10572aa8e011b5733cab7e78e33a1124 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -126,17 +126,34 @@ enum lws_websocket_opcodes_04 {
 	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,
@@ -144,7 +161,7 @@ enum lws_connection_states {
 	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 {
@@ -188,6 +205,8 @@ enum connection_mode {
 	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,
@@ -236,6 +255,7 @@ enum pending_timeout {
 	PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
 	PENDING_TIMEOUT_AWAITING_PING,
 	PENDING_TIMEOUT_CLOSE_ACK,
+	PENDING_TIMEOUT_AWAITING_EXTENSION_CONNECT_RESPONSE,
 };
 
 
@@ -272,6 +292,8 @@ struct libwebsocket {
 
 	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 */
 
@@ -301,6 +323,10 @@ struct libwebsocket {
 	char *c_origin;
 	char *c_protocol;
 
+	char *c_address;
+	int c_port;
+
+
 #ifdef LWS_OPENSSL_SUPPORT
 	SSL *ssl;
 	BIO *client_bio;
@@ -353,6 +379,42 @@ extern int
 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 *
diff --git a/test-server/test-server.c b/test-server/test-server.c
index 2cb063c364a4a83a274a93966be4b69f3bc86dc4..c1cfab8101a1b14f4597fdab55b042714a65c306 100644
--- a/test-server/test-server.c
+++ b/test-server/test-server.c
@@ -124,7 +124,7 @@ static void
 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",
@@ -148,6 +148,7 @@ dump_handshake_info(struct lws_tokens *lwst)
 		/*[WSI_TOKEN_ACCEPT]		=*/ "Accept",
 		/*[WSI_TOKEN_NONCE]		=*/ "Nonce",
 		/*[WSI_TOKEN_HTTP]		=*/ "Http",
+		/*[WSI_TOKEN_MUXURL]	=*/ "MuxURL",
 	};
 	
 	for (n = 0; n < WSI_TOKEN_COUNT; n++) {
@@ -187,6 +188,7 @@ callback_dumb_increment(struct libwebsocket_context * context,
 	switch (reason) {
 
 	case LWS_CALLBACK_ESTABLISHED:
+		fprintf(stderr, "callback_dumb_increment: LWS_CALLBACK_ESTABLISHED\n");
 		pss->number = 0;
 		break;
 
@@ -266,6 +268,7 @@ callback_lws_mirror(struct libwebsocket_context * context,
 	switch (reason) {
 
 	case LWS_CALLBACK_ESTABLISHED:
+		fprintf(stderr, "callback_lws_mirror: LWS_CALLBACK_ESTABLISHED\n");
 		pss->ringbuffer_tail = ringbuffer_head;
 		pss->wsi = wsi;
 		break;