client accept connection request even if no free ah

It can join the free ah list and pick up client connect processing
later when the ah becomes available; this simplifies the code
doing the request since he won't have to deal with unexpected
failures / retries based on dynamic ah availability.

To do this though we have to handle that the connect_info members
may not have scope that lets them still exist after we return from
the first connect call, we stash them in a malloc'd buffer so the
connect processing can have them much later even so.

Signed-off-by: Andy Green <andy.green@linaro.org>
This commit is contained in:
Andy Green 2016-02-29 14:19:16 +08:00
parent f859e2d3ed
commit 2d8d35a1be
4 changed files with 143 additions and 45 deletions

View file

@ -72,6 +72,10 @@ with systemd
6) test server html is updated with tabs and a new live server monitoring
feature. Input sanitization added to the js.
7) client connections attempted when no ah is free no longer fail, they are
just deferred until an ah becomes available.
User API additions
------------------

View file

@ -26,7 +26,7 @@ lws_client_connect_2(struct lws *wsi)
"CONNECT %s:%u HTTP/1.0\x0d\x0a"
"User-agent: libwebsockets\x0d\x0a",
lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS),
wsi->u.hdr.ah->c_port);
wsi->u.hdr.c_port);
if (context->proxy_basic_auth_token[0])
plen += sprintf((char *)pt->serv_buf + plen,
@ -49,10 +49,10 @@ lws_client_connect_2(struct lws *wsi)
#ifdef LWS_USE_IPV6
if (LWS_IPV6_ENABLED(context)) {
memset(&server_addr6, 0, sizeof(struct sockaddr_in6));
server_addr6.sin6_port = htons(wsi->u.hdr.ah->c_port);
server_addr6.sin6_port = htons(wsi->u.hdr.c_port);
} else
#endif
server_addr4.sin_port = htons(wsi->u.hdr.ah->c_port);
server_addr4.sin_port = htons(wsi->u.hdr.c_port);
}
/*
@ -258,7 +258,7 @@ lws_client_connect_2(struct lws *wsi)
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
context->http_proxy_address))
goto failed;
wsi->u.hdr.ah->c_port = context->http_proxy_port;
wsi->u.hdr.c_port = context->http_proxy_port;
n = send(wsi->sock, (char *)pt->serv_buf, plen,
MSG_NOSIGNAL);
@ -358,7 +358,7 @@ lws_client_reset(struct lws *wsi, int ssl, const char *address, int port, const
wsi->state = LWSS_CLIENT_UNCONNECTED;
wsi->protocol = NULL;
wsi->pending_timeout = NO_PENDING_TIMEOUT;
wsi->u.hdr.ah->c_port = port;
wsi->u.hdr.c_port = port;
return lws_client_connect_2(wsi);
}
@ -382,10 +382,16 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
goto bail;
wsi->context = i->context;
/* assert the mode and union status (hdr) clearly */
lws_union_transition(wsi, LWSCM_HTTP_SERVING);
wsi->sock = LWS_SOCK_INVALID;
/* -1 means just use latest supported */
/* 1) fill up the wsi with stuff from the connect_info as far as it
* can go. It's because not only is our connection async, we might
* not even be able to get ahold of an ah at this point.
*/
/* -1 means just use latest supported */
if (i->ietf_version_or_minus_one != -1 && i->ietf_version_or_minus_one)
v = i->ietf_version_or_minus_one;
@ -395,6 +401,13 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
wsi->protocol = NULL;
wsi->pending_timeout = NO_PENDING_TIMEOUT;
wsi->position_in_fds_table = -1;
wsi->u.hdr.c_port = i->port;
wsi->protocol = &i->context->protocols[0];
if (wsi && !wsi->user_space && i->userdata) {
wsi->user_space_externally_allocated = 1;
wsi->user_space = i->userdata;
}
#ifdef LWS_OPENSSL_SUPPORT
wsi->use_ssl = i->ssl_connection;
@ -405,42 +418,93 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
}
#endif
if (lws_header_table_attach(wsi, 0))
/* 2) stash the things from connect_info that we can't process without
* an ah. Because if no ah, we will go on the ah waiting list and
* process those things later (after the connect_info and maybe the
* things pointed to have gone out of scope.
*/
wsi->u.hdr.stash = lws_malloc(sizeof(*wsi->u.hdr.stash));
if (!wsi->u.hdr.stash) {
lwsl_err("%s: OOM\n", __func__);
goto bail;
}
wsi->u.hdr.stash->origin[0] = '\0';
wsi->u.hdr.stash->protocol[0] = '\0';
strncpy(wsi->u.hdr.stash->address, i->address,
sizeof(wsi->u.hdr.stash->address) - 1);
strncpy(wsi->u.hdr.stash->path, i->path,
sizeof(wsi->u.hdr.stash->path) - 1);
strncpy(wsi->u.hdr.stash->host, i->host,
sizeof(wsi->u.hdr.stash->host) - 1);
if (i->origin)
strncpy(wsi->u.hdr.stash->origin, i->origin,
sizeof(wsi->u.hdr.stash->origin) - 1);
if (i->protocol)
strncpy(wsi->u.hdr.stash->protocol, i->protocol,
sizeof(wsi->u.hdr.stash->protocol) - 1);
wsi->u.hdr.stash->address[sizeof(wsi->u.hdr.stash->address) - 1] = '\0';
wsi->u.hdr.stash->path[sizeof(wsi->u.hdr.stash->path) - 1] = '\0';
wsi->u.hdr.stash->host[sizeof(wsi->u.hdr.stash->host) - 1] = '\0';
wsi->u.hdr.stash->origin[sizeof(wsi->u.hdr.stash->origin) - 1] = '\0';
wsi->u.hdr.stash->protocol[sizeof(wsi->u.hdr.stash->protocol) - 1] = '\0';
/* if we went on the waiting list, no probs just return the wsi
* when we get the ah, now or later, he will call
* lws_client_connect_via_info2() below
*/
if (lws_header_table_attach(wsi, 0))
lwsl_debug("%s: went on ah wait list\n", __func__);
return wsi;
bail:
lws_free(wsi);
return NULL;
}
struct lws *
lws_client_connect_via_info2(struct lws *wsi)
{
struct client_info_stash *stash = wsi->u.hdr.stash;
if (!stash)
return wsi;
/*
* we're not necessarily in a position to action these right away,
* stash them... we only need during connect phase so u.hdr is fine
*/
wsi->u.hdr.ah->c_port = i->port;
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, i->address))
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
stash->address))
goto bail1;
/* these only need u.hdr lifetime as well */
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, i->path))
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, stash->path))
goto bail1;
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, i->host))
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, stash->host))
goto bail1;
if (i->origin)
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ORIGIN, i->origin))
if (stash->origin[0])
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ORIGIN,
stash->origin))
goto bail1;
/*
* this is a list of protocols we tell the server we're okay with
* stash it for later when we compare server response with it
*/
if (i->protocol)
if (stash->protocol[0])
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS,
i->protocol))
stash->protocol))
goto bail1;
wsi->protocol = &i->context->protocols[0];
if (wsi && !wsi->user_space && i->userdata) {
wsi->user_space_externally_allocated = 1;
wsi->user_space = i->userdata;
}
lws_free_set_NULL(wsi->u.hdr.stash);
/*
* Check with each extension if it is able to route and proxy this
@ -449,9 +513,10 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
* connection.
*/
if (lws_ext_cb_all_exts(i->context, wsi,
LWS_EXT_CB_CAN_PROXY_CLIENT_CONNECTION,
(void *)i->address, i->port) > 0) {
if (lws_ext_cb_all_exts(wsi->context, wsi,
LWS_EXT_CB_CAN_PROXY_CLIENT_CONNECTION,
(void *)stash->address,
wsi->u.hdr.c_port) > 0) {
lwsl_client("lws_client_connect: ext handling conn\n");
lws_set_timeout(wsi,
@ -462,17 +527,12 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
return wsi;
}
lwsl_client("lws_client_connect: direct conn\n");
wsi->context->count_wsi_allocated++;
return lws_client_connect_2(wsi);
bail1:
/* we're closing, losing some rx is OK */
wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
lws_header_table_detach(wsi, 0);
bail:
lws_free(wsi);
lws_free_set_NULL(wsi->u.hdr.stash);
return NULL;
}

View file

@ -185,6 +185,11 @@ reset:
lws_header_table_reset(wsi, autoservice);
time(&wsi->u.hdr.ah->assigned);
#ifndef LWS_NO_CLIENT
if (wsi->state == LWSS_CLIENT_UNCONNECTED)
lws_client_connect_via_info2(wsi);
#endif
return 0;
bail:
@ -293,6 +298,11 @@ int lws_header_table_detach(struct lws *wsi, int autoservice)
wsi->u.hdr.ah_wait_list = NULL;
pt->ah_wait_list_length--;
#ifndef LWS_NO_CLIENT
if (wsi->state == LWSS_CLIENT_UNCONNECTED)
lws_client_connect_via_info2(wsi);
#endif
assert(!!pt->ah_wait_list_length == !!(int)(long)pt->ah_wait_list);
bail:
lws_pt_unlock(pt);

View file

@ -359,7 +359,7 @@ enum lws_connection_states {
LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS,
LWSS_HTTP2_ESTABLISHED,
LWSS_CGI
LWSS_CGI,
};
enum http_version {
@ -517,7 +517,6 @@ struct allocated_headers {
#ifndef LWS_NO_CLIENT
char initial_handshake_hash_base64[30];
unsigned short c_port;
#endif
unsigned short pos;
@ -788,10 +787,37 @@ enum uri_esc_states {
* used interchangeably to access the same data
*/
struct _lws_header_related {
/* MUST be first in struct */
struct allocated_headers *ah;
struct lws *ah_wait_list;
unsigned char *preamble_rx;
#ifndef LWS_NO_CLIENT
struct client_info_stash *stash;
#endif
unsigned int preamble_rx_len;
enum uri_path_states ups;
enum uri_esc_states ues;
short lextable_pos;
unsigned short current_token_limit;
#ifndef LWS_NO_CLIENT
unsigned short c_port;
#endif
char esc_stash;
char post_literal_equal;
unsigned char parser_state; /* enum lws_token_indexes */
char redirects;
};
struct _lws_http_mode_related {
/* MUST be first in struct */
struct allocated_headers *ah; /* mirroring _lws_header_related */
struct lws *ah_wait_list;
unsigned char *preamble_rx;
#ifndef LWS_NO_CLIENT
struct client_info_stash *stash;
#endif
unsigned int preamble_rx_len;
struct lws *new_wsi_list;
unsigned long filepos;
unsigned long filelen;
@ -950,21 +976,16 @@ struct _lws_http2_related {
#endif
struct _lws_header_related {
/* MUST be first in struct */
struct allocated_headers *ah;
struct lws *ah_wait_list;
unsigned char *preamble_rx;
unsigned int preamble_rx_len;
enum uri_path_states ups;
enum uri_esc_states ues;
short lextable_pos;
unsigned short current_token_limit;
char esc_stash;
char post_literal_equal;
unsigned char parser_state; /* enum lws_token_indexes */
char redirects;
#ifndef LWS_NO_CLIENT
struct client_info_stash {
char address[256];
char path[1024];
char host[256];
char origin[256];
char protocol[256];
};
#endif
struct _lws_websocket_related {
/* cheapest way to deal with ah overlap with ws union transition */
@ -1212,6 +1233,9 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt);
LWS_EXTERN int
lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd);
LWS_EXTERN struct lws *
lws_client_connect_via_info2(struct lws *wsi);
/*
* EXTENSIONS
*/