trac 40 client connection properly nonblocking
As spotted by JM on Trac#40 http://libwebsockets.org/trac/libwebsockets/ticket/40 client connect didn't do anything about being truly nonblocking. This patch should hopefully solve that. Signed-off-by: Andy Green <andy.green@linaro.org>
This commit is contained in:
parent
e92947680a
commit
5dc62ead9d
8 changed files with 142 additions and 45 deletions
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
----------------
|
||||
|
|
|
@ -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,30 +83,39 @@ 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;
|
||||
|
||||
if (errno == EALREADY || errno == EINPROGRESS) {
|
||||
lwsl_client("nonblocking connect retry\n");
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
lwsl_client("connected\n");
|
||||
|
||||
if (lws_set_socket_options(context, wsi->sock)) {
|
||||
lwsl_err("Failed to set wsi socket options\n");
|
||||
compatible_close(wsi->sock);
|
||||
goto oom4;
|
||||
}
|
||||
|
||||
insert_wsi_socket_into_fds(context, wsi);
|
||||
|
||||
/* we are connected to server, or proxy */
|
||||
|
||||
if (context->http_proxy_port) {
|
||||
|
||||
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 */
|
||||
|
||||
|
|
16
lib/client.c
16
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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Add table
Reference in a new issue