Skip to content
Snippets Groups Projects
Commit 2d8d35a1 authored by Andy Green's avatar Andy Green
Browse files

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: default avatarAndy Green <andy.green@linaro.org>
parent f859e2d3
Branches
No related tags found
No related merge requests found
...@@ -72,6 +72,10 @@ with systemd ...@@ -72,6 +72,10 @@ with systemd
6) test server html is updated with tabs and a new live server monitoring 6) test server html is updated with tabs and a new live server monitoring
feature. Input sanitization added to the js. 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 User API additions
------------------ ------------------
......
...@@ -26,7 +26,7 @@ lws_client_connect_2(struct lws *wsi) ...@@ -26,7 +26,7 @@ lws_client_connect_2(struct lws *wsi)
"CONNECT %s:%u HTTP/1.0\x0d\x0a" "CONNECT %s:%u HTTP/1.0\x0d\x0a"
"User-agent: libwebsockets\x0d\x0a", "User-agent: libwebsockets\x0d\x0a",
lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS), 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]) if (context->proxy_basic_auth_token[0])
plen += sprintf((char *)pt->serv_buf + plen, plen += sprintf((char *)pt->serv_buf + plen,
...@@ -49,10 +49,10 @@ lws_client_connect_2(struct lws *wsi) ...@@ -49,10 +49,10 @@ lws_client_connect_2(struct lws *wsi)
#ifdef LWS_USE_IPV6 #ifdef LWS_USE_IPV6
if (LWS_IPV6_ENABLED(context)) { if (LWS_IPV6_ENABLED(context)) {
memset(&server_addr6, 0, sizeof(struct sockaddr_in6)); 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 } else
#endif #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) ...@@ -258,7 +258,7 @@ lws_client_connect_2(struct lws *wsi)
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
context->http_proxy_address)) context->http_proxy_address))
goto failed; 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, n = send(wsi->sock, (char *)pt->serv_buf, plen,
MSG_NOSIGNAL); MSG_NOSIGNAL);
...@@ -358,7 +358,7 @@ lws_client_reset(struct lws *wsi, int ssl, const char *address, int port, const ...@@ -358,7 +358,7 @@ lws_client_reset(struct lws *wsi, int ssl, const char *address, int port, const
wsi->state = LWSS_CLIENT_UNCONNECTED; wsi->state = LWSS_CLIENT_UNCONNECTED;
wsi->protocol = NULL; wsi->protocol = NULL;
wsi->pending_timeout = NO_PENDING_TIMEOUT; 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); return lws_client_connect_2(wsi);
} }
...@@ -382,10 +382,16 @@ lws_client_connect_via_info(struct lws_client_connect_info *i) ...@@ -382,10 +382,16 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
goto bail; goto bail;
wsi->context = i->context; wsi->context = i->context;
/* assert the mode and union status (hdr) clearly */
lws_union_transition(wsi, LWSCM_HTTP_SERVING);
wsi->sock = LWS_SOCK_INVALID; 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) if (i->ietf_version_or_minus_one != -1 && i->ietf_version_or_minus_one)
v = 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) ...@@ -395,6 +401,13 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
wsi->protocol = NULL; wsi->protocol = NULL;
wsi->pending_timeout = NO_PENDING_TIMEOUT; wsi->pending_timeout = NO_PENDING_TIMEOUT;
wsi->position_in_fds_table = -1; 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 #ifdef LWS_OPENSSL_SUPPORT
wsi->use_ssl = i->ssl_connection; wsi->use_ssl = i->ssl_connection;
...@@ -405,42 +418,93 @@ lws_client_connect_via_info(struct lws_client_connect_info *i) ...@@ -405,42 +418,93 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
} }
#endif #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; 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, * 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 * 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,
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, i->address)) stash->address))
goto bail1; goto bail1;
/* these only need u.hdr lifetime as well */ /* 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; 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; goto bail1;
if (i->origin) if (stash->origin[0])
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ORIGIN, i->origin)) if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ORIGIN,
stash->origin))
goto bail1; goto bail1;
/* /*
* this is a list of protocols we tell the server we're okay with * 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 * 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, if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS,
i->protocol)) stash->protocol))
goto bail1; goto bail1;
wsi->protocol = &i->context->protocols[0]; lws_free_set_NULL(wsi->u.hdr.stash);
if (wsi && !wsi->user_space && i->userdata) {
wsi->user_space_externally_allocated = 1;
wsi->user_space = i->userdata;
}
/* /*
* Check with each extension if it is able to route and proxy this * 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) ...@@ -449,9 +513,10 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
* connection. * connection.
*/ */
if (lws_ext_cb_all_exts(i->context, wsi, if (lws_ext_cb_all_exts(wsi->context, wsi,
LWS_EXT_CB_CAN_PROXY_CLIENT_CONNECTION, LWS_EXT_CB_CAN_PROXY_CLIENT_CONNECTION,
(void *)i->address, i->port) > 0) { (void *)stash->address,
wsi->u.hdr.c_port) > 0) {
lwsl_client("lws_client_connect: ext handling conn\n"); lwsl_client("lws_client_connect: ext handling conn\n");
lws_set_timeout(wsi, lws_set_timeout(wsi,
...@@ -462,17 +527,12 @@ lws_client_connect_via_info(struct lws_client_connect_info *i) ...@@ -462,17 +527,12 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
return wsi; return wsi;
} }
lwsl_client("lws_client_connect: direct conn\n"); lwsl_client("lws_client_connect: direct conn\n");
wsi->context->count_wsi_allocated++; wsi->context->count_wsi_allocated++;
return lws_client_connect_2(wsi); return lws_client_connect_2(wsi);
bail1: bail1:
/* we're closing, losing some rx is OK */ lws_free_set_NULL(wsi->u.hdr.stash);
wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
lws_header_table_detach(wsi, 0);
bail:
lws_free(wsi);
return NULL; return NULL;
} }
......
...@@ -185,6 +185,11 @@ reset: ...@@ -185,6 +185,11 @@ reset:
lws_header_table_reset(wsi, autoservice); lws_header_table_reset(wsi, autoservice);
time(&wsi->u.hdr.ah->assigned); 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; return 0;
bail: bail:
...@@ -293,6 +298,11 @@ int lws_header_table_detach(struct lws *wsi, int autoservice) ...@@ -293,6 +298,11 @@ int lws_header_table_detach(struct lws *wsi, int autoservice)
wsi->u.hdr.ah_wait_list = NULL; wsi->u.hdr.ah_wait_list = NULL;
pt->ah_wait_list_length--; 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); assert(!!pt->ah_wait_list_length == !!(int)(long)pt->ah_wait_list);
bail: bail:
lws_pt_unlock(pt); lws_pt_unlock(pt);
......
...@@ -359,7 +359,7 @@ enum lws_connection_states { ...@@ -359,7 +359,7 @@ enum lws_connection_states {
LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS, LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS,
LWSS_HTTP2_ESTABLISHED, LWSS_HTTP2_ESTABLISHED,
LWSS_CGI LWSS_CGI,
}; };
enum http_version { enum http_version {
...@@ -517,7 +517,6 @@ struct allocated_headers { ...@@ -517,7 +517,6 @@ struct allocated_headers {
#ifndef LWS_NO_CLIENT #ifndef LWS_NO_CLIENT
char initial_handshake_hash_base64[30]; char initial_handshake_hash_base64[30];
unsigned short c_port;
#endif #endif
unsigned short pos; unsigned short pos;
...@@ -788,10 +787,37 @@ enum uri_esc_states { ...@@ -788,10 +787,37 @@ enum uri_esc_states {
* used interchangeably to access the same data * 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 { struct _lws_http_mode_related {
/* MUST be first in struct */ /* MUST be first in struct */
struct allocated_headers *ah; /* mirroring _lws_header_related */ struct allocated_headers *ah; /* mirroring _lws_header_related */
struct lws *ah_wait_list; 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; struct lws *new_wsi_list;
unsigned long filepos; unsigned long filepos;
unsigned long filelen; unsigned long filelen;
...@@ -950,21 +976,16 @@ struct _lws_http2_related { ...@@ -950,21 +976,16 @@ struct _lws_http2_related {
#endif #endif
struct _lws_header_related { #ifndef LWS_NO_CLIENT
/* MUST be first in struct */ struct client_info_stash {
struct allocated_headers *ah; char address[256];
struct lws *ah_wait_list; char path[1024];
unsigned char *preamble_rx; char host[256];
unsigned int preamble_rx_len; char origin[256];
enum uri_path_states ups; char protocol[256];
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;
}; };
#endif
struct _lws_websocket_related { struct _lws_websocket_related {
/* cheapest way to deal with ah overlap with ws union transition */ /* cheapest way to deal with ah overlap with ws union transition */
...@@ -1212,6 +1233,9 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt); ...@@ -1212,6 +1233,9 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt);
LWS_EXTERN int LWS_EXTERN int
lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd); lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd);
LWS_EXTERN struct lws *
lws_client_connect_via_info2(struct lws *wsi);
/* /*
* EXTENSIONS * EXTENSIONS
*/ */
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment