diff --git a/README.coding b/README.coding
index d26c21dbb3d5992f08be0be85a4c9d3d5442c0d2..a34211ec40ec2aeca1aa400aa236891a1e59e358 100644
--- a/README.coding
+++ b/README.coding
@@ -231,3 +231,23 @@ is too computationally expensive.  To use it, point it to a string like
 
 if left NULL, then the "DEFAULT" set of ciphers are all possible to select.
 
+
+Async nature of client connections
+----------------------------------
+
+When you call libwebsocket_client_connect(..) and get a wsi back, it does not
+mean your connection is active.  It just mean it started trying to connect.
+
+Your client connection is actually active only when you receive
+LWS_CALLBACK_CLIENT_ESTABLISHED for it.
+
+There's a 5 second timeout for the connection, and it may give up or die for
+other reasons, if any of that happens you'll get a
+LWS_CALLBACK_CLIENT_CONNECTION_ERROR callback on protocol 0 instead for the
+wsi.
+
+After attempting the connection and getting back a non-NULL wsi you should
+loop calling libwebsocket_service() until one of the above callbacks occurs.
+
+As usual, see test-client.c for example code.
+
diff --git a/changelog b/changelog
index 21ecb0560eb9c4ffec7948bcdea9839fde51392e..31cc570c7ccdf5e1cf0c92192d32a70388688fe8 100644
--- a/changelog
+++ b/changelog
@@ -37,6 +37,10 @@ User api additions
 	move the protocol name to the "in" parameter.  The docs for this
 	callback are also updated to reflect how to check headers in there.
 
+ - libwebsocket_client_connect() is now properly nonblocking and async.  See
+	README.coding and test-client.c for information on the callbacks you
+	can rely on controlling the async connection period with.
+
 
 User api changes
 ----------------
diff --git a/lib/client-handshake.c b/lib/client-handshake.c
index 2dd810c561f20dc4986c017e04c8139633085bab..a501565690f0aa850d45f77a05e90dfaa983121c 100644
--- a/lib/client-handshake.c
+++ b/lib/client-handshake.c
@@ -52,11 +52,28 @@ struct libwebsocket *__libwebsocket_client_connect_2(
 		goto oom4;
 	}
 
-	wsi->sock = socket(AF_INET, SOCK_STREAM, 0);
-
 	if (wsi->sock < 0) {
-		lwsl_warn("Unable to open socket\n");
-		goto oom4;
+
+		wsi->sock = socket(AF_INET, SOCK_STREAM, 0);
+
+		if (wsi->sock < 0) {
+			lwsl_warn("Unable to open socket\n");
+			goto oom4;
+		}
+
+		if (lws_set_socket_options(context, wsi->sock)) {
+			lwsl_err("Failed to set wsi socket options\n");
+			compatible_close(wsi->sock);
+			goto oom4;
+		}
+
+		wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT;
+
+		insert_wsi_socket_into_fds(context, wsi);
+
+		libwebsocket_set_timeout(wsi,
+			PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE,
+							      AWAITING_TIMEOUT);
 	}
 
 	server_addr.sin_family = AF_INET;
