diff --git a/lib/client-handshake.c b/lib/client-handshake.c
index c6f8ba3ef21cfb80b6a9c88a279f00f625cf9cb0..56c4e943d806fd7b5eb05eff51a867cdef702cfe 100644
--- a/lib/client-handshake.c
+++ b/lib/client-handshake.c
@@ -2,20 +2,6 @@
 #include <netdb.h>
 
 
-/*
- * In-place str to lower case
- */
-
-void
-strtolower(char *s)
-{
-	while (*s) {
-		*s = tolower(*s);
-		s++;
-	}
-}
-
-
 /**
  * libwebsocket_client_connect() - Connect to another websocket server
  * @this:	Websocket context
@@ -48,25 +34,12 @@ libwebsocket_client_connect(struct libwebsocket_context *this,
 {
 	struct hostent *server_hostent;
 	struct sockaddr_in server_addr;
-	char buf[150];
-	char key_b64[150];
-	char hash[20];
+	char pkt[512];
 	struct pollfd pfd;
-	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 pkt[1024];
-	char *p = &pkt[0];
-	const char *pc;
-	int len;
-	int okay = 0;
 	struct libwebsocket *wsi;
 	int n;
 	int plen = 0;
-#ifdef LWS_OPENSSL_SUPPORT
-	char ssl_err_buf[512];
-#else
+#ifndef LWS_OPENSSL_SUPPORT
 	if (ssl_connection) {
 		fprintf(stderr, "libwebsockets not configured for ssl\n");
 		return NULL;
@@ -74,10 +47,8 @@ libwebsocket_client_connect(struct libwebsocket_context *this,
 #endif
 
 	wsi = malloc(sizeof(struct libwebsocket));
-	if (wsi == NULL) {
-		fprintf(stderr, "Out of memory allocing new connection\n");
-		return NULL;
-	}
+	if (wsi == NULL)
+		goto bail1;
 
 	memset(wsi, 0, sizeof *wsi);
 
@@ -93,6 +64,32 @@ libwebsocket_client_connect(struct libwebsocket_context *this,
 	wsi->pings_vs_pongs = 0;
 	wsi->protocol = NULL;
 	wsi->pending_timeout = NO_PENDING_TIMEOUT;
+#ifdef LWS_OPENSSL_SUPPORT
+	wsi->use_ssl = ssl_connection;
+#endif
+
+	/* copy parameters over so state machine has access */
+
+	wsi->c_path = malloc(strlen(path) + 1);
+	if (wsi->c_path == NULL)
+		goto bail1;
+	strcpy(wsi->c_path, path);
+	wsi->c_host = malloc(strlen(host) + 1);
+	if (wsi->c_host == NULL)
+		goto oom1;
+	strcpy(wsi->c_host, host);
+	wsi->c_origin = malloc(strlen(origin) + 1);
+	if (wsi->c_origin == NULL)
+		goto oom2;
+	strcpy(wsi->c_origin, origin);
+	if (protocol) {
+		wsi->c_protocol = malloc(strlen(protocol) + 1);
+		if (wsi->c_protocol == NULL)
+			goto oom3;
+		strcpy(wsi->c_protocol, protocol);
+	} else
+		wsi->c_protocol = NULL;
+
 
 	/* set up appropriate masking */
 
@@ -109,7 +106,7 @@ libwebsocket_client_connect(struct libwebsocket_context *this,
 		fprintf(stderr,
 			"Client ietf version %d not supported\n",
 						       wsi->ietf_spec_revision);
-		return NULL;
+		goto oom4;
 	}
 
 	/* force no mask if he asks for that though */
@@ -145,18 +142,16 @@ libwebsocket_client_connect(struct libwebsocket_context *this,
 	server_hostent = gethostbyname(address);
 	if (server_hostent == NULL) {
 		fprintf(stderr, "Unable to get host name from %s\n", address);
-		goto bail1;
+		goto oom4;
 	}
 
 	wsi->sock = socket(AF_INET, SOCK_STREAM, 0);
 
 	if (wsi->sock < 0) {
 		fprintf(stderr, "Unable to open socket\n");
-		goto bail1;
+		goto oom4;
 	}
 
-	insert_wsi(this, wsi);
-
 	server_addr.sin_family = AF_INET;
 	server_addr.sin_port = htons(port);
 	server_addr.sin_addr = *((struct in_addr *)server_hostent->h_addr);
@@ -165,9 +160,24 @@ libwebsocket_client_connect(struct libwebsocket_context *this,
 	if (connect(wsi->sock, (struct sockaddr *)&server_addr,
 					      sizeof(struct sockaddr)) == -1)  {
 		fprintf(stderr, "Connect failed\n");
-		goto bail1;
+		goto oom4;
 	}
 
+	/* into fd -> wsi hashtable */
+
+	insert_wsi(this, wsi);
+
+	/* into internal poll list */
+
+	this->fds[this->fds_count].fd = wsi->sock;
+	this->fds[this->fds_count].revents = 0;
+	this->fds[this->fds_count++].events = POLLIN;
+
+	/* external POLL support via protocol 0 */
+	this->protocols[0].callback(this, wsi,
+		LWS_CALLBACK_ADD_POLL_FD,
+		(void *)(long)wsi->sock, NULL, POLLIN);
+
 	/* we are connected to server, or proxy */
 
 	if (this->http_proxy_port) {
@@ -179,349 +189,41 @@ libwebsocket_client_connect(struct libwebsocket_context *this,
 			goto bail1;
 		}
 
-		pfd.fd = wsi->sock;
-		pfd.events = POLLIN;
-		pfd.revents = 0;
+		libwebsocket_set_timeout(wsi,
+			PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE, 5);
 
-		n = poll(&pfd, 1, 5000);
-		if (n <= 0) {
-			close(wsi->sock);
-			fprintf(stderr, "libwebsocket_client_handshake "
-					"timeout on proxy response");
-			goto bail1;
-		}
-
-		n = recv(wsi->sock, pkt, sizeof pkt, 0);
-		if (n < 0) {
-			close(wsi->sock);
-			fprintf(stderr, "ERROR reading from proxy socket\n");
-			goto bail1;
-		}
-
-		pkt[13] = '\0';
-		if (strcmp(pkt, "HTTP/1.0 200 ") != 0) {
-			close(wsi->sock);
-			fprintf(stderr, "ERROR from proxy: %s\n", pkt);
-			goto bail1;
-		}
-
-		/* we can just start sending to proxy */
-	}
-
-#ifdef LWS_OPENSSL_SUPPORT
-	if (ssl_connection) {
-
-		wsi->ssl = SSL_new(this->ssl_client_ctx);
-		wsi->client_bio = BIO_new_socket(wsi->sock, BIO_NOCLOSE);
-		SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio);
-
-		if (SSL_connect(wsi->ssl) <= 0) {
-			fprintf(stderr, "SSL connect error %s\n",
-				ERR_error_string(ERR_get_error(), ssl_err_buf));
-			goto bail1;
-		}
+		wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY;
 
-		n = SSL_get_verify_result(wsi->ssl);
-		if (n != X509_V_OK) {
-			if (n != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
-							  ssl_connection != 2) {
-
-				fprintf(stderr, "server's cert didn't "
-							   "look good %d\n", n);
-				goto bail2;
-			}
-		}
-	} else {
-		wsi->ssl = NULL;
-#endif
-
-
-#ifdef LWS_OPENSSL_SUPPORT
+		return wsi;
 	}
-#endif
 
 	/*
-	 * create the random key
+	 * 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
 	 */
 
-	n = read(this->fd_random, hash, 16);
-	if (n != 16) {
-		fprintf(stderr, "Unable to read from random device %s\n",
-							SYSTEM_RANDOM_FILEPATH);
-		goto bail2;
-	}
-
-	lws_b64_encode_string(hash, 16, key_b64, sizeof key_b64);
-
-	/*
-	 * 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", path);
-	 p += sprintf(p, "Host: %s\x0d\x0a", host);
-	 p += sprintf(p, "Upgrade: websocket\x0d\x0a");
-	 p += sprintf(p, "Connection: Upgrade\x0d\x0aSec-WebSocket-Key: ");
-	 strcpy(p, key_b64);
-	 p += strlen(key_b64);
-	 p += sprintf(p, "\x0d\x0aSec-WebSocket-Origin: %s\x0d\x0a", origin);
-	 if (protocol != NULL)
-		p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a", protocol);
-	 p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a\x0d\x0a",
-						       wsi->ietf_spec_revision);
-
-
-	/* prepare the expected server accept response */
-
-	strcpy(buf, key_b64);
-	strcpy(&buf[strlen(buf)], magic_websocket_guid);
-
-	SHA1((unsigned char *)buf, strlen(buf), (unsigned char *)hash);
-
-	lws_b64_encode_string(hash, 20, wsi->initial_handshake_hash_base64,
-				  sizeof wsi->initial_handshake_hash_base64);
-
-	/* send our request to the server */
-
-#ifdef LWS_OPENSSL_SUPPORT
-	if (ssl_connection)
-		n = SSL_write(wsi->ssl, pkt, p - pkt);
-	else
-#endif
-		n = send(wsi->sock, pkt, p - pkt, 0);
-
-	if (n < 0) {
-		fprintf(stderr, "ERROR writing to client socket\n");
-		goto bail2;
-	}
-
-	wsi->parser_state = WSI_TOKEN_NAME_PART;
-
+	wsi->mode = LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE;
 	pfd.fd = wsi->sock;
-	pfd.events = POLLIN;
-	pfd.revents = 0;
-
-	n = poll(&pfd, 1, 5000);
-	if (n < 0) {
-		fprintf(stderr, "libwebsocket_client_handshake socket error "
-				"while waiting for handshake response");
-		goto bail2;
-	}
-	if (n == 0) {
-		fprintf(stderr, "libwebsocket_client_handshake timeout "
-				"while waiting for handshake response");
-		goto bail2;
-	}
-
-	/* interpret the server response */
-
-	/*
-	 *  HTTP/1.1 101 Switching Protocols
-	 *  Upgrade: websocket
-	 *  Connection: Upgrade
-	 *  Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
-	 *  Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
-	 *  Sec-WebSocket-Protocol: chat
-	 */
-
-#ifdef LWS_OPENSSL_SUPPORT
-	if (ssl_connection)
-		len = SSL_read(wsi->ssl, pkt, sizeof pkt);
-	else
-#endif
-		len = recv(wsi->sock, pkt, sizeof pkt, 0);
-
-	if (len < 0) {
-		fprintf(stderr, "libwebsocket_client_handshake read error\n");
-		goto bail2;
-	}
-
-	p = pkt;
-	for (n = 0; n < len; n++)
-		libwebsocket_parse(wsi, *p++);
-
-	if (wsi->parser_state != WSI_PARSING_COMPLETE) {
-		fprintf(stderr, "libwebsocket_client_handshake server response"
-				" failed parsing\n");
-		goto bail2;
-	}
-
-	/*
-	 * 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->utf8_token[WSI_TOKEN_PROTOCOL].token_len &&
-							    protocol != NULL)) {
-		fprintf(stderr, "libwebsocket_client_handshake "
-						"missing required header(s)\n");
-		pkt[len] = '\0';
-		fprintf(stderr, "%s", pkt);
-		goto bail2;
-	}
-
-	/*
-	 * 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 bail2;
-	}
-
-	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 bail2;
-	}
-
-	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 bail2;
-	}
-	/*
-	 * 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) {
+	pfd.revents = POLLIN;
+	libwebsocket_service_fd(this, &pfd);
 
-		/* no protocol name to work from, default to first protocol */
-		wsi->protocol = &this->protocols[0];
-
-		goto check_accept;
-	}
-
-	pc = protocol;
-	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++;
-	}
-	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 (this->protocols[n].callback) {
-		if (strcmp(wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
-				       this->protocols[n].name) == 0)
-			wsi->protocol = &this->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;
-	}
-
-check_accept:
-	/*
-	 * Confirm his accept token is the same as 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;
-	}
-
-	/*
-	 * Calculate the masking key to use when sending data to server
-	 */
-
-	strcpy(buf, key_b64);
-	p = buf + strlen(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((unsigned char *)buf, strlen(buf), wsi->masking_key_04);
-
-	/* 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;
-
-	/* okay he is good to go */
-
-	this->fds[this->fds_count].fd = wsi->sock;
-	this->fds[this->fds_count].revents = 0;
-	this->fds[this->fds_count++].events = POLLIN;
-
-	/* external POLL support via protocol 0 */
-	this->protocols[0].callback(this, wsi,
-		LWS_CALLBACK_ADD_POLL_FD,
-		(void *)(long)wsi->sock, NULL, POLLIN);
-
-
-	wsi->state = WSI_STATE_ESTABLISHED;
-	wsi->mode = LWS_CONNMODE_WS_CLIENT;
+	return wsi;
 
-	fprintf(stderr, "handshake OK for protocol %s\n", wsi->protocol->name);
+oom4:
+	if (wsi->c_protocol)
+		free(wsi->c_protocol);
 
-	/* call him back to inform him he is up */
+oom3:
+	free(wsi->c_origin);
 
-	wsi->protocol->callback(this, wsi,
-			 LWS_CALLBACK_CLIENT_ESTABLISHED,
-			 wsi->user_space,
-			 NULL, 0);
-	return wsi;
+oom2:
+	free(wsi->c_host);
 
+oom1:
+	free(wsi->c_path);
 
-bail2:
-	libwebsocket_close_and_free_session(this, wsi);
 bail1:
 	free(wsi);
 
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index a35193ded1c3ebabe6bc0f428f67e18e28d0ea6d..0e648179d98be36bcc1c99209fa52ae23ed8002f 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -21,6 +21,19 @@
 
 #include "private-libwebsockets.h"
 
+/*
+ * In-place str to lower case
+ */
+
+static void
+strtolower(char *s)
+{
+	while (*s) {
+		*s = tolower(*s);
+		s++;
+	}
+}
+
 /* file descriptor hash management */
 
 struct libwebsocket *
@@ -285,7 +298,18 @@ libwebsocket_service_fd(struct libwebsocket_context *this,
 	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;
+	int okay = 0;
+#ifdef LWS_OPENSSL_SUPPORT
+	char ssl_err_buf[512];
+#endif
 	/*
 	 * you can call us with pollfd = NULL to just allow the once-per-second
 	 * global timeout checks; if less than a second since the last check
@@ -601,6 +625,397 @@ libwebsocket_service_fd(struct libwebsocket_context *this,
 		}
 		break;
 
+	case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY:
+
+		/* handle proxy hung up on us */
+
+		if (pollfd->revents & (POLLERR | POLLHUP)) {
+
+			fprintf(stderr, "Proxy connection %p (fd=%d) dead\n",
+				(void *)wsi, pollfd->fd);
+
+			libwebsocket_close_and_free_session(this, wsi);
+			return 1;
+		}
+
+		n = recv(wsi->sock, pkt, sizeof pkt, 0);
+		if (n < 0) {
+			libwebsocket_close_and_free_session(this, wsi);
+			fprintf(stderr, "ERROR reading from proxy socket\n");
+			return 1;
+		}
+
+		pkt[13] = '\0';
+		if (strcmp(pkt, "HTTP/1.0 200 ") != 0) {
+			libwebsocket_close_and_free_session(this, wsi);
+			fprintf(stderr, "ERROR from proxy: %s\n", pkt);
+			return 1;
+		}
+
+		/* 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(this->ssl_client_ctx);
+			wsi->client_bio = BIO_new_socket(wsi->sock, BIO_NOCLOSE);
+			SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio);
+
+			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(this, wsi);
+				return 1;
+			}
+
+			n = SSL_get_verify_result(wsi->ssl);
+			if (n != X509_V_OK) {
+				if (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(this, wsi);
+					return 1;
+				}
+			}
+		} else {
+			wsi->ssl = NULL;
+	#endif
+
+
+	#ifdef LWS_OPENSSL_SUPPORT
+		}
+	#endif
+
+		/*
+		 * create the random key
+		 */
+
+		n = read(this->fd_random, 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);
+			free(wsi->c_origin);
+			if (wsi->c_protocol)
+				free(wsi->c_protocol);
+			libwebsocket_close_and_free_session(this, wsi);
+			return 1;
+		}
+
+		lws_b64_encode_string(hash, 16, wsi->key_b64,
+							   sizeof wsi->key_b64);
+
+		/*
+		 * 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);
+		 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\x0aSec-WebSocket-Origin: %s\x0d\x0a",
+								 wsi->c_origin);
+		 if (wsi->c_protocol != NULL)
+			p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a",
+							       wsi->c_protocol);
+		 p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a\x0d\x0a",
+						       wsi->ietf_spec_revision);
+
+		/* done with these now */
+
+		free(wsi->c_path);
+		free(wsi->c_host);
+		free(wsi->c_origin);
+
+		/* 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);
+
+		/* send our request to the server */
+
+	#ifdef LWS_OPENSSL_SUPPORT
+		if (wsi->use_ssl)
+			n = SSL_write(wsi->ssl, pkt, p - pkt);
+		else
+	#endif
+			n = send(wsi->sock, pkt, p - pkt, 0);
+
+		if (n < 0) {
+			fprintf(stderr, "ERROR writing to client socket\n");
+			libwebsocket_close_and_free_session(this, wsi);
+			return 1;
+		}
+
+		wsi->parser_state = WSI_TOKEN_NAME_PART;
+		wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY;
+		libwebsocket_set_timeout(wsi,
+				PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, 5);
+
+		break;
+
+	case LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY:
+
+		/* handle server hung up on us */
+
+		if (pollfd->revents & (POLLERR | POLLHUP)) {
+
+			fprintf(stderr, "Server connection %p (fd=%d) dead\n",
+				(void *)wsi, pollfd->fd);
+
+			goto bail3;
+		}
+
+
+		/* interpret the server response */
+
+		/*
+		 *  HTTP/1.1 101 Switching Protocols
+		 *  Upgrade: websocket
+		 *  Connection: Upgrade
+		 *  Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
+		 *  Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
+		 *  Sec-WebSocket-Protocol: chat
+		 */
+
+	#ifdef LWS_OPENSSL_SUPPORT
+		if (wsi->use_ssl)
+			len = SSL_read(wsi->ssl, pkt, sizeof pkt);
+		else
+	#endif
+			len = recv(wsi->sock, pkt, sizeof pkt, 0);
+
+		if (len < 0) {
+			fprintf(stderr,
+				  "libwebsocket_client_handshake read error\n");
+			goto bail3;
+		}
+
+		p = pkt;
+		for (n = 0; n < len; n++)
+			libwebsocket_parse(wsi, *p++);
+
+		if (wsi->parser_state != WSI_PARSING_COMPLETE) {
+			fprintf(stderr, "libwebsocket_client_handshake "
+					"server response ailed parsing\n");
+			goto bail3;
+		}
+
+		/*
+		 * 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->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;
+		}
+
+
+		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 = &this->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 (this->protocols[n].callback) {
+			if (strcmp(wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
+					       this->protocols[n].name) == 0)
+				wsi->protocol = &this->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;
+		}
+
+	check_accept:
+		/*
+		 * 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;
+		}
+
+		/*
+		 * Calculate the 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);
+
+		/* 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(this, wsi,
+				 LWS_CALLBACK_CLIENT_ESTABLISHED,
+				 wsi->user_space,
+				 NULL, 0);
+
+		break;
+
+bail3:
+		if (wsi->c_protocol)
+			free(wsi->c_protocol);
+
+bail2:
+		libwebsocket_close_and_free_session(this, wsi);
+		return 1;
+		
+
 	case LWS_CONNMODE_WS_SERVING:
 	case LWS_CONNMODE_WS_CLIENT:
 
@@ -829,6 +1244,28 @@ libwebsocket_callback_on_writable_all_protocol(
 	return 0;
 }
 
+/**
+ * libwebsocket_set_timeout() - marks the wsi as subject to a timeout
+ *
+ * You will not need this unless you are doing something special
+ *
+ * @wsi:	Websocket connection instance
+ * @reason:	timeout reason
+ * @secs:	how many seconds
+ */
+
+void
+libwebsocket_set_timeout(struct libwebsocket *wsi,
+					  enum pending_timeout reason, int secs)
+{
+	struct timeval tv;
+
+	gettimeofday(&tv, NULL);
+
+	wsi->pending_timeout_limit = tv.tv_sec + secs;
+	wsi->pending_timeout = reason;
+}
+
 
 /**
  * libwebsocket_get_socket_fd() - returns the socket file descriptor
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index d58854137aa6505792cc9e20ff3af364c17f3c89..5fbbcef4f63e52eb545e43d535ceece5c1f73c34 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -106,7 +106,7 @@ enum lws_connection_states {
 	WSI_STATE_DEAD_SOCKET,
 	WSI_STATE_ESTABLISHED,
 	WSI_STATE_CLIENT_UNCONNECTED,
-	WSI_STATE_RETURNED_CLOSE_ALREADY
+	WSI_STATE_RETURNED_CLOSE_ALREADY,
 };
 
 enum lws_rx_parse_state {
@@ -141,6 +141,11 @@ enum connection_mode {
 	LWS_CONNMODE_WS_SERVING,
 	LWS_CONNMODE_WS_CLIENT,
 
+	/* transient modes */
+	LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY,
+	LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE,
+	LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY,
+
 	/* special internal types */
 	LWS_CONNMODE_SERVER_LISTENER,
 	LWS_CONNMODE_BROADCAST_PROXY_LISTENER,
@@ -182,7 +187,9 @@ struct libwebsocket_context {
 
 enum pending_timeout {
 	NO_PENDING_TIMEOUT = 0,
+	PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE,
 	PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
+	PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
 	PENDING_TIMEOUT_AWAITING_PING,
 };
 
@@ -217,6 +224,7 @@ struct libwebsocket {
 
 	/* 04 protocol specific */
 
+	char key_b64[150];
 	unsigned char masking_key_04[20];
 	unsigned char frame_masking_nonce_04[4];
 	unsigned char frame_mask_04[20];
@@ -232,10 +240,15 @@ struct libwebsocket {
 	/* client support */
 	char initial_handshake_hash_base64[30];
 	enum connection_mode mode;
+	char *c_path;
+	char *c_host;
+	char *c_origin;
+	char *c_protocol;
 
 #ifdef LWS_OPENSSL_SUPPORT
 	SSL *ssl;
 	BIO *client_bio;
+	int use_ssl;
 #endif
 
 	void *user_space;
@@ -282,6 +295,10 @@ insert_wsi(struct libwebsocket_context *this, struct libwebsocket *wsi);
 extern int
 delete_from_fd(struct libwebsocket_context *this, int fd);
 
+extern void
+libwebsocket_set_timeout(struct libwebsocket *wsi,
+					 enum pending_timeout reason, int secs);
+
 #ifndef LWS_OPENSSL_SUPPORT
 
 unsigned char *
diff --git a/libwebsockets-api-doc.html b/libwebsockets-api-doc.html
index feb6638e340a6339d8ae2d89ae00d07669f99502..e351ab87081d9e99d2f8e9869f29494b7715391a 100644
--- a/libwebsockets-api-doc.html
+++ b/libwebsockets-api-doc.html
@@ -141,6 +141,27 @@ nothing is pending, or as soon as it services whatever was pending.
 <dd>Protocol whose connections will get callbacks
 </dl>
 <hr>
+<h2>libwebsocket_set_timeout - marks the wsi as subject to a timeout</h2>
+<i>void</i>
+<b>libwebsocket_set_timeout</b>
+(<i>struct libwebsocket *</i> <b>wsi</b>,
+<i>enum pending_timeout</i> <b>reason</b>,
+<i>int</i> <b>secs</b>)
+<h3>Arguments</h3>
+<dl>
+<dt><b>wsi</b>
+<dd>Websocket connection instance
+<dt><b>reason</b>
+<dd>timeout reason
+<dt><b>secs</b>
+<dd>how many seconds
+</dl>
+<h3>Description</h3>
+<blockquote>
+<p>
+You will not need this unless you are doing something special
+</blockquote>
+<hr>
 <h2>libwebsocket_get_socket_fd - returns the socket file descriptor</h2>
 <i>int</i>
 <b>libwebsocket_get_socket_fd</b>