diff --git a/lib/client-handshake.c b/lib/client-handshake.c
index 34c29c80fea5b7c3f8f560b50542fd8057680a55..58e0ede45fde01e7e41cad77cf8b7d9194a73ca8 100644
--- a/lib/client-handshake.c
+++ b/lib/client-handshake.c
@@ -123,9 +123,15 @@ struct libwebsocket *__libwebsocket_client_connect_2(
* 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
+ * response inside the state machine... but notice in SSL case this
+ * may not have sent anything yet with 0 return, and won't until some
+ * many retries from main loop. To stop that becoming endless,
+ * cover with a timeout.
*/
+ libwebsocket_set_timeout(wsi,
+ PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE, AWAITING_TIMEOUT);
+
wsi->mode = LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE;
pfd.fd = wsi->sock;
pfd.revents = POLLIN;
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index 7a2232e8d2974391abe9fdb7dbe68fa32aa747e4..63b10ffcab34e2cd4ac2b301edad6cbcb83c7846 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -1834,7 +1834,27 @@ bail_prox_listener:
case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE:
+ /*
+ * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
+ * timeout protection set in client-handshake.c
+ */
+
#ifdef LWS_OPENSSL_SUPPORT
+
+ /*
+ * take care of our libwebsocket_callback_on_writable
+ * happening at a time when there's no real connection yet
+ */
+
+ 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);
+
+ /* we can retry this... so just cook the SSL BIO the first time */
+
if (wsi->use_ssl && !wsi->ssl) {
wsi->ssl = SSL_new(context->ssl_client_ctx);
@@ -1848,8 +1868,33 @@ bail_prox_listener:
}
if (wsi->use_ssl) {
- if (SSL_connect(wsi->ssl) <= 0) {
+ n = SSL_connect(wsi->ssl);
+
+ if (n < 0) {
+ n = SSL_get_error(wsi->ssl, n);
+
+ if (n == SSL_ERROR_WANT_READ ||
+ n == SSL_ERROR_WANT_WRITE) {
+ /*
+ * wants us to retry connect due to state of the
+ * underlying ssl layer... but since it may be
+ * stalled on blocked write, no incoming data may
+ * arrive to trigger the retry. Force (possibly
+ * many if the SSL state persists in returning the
+ * condition code, but other sockets are getting
+ * serviced inbetweentimes) us to get called back
+ * when writable.
+ */
+
+ lwsl_info("SSL_connect -> SSL_ERROR_WANT_... retrying\n");
+ libwebsocket_callback_on_writable(context, wsi);
+
+ return 0; /* no error */
+ }
+ n = -1;
+ }
+ if (n <= 0) {
/*
* retry if new data comes until we
* run into the connection timeout or win
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index 823ba7e79b04b4159e692e60c03543d22cae3ca7..212bbb1af2c0d3fec7b765d222a0ea1f6e04c34d 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -305,6 +305,7 @@ enum pending_timeout {
PENDING_TIMEOUT_AWAITING_PING,
PENDING_TIMEOUT_CLOSE_ACK,
PENDING_TIMEOUT_AWAITING_EXTENSION_CONNECT_RESPONSE,
+ PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE,
};