@@ -66,20 +83,30 @@ struct libwebsocket *__libwebsocket_client_connect_2(
 
 	if (connect(wsi->sock, (struct sockaddr *)&server_addr,
 					     sizeof(struct sockaddr)) == -1)  {
-		lwsl_debug("Connect failed\n");
-		compatible_close(wsi->sock);
-		goto oom4;
-	}
 
-	lwsl_client("connected\n");
+		if (errno == EALREADY || errno == EINPROGRESS) {
+			lwsl_client("nonblocking connect retry\n");
 
-	if (lws_set_socket_options(context, wsi->sock)) {
-		lwsl_err("Failed to set wsi socket options\n");
-		compatible_close(wsi->sock);
-		goto oom4;
+			/*
+			 * must do specifically a POLLOUT poll to hear
+			 * about the connect completion
+			 */
+
+			context->fds[wsi->position_in_fds_table].events |= POLLOUT;
+
+			/* external POLL support via protocol 0 */
+			context->protocols[0].callback(context, wsi,
+				LWS_CALLBACK_SET_MODE_POLL_FD,
+				wsi->user_space, (void *)(long)wsi->sock, POLLOUT);
+
+			return wsi;
+		}
+
+		lwsl_debug("Connect failed errno=%d\n", errno);
+		goto failed;
 	}
 
-	insert_wsi_socket_into_fds(context, wsi);
+	lwsl_client("connected\n");
 
 	/* we are connected to server, or proxy */
 
@@ -87,9 +114,8 @@ struct libwebsocket *__libwebsocket_client_connect_2(
 
 		n = send(wsi->sock, context->service_buffer, plen, MSG_NOSIGNAL);
 		if (n < 0) {
-			compatible_close(wsi->sock);
 			lwsl_debug("ERROR writing to proxy socket\n");
-			goto oom4;
+			goto failed;
 		}
 
 		libwebsocket_set_timeout(wsi,
@@ -121,7 +147,7 @@ struct libwebsocket *__libwebsocket_client_connect_2(
 	n = libwebsocket_service_fd(context, &pfd);
 
 	if (n < 0)
-		goto oom4;
+		goto failed;
 
 	if (n) /* returns 1 on failure after closing wsi */
 		return NULL;
@@ -131,7 +157,11 @@ struct libwebsocket *__libwebsocket_client_connect_2(
 oom4:
 	free(wsi->u.hdr.ah);
 	free(wsi);
+	return NULL;
 
+failed:
+	libwebsocket_close_and_free_session(context, wsi,
+						     LWS_CLOSE_STATUS_NOSTATUS);
 	return NULL;
 }
 
@@ -185,6 +215,7 @@ libwebsocket_client_connect(struct libwebsocket_context *context,
 		goto bail;
 
 	memset(wsi, 0, sizeof(*wsi));
+	wsi->sock = -1;
 
 	/* -1 means just use latest supported */
 
diff --git a/lib/client.c b/lib/client.c
index 96ae2bd3cf838c644dd9e8b0859e754551682d96..4f806275e4afa40980f160616a66dfc3b4158582 100644
--- a/lib/client.c
+++ b/lib/client.c
@@ -45,6 +45,22 @@ int lws_client_socket_service(struct libwebsocket_context *context,
 
 	switch (wsi->mode) {
 
+	case LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT:
+
+		/*
+		 * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
+		 * timeout protection set in client-handshake.c
+		 */
+
+		if (__libwebsocket_client_connect_2(context, wsi) == NULL) {
+			/* closed */
+			lwsl_client("closed\n");
+			return -1;
+		}
+
+		/* either still pending connection, or changed mode */
+		return 0;
+
 	case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY:
 
 		/* handle proxy hung up on us */
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index e2254e05f2e37f847690f103490517f0e2c9af9f..ea8a8ff3b769972f7ba79f1f24fe22d0896ff756 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -206,6 +206,17 @@ libwebsocket_close_and_free_session(struct libwebsocket_context *context,
 
 	wsi->u.ws.close_reason = reason;
 
+	if (wsi->mode == LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT ||
+			wsi->mode == LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE) {
+
+		context->protocols[0].callback(context, wsi,
+			LWS_CALLBACK_CLIENT_CONNECTION_ERROR, NULL, NULL, 0);
+
+		free(wsi->u.hdr.ah);
+		goto just_kill_connection;
+	}
+
+
 	if (wsi->mode == LWS_CONNMODE_HTTP_SERVING_ACCEPTED && wsi->u.http.fd) {
 		lwsl_debug("closing http fd %d\n", wsi->u.http.fd);
 		close(wsi->u.http.fd);
@@ -904,12 +915,10 @@ libwebsocket_service_fd(struct libwebsocket_context *context,
 		return 0;
 
 	/* just here for timeout management? */
-
 	if (pollfd == NULL)
 		return 0;
 
 	/* no, here to service a socket descriptor */
-
 	wsi = context->lws_lookup[pollfd->fd];
 	if (wsi == NULL)
 		/* not lws connection ... leave revents alone and return */
@@ -1286,8 +1295,10 @@ libwebsocket_service(struct libwebsocket_context *context, int timeout_ms)
 	/* wait for something to need service */
 
 	n = poll(context->fds, context->fds_count, timeout_ms);
-	if (n == 0) /* poll timeout */
+	if (n == 0) /* poll timeout */ {
+		libwebsocket_service_fd(context, NULL);
 		return 0;
+	}
 
 	if (n < 0)
 		return -1;
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index b3cfa154f988d4fde1371a3440a92a2e4379706f..2d62107f60fbfc89025321ba69813633f3933f5c 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -838,6 +838,7 @@ libwebsocket_context_user(struct libwebsocket_context *context);
 enum pending_timeout {
 	NO_PENDING_TIMEOUT = 0,
 	PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE,
+	PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE,
 	PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
 	PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
 	PENDING_TIMEOUT_AWAITING_PING,
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index 11f9204316213ba87eb28468eb2e8f76d8e00cfe..7c39183a34bb9c70197ca371ed8295036bacf839 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -221,6 +221,7 @@ enum connection_mode {
 	LWS_CONNMODE_SSL_ACK_PENDING,
 
 	/* transient modes */
+	LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT,
 	LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY,
 	LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE,
 	LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY,
diff --git a/test-server/test-client.c b/test-server/test-client.c
index 7b32cb1f672ada24e455f86cd3e15f42ee557913..949d398815f760c002c4630fbe8c5b3c3693dc45 100644
--- a/test-server/test-client.c
+++ b/test-server/test-client.c
@@ -39,6 +39,7 @@ static int deny_mux;
 static struct libwebsocket *wsi_mirror;
 static int mirror_lifetime = 0;
 static int force_exit = 0;
+static int longlived = 0;
 
 /*
  * This demo shows how to connect multiple websockets simultaneously to a
@@ -73,6 +74,15 @@ callback_dumb_increment(struct libwebsocket_context *this,
 {
 	switch (reason) {
 
+	case LWS_CALLBACK_CLIENT_ESTABLISHED:
+		fprintf(stderr, "callback_dumb_increment: LWS_CALLBACK_CLIENT_ESTABLISHED\n");
+		break;
+
+	case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
+		fprintf(stderr, "LWS_CALLBACK_CLIENT_CONNECTION_ERROR\n");
+		was_closed = 1;
+		break;
+
 	case LWS_CALLBACK_CLOSED:
 		fprintf(stderr, "LWS_CALLBACK_CLOSED\n");
 		was_closed = 1;
@@ -125,13 +135,25 @@ callback_lws_mirror(struct libwebsocket_context *context,
 
 	switch (reason) {
 
-	case LWS_CALLBACK_CLOSED:
-		fprintf(stderr, "mirror: LWS_CALLBACK_CLOSED mirror_lifetime=%d\n", mirror_lifetime);
-		wsi_mirror = NULL;
-		break;
-
 	case LWS_CALLBACK_CLIENT_ESTABLISHED:
 
+		fprintf(stderr, "callback_lws_mirror: LWS_CALLBACK_CLIENT_ESTABLISHED\n");
+
+		mirror_lifetime = 10 + (random() & 1023);
+		/* useful to test single connection stability */
+		if (longlived)
+			mirror_lifetime += 50000;
+
+		fprintf(stderr, "opened mirror connection with "
+				     "%d lifetime\n", mirror_lifetime);
+
+		/*
+		 * mirror_lifetime is decremented each send, when it reaches
+		 * zero the connection is closed in the send callback.
+		 * When the close callback comes, wsi_mirror is set to NULL
+		 * so a new connection will be opened
+		 */
+
 		/*
 		 * start the ball rolling,
 		 * LWS_CALLBACK_CLIENT_WRITEABLE will come next service
@@ -140,6 +162,11 @@ callback_lws_mirror(struct libwebsocket_context *context,
 		libwebsocket_callback_on_writable(context, wsi);
 		break;
 
+	case LWS_CALLBACK_CLOSED:
+		fprintf(stderr, "mirror: LWS_CALLBACK_CLOSED mirror_lifetime=%d\n", mirror_lifetime);
+		wsi_mirror = NULL;
+		break;
+
 	case LWS_CALLBACK_CLIENT_RECEIVE:
 /*		fprintf(stderr, "rx %d '%s'\n", (int)len, (char *)in); */
 		break;
@@ -227,7 +254,6 @@ int main(int argc, char **argv)
 	const char *address;
 	struct libwebsocket *wsi_dumb;
 	int ietf_version = -1; /* latest */
-	int longlived = 0;
 	struct lws_context_creation_info info;
 
 	memset(&info, 0, sizeof info);
@@ -306,16 +332,18 @@ int main(int argc, char **argv)
 			 protocols[PROTOCOL_DUMB_INCREMENT].name, ietf_version);
 
 	if (wsi_dumb == NULL) {
-		fprintf(stderr, "libwebsocket dumb connect failed\n");
+		fprintf(stderr, "libwebsocket connect failed\n");
 		ret = 1;
 		goto bail;
 	}
 
-	fprintf(stderr, "Websocket connections opened\n");
+	fprintf(stderr, "Waiting for connect...\n");
 
 	/*
 	 * sit there servicing the websocket context to handle incoming
 	 * packets, and drawing random circles on the mirror protocol websocket
+	 * nothing happens until the client websocket connection is
+	 * asynchronously established
 	 */
 
 	n = 0;
@@ -337,25 +365,10 @@ int main(int argc, char **argv)
 
 		if (wsi_mirror == NULL) {
 			fprintf(stderr, "libwebsocket "
-					      "dumb connect failed\n");
+					      "mirror connect failed\n");
 			ret = 1;
 			goto bail;
 		}
-
-		mirror_lifetime = 10 + (random() & 1023);
-		/* useful to test single connection stability */
-		if (longlived)
-			mirror_lifetime += 50000;
-
-		fprintf(stderr, "opened mirror connection with "
-				     "%d lifetime\n", mirror_lifetime);
-
-		/*
-		 * mirror_lifetime is decremented each send, when it reaches
-		 * zero the connection is closed in the send callback.
-		 * When the close callback comes, wsi_mirror is set to NULL
-		 * so a new connection will be opened
-		 */
 	}
 
 bail: