1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

client: do client stash in a single alloc

Improve the code around stash, getting rid of the strdups for a net
code reduction.  Remove the special destroy helper for stash since
it becomes a one-liner.

Trade several stack allocs in the client reset function for a single
sized brief heap alloc to reduce peak stack alloc by around 700 bytes.
This commit is contained in:
Andy Green 2019-08-17 20:46:53 +01:00
parent 72b482ee15
commit c099e7be92
12 changed files with 147 additions and 184 deletions

View file

@ -100,7 +100,7 @@ option(LWS_WITHOUT_DAEMONIZE "Don't build the daemonization api" ON)
option(LWS_SSL_SERVER_WITH_ECDH_CERT "Include SSL server use ECDH certificate" OFF)
option(LWS_WITH_LEJP "With the Lightweight JSON Parser" ON)
option(LWS_WITH_SQLITE3 "Require SQLITE3 support" OFF)
option(LWS_WITH_STRUCT_JSON "Generic struct serialization to and from JSON" ON)
option(LWS_WITH_STRUCT_JSON "Generic struct serialization to and from JSON" OFF)
option(LWS_WITH_STRUCT_SQLITE3 "Generic struct serialization to and from SQLITE3" OFF)
option(LWS_WITH_SMTP "Provide SMTP support" OFF)
if (WIN32 OR LWS_WITH_ESP32)

View file

@ -135,6 +135,7 @@
#cmakedefine LWS_WITH_STATEFUL_URLDECODE
#cmakedefine LWS_WITH_STATS
#cmakedefine LWS_WITH_STRUCT_SQLITE3
#cmakedefine LWS_WITH_STRUCT_JSON
#cmakedefine LWS_WITH_SQLITE3
#cmakedefine LWS_WITH_THREADPOOL
#cmakedefine LWS_WITH_TLS

View file

@ -55,7 +55,7 @@ __lws_free_wsi(struct lws *wsi)
#endif
__lws_same_vh_protocol_remove(wsi);
#if !defined(LWS_NO_CLIENT)
lws_client_stash_destroy(wsi);
lws_free_set_NULL(wsi->stash);
lws_free_set_NULL(wsi->cli_hostname_copy);
#endif
@ -257,7 +257,7 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason,
#endif
#if !defined(LWS_NO_CLIENT)
lws_client_stash_destroy(wsi);
lws_free_set_NULL(wsi->stash);
#endif
if (wsi->role_ops == &role_ops_raw_skt) {

View file

@ -24,34 +24,16 @@
#include "private-lib-core.h"
void
lws_client_stash_destroy(struct lws *wsi)
{
if (!wsi || !wsi->stash)
return;
lws_free_set_NULL(wsi->stash->address);
lws_free_set_NULL(wsi->stash->path);
lws_free_set_NULL(wsi->stash->host);
lws_free_set_NULL(wsi->stash->origin);
lws_free_set_NULL(wsi->stash->protocol);
lws_free_set_NULL(wsi->stash->method);
lws_free_set_NULL(wsi->stash->iface);
lws_free_set_NULL(wsi->stash->alpn);
lws_free_set_NULL(wsi->stash);
}
LWS_VISIBLE struct lws *
lws_client_connect_via_info(const struct lws_client_connect_info *i)
{
const char *local = i->protocol;
struct lws *wsi, *safe = NULL;
const struct lws_protocols *p;
const char *local = i->protocol;
int tid = 0;
#if LWS_MAX_SMP > 1
int n;
#endif
const char *cisin[CIS_COUNT];
int tid = 0, n, m;
size_t size;
char *pc;
if (i->context->requested_kill)
return NULL;
@ -196,45 +178,43 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i)
* with no relationship to http or ah
*/
wsi->stash = lws_zalloc(sizeof(*wsi->stash), "client stash");
cisin[CIS_ADDRESS] = i->address;
cisin[CIS_PATH] = i->path;
cisin[CIS_HOST] = i->host;
cisin[CIS_ORIGIN] = i->origin;
cisin[CIS_PROTOCOL] = i->protocol;
cisin[CIS_METHOD] = i->method;
cisin[CIS_IFACE] = i->iface;
cisin[CIS_ALPN] = i->alpn;
size = sizeof(*wsi->stash);
/*
* Let's overallocate the stash object with space for all the args
* in one hit.
*/
for (n = 0; n < CIS_COUNT; n++)
if (cisin[n])
size += strlen(cisin[n]) + 1;
wsi->stash = lws_malloc(size, "client stash");
if (!wsi->stash) {
lwsl_err("%s: OOM\n", __func__);
goto bail1;
}
/* all the pointers default to NULL, but no need to zero the args */
memset(wsi->stash, 0, sizeof(*wsi->stash));
wsi->stash->address = lws_strdup(i->address);
wsi->stash->path = lws_strdup(i->path);
wsi->stash->host = lws_strdup(i->host);
wsi->stash->opaque_user_data = i->opaque_user_data;
pc = (char *)&wsi->stash[1];
if (!wsi->stash->address || !wsi->stash->path || !wsi->stash->host)
goto bail1;
if (i->origin) {
wsi->stash->origin = lws_strdup(i->origin);
if (!wsi->stash->origin)
goto bail1;
}
if (i->protocol) {
wsi->stash->protocol = lws_strdup(i->protocol);
if (!wsi->stash->protocol)
goto bail1;
}
if (i->method) {
wsi->stash->method = lws_strdup(i->method);
if (!wsi->stash->method)
goto bail1;
}
if (i->iface) {
wsi->stash->iface = lws_strdup(i->iface);
if (!wsi->stash->iface)
goto bail1;
}
if (i->alpn) {
wsi->stash->alpn = lws_strdup(i->alpn);
if (!wsi->stash->alpn)
goto bail1;
}
for (n = 0; n < CIS_COUNT; n++)
if (cisin[n]) {
wsi->stash->cis[n] = pc;
m = strlen(cisin[n]) + 1;
memcpy(pc, cisin[n], m);
pc += m;
}
/*
* at this point user callbacks like
@ -302,7 +282,7 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i)
return wsi;
bail1:
lws_client_stash_destroy(wsi);
lws_free_set_NULL(wsi->stash);
bail:
lws_free(wsi);

View file

@ -231,17 +231,24 @@ enum uri_esc_states {
URIES_SEEN_PERCENT_H1,
};
#ifndef LWS_NO_CLIENT
enum {
CIS_ADDRESS,
CIS_PATH,
CIS_HOST,
CIS_ORIGIN,
CIS_PROTOCOL,
CIS_METHOD,
CIS_IFACE,
CIS_ALPN,
CIS_COUNT
};
struct client_info_stash {
char *address;
char *path;
char *host;
char *origin;
char *protocol;
char *method;
char *iface;
char *alpn;
char *cis[CIS_COUNT];
void *opaque_user_data; /* not allocated or freed by lws */
};
#endif
@ -886,9 +893,6 @@ lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len);
LWS_EXTERN int
lws_service_flag_pending(struct lws_context *context, int tsi);
LWS_EXTERN void
lws_client_stash_destroy(struct lws *wsi);
static LWS_INLINE int
lws_has_buffered_out(struct lws *wsi) { return !!wsi->buflist_out; }

View file

@ -618,6 +618,9 @@ LWS_EXTERN int alloc_file(struct lws_context *context, const char *filename,
void
lws_context_destroy2(struct lws_context *context);
#if !defined(PRIu64)
#define PRIu64 "llu"
#endif
#ifdef __cplusplus
};

View file

@ -937,7 +937,7 @@ rops_client_bind_h1(struct lws *wsi, const struct lws_client_connect_info *i)
* we can assign the user space now, otherwise do it after the
* ws subprotocol negotiated
*/
if (!wsi->user_space && wsi->stash->method)
if (!wsi->user_space && wsi->stash->cis[CIS_METHOD])
if (lws_ensure_user_space(wsi))
return 1;
@ -951,11 +951,8 @@ rops_client_bind_h1(struct lws *wsi, const struct lws_client_connect_info *i)
* only try h2 if he assertively said to use h2 alpn, otherwise
* ws implies alpn restriction to h1.
*/
if (!wsi->stash->method && !wsi->stash->alpn) {
wsi->stash->alpn = lws_strdup("http/1.1");
if (!wsi->stash->alpn)
return 1;
}
if (!wsi->stash->cis[CIS_METHOD] && !wsi->stash->cis[CIS_ALPN])
wsi->stash->cis[CIS_ALPN] = "http/1.1";
/* if we went on the ah waiting list, it's ok, we can wait.
*

View file

@ -793,7 +793,8 @@ lws_h2_dump_waiting_children(struct lws *wsi)
while (wsi) {
lwsl_info(" %c %p %s %s\n",
wsi->h2.requested_POLLOUT ? '*' : ' ',
wsi, wsi->role_ops->name, wsi->protocol->name);
wsi, wsi->role_ops->name, wsi->protocol ?
wsi->protocol->name : "noprotocol");
wsi = wsi->h2.sibling_list;
}

View file

@ -60,7 +60,7 @@ lws_client_connect_3(struct lws *wsi, struct lws *wsi_piggyback, ssize_t plen)
int n, m, rawish = 0;
if (wsi->stash)
meth = wsi->stash->method;
meth = wsi->stash->cis[CIS_METHOD];
else
meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
@ -78,17 +78,11 @@ lws_client_connect_3(struct lws *wsi, struct lws *wsi_piggyback, ssize_t plen)
/*
* OK from now on we talk via the proxy, so connect to that
*
* (will overwrite existing pointer,
* leaving old string/frag there but unreferenced)
*/
if (wsi->stash) {
lws_free(wsi->stash->address);
wsi->stash->address =
lws_strdup(wsi->vhost->http.http_proxy_address);
if (!wsi->stash->address)
goto failed;
} else
if (wsi->stash)
wsi->stash->cis[CIS_ADDRESS] =
wsi->vhost->http.http_proxy_address;
else
if (lws_hdr_simple_create(wsi,
_WSI_TOKEN_CLIENT_PEER_ADDRESS,
wsi->vhost->http.http_proxy_address))
@ -270,7 +264,7 @@ lws_client_connect_2(struct lws *wsi)
/* we can only piggyback GET or POST */
if (wsi->stash)
meth = wsi->stash->method;
meth = wsi->stash->cis[CIS_METHOD];
else
meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
@ -368,8 +362,9 @@ create_new_conn:
*/
if (!wsi->cli_hostname_copy) {
if (wsi->stash)
wsi->cli_hostname_copy = lws_strdup(wsi->stash->host);
if (wsi->stash && wsi->stash->cis[CIS_HOST])
wsi->cli_hostname_copy =
lws_strdup(wsi->stash->cis[CIS_HOST]);
else {
char *pa = lws_hdr_simple_ptr(wsi,
_WSI_TOKEN_CLIENT_PEER_ADDRESS);
@ -401,7 +396,7 @@ create_new_conn:
*/
if (wsi->stash)
ads = wsi->stash->address;
ads = wsi->stash->cis[CIS_ADDRESS];
else
ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS);
#if defined(LWS_WITH_UNIX_SOCK)
@ -490,9 +485,9 @@ create_new_conn:
* to whatever we decided to connect to
*/
lwsl_info("%s: %p: address %s:%u\n", __func__, wsi, ads, port);
lwsl_info("%s: %p: address %s:%u\n", __func__, wsi, ads, port);
n = lws_getaddrinfo46(wsi, ads, &result);
n = lws_getaddrinfo46(wsi, ads, &result);
memset(&sa46, 0, sizeof(sa46));
#ifdef LWS_WITH_IPV6
if (wsi->ipv6) {
@ -571,7 +566,7 @@ create_new_conn:
}
#endif
} else {
lwsl_err("getaddrinfo failed: %d\n", n);
lwsl_err("getaddrinfo failed: %s: %d\n", ads, n);
cce = "getaddrinfo failed";
goto oom4;
}
@ -678,7 +673,7 @@ ads_known:
AWAITING_TIMEOUT);
if (wsi->stash)
iface = wsi->stash->iface;
iface = wsi->stash->cis[CIS_IFACE];
else
iface = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE);
@ -792,6 +787,14 @@ failed1:
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
static uint8_t hnames2[] = {
_WSI_TOKEN_CLIENT_ORIGIN,
_WSI_TOKEN_CLIENT_SENT_PROTOCOLS,
_WSI_TOKEN_CLIENT_METHOD,
_WSI_TOKEN_CLIENT_IFACE,
_WSI_TOKEN_CLIENT_ALPN
};
/**
* lws_client_reset() - retarget a connected wsi to start over with a new
* connection (ie, redirect)
@ -802,13 +805,14 @@ failed1:
* path: uri path to connect to on the new server
* host: host header to send to the new server
*/
LWS_VISIBLE struct lws *
struct lws *
lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
const char *path, const char *host)
{
char origin[300] = "", protocol[300] = "", method[32] = "",
iface[16] = "", alpn[32] = "", *p;
char *stash, *p;
struct lws *wsi;
size_t size = 0;
int n;
if (!pwsi)
return NULL;
@ -821,25 +825,23 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
}
wsi->redirects++;
p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN);
if (p)
lws_strncpy(origin, p, sizeof(origin));
for (n = 0; n < (int)LWS_ARRAY_SIZE(hnames2); n++)
size += lws_hdr_total_length(wsi, hnames2[n]) + 1;
p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
if (p)
lws_strncpy(protocol, p, sizeof(protocol));
if ((int)size < lws_hdr_total_length(wsi, _WSI_TOKEN_CLIENT_URI) + 1)
size = lws_hdr_total_length(wsi, _WSI_TOKEN_CLIENT_URI) + 1;
p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
if (p)
lws_strncpy(method, p, sizeof(method));
p = stash = lws_malloc(size, __func__);
if (!stash)
return NULL;
p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE);
if (p)
lws_strncpy(iface, p, sizeof(iface));
p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ALPN);
if (p)
lws_strncpy(alpn, p, sizeof(alpn));
for (n = 0; n < (int)LWS_ARRAY_SIZE(hnames2); n++)
if (lws_hdr_total_length(wsi, hnames2[n])) {
memcpy(p, lws_hdr_simple_ptr(wsi, hnames2[n]),
lws_hdr_total_length(wsi, hnames2[n]) + 1);
p += lws_hdr_total_length(wsi, hnames2[n]) + 1;
} else
*p++ = '\0';
if (!port) {
port = 443;
@ -860,14 +862,15 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
if (wsi->context->event_loop_ops->close_handle_manually)
wsi->context->event_loop_ops->close_handle_manually(wsi);
else
compatible_close(wsi->desc.sockfd);
if (wsi->desc.sockfd != LWS_SOCK_INVALID)
compatible_close(wsi->desc.sockfd);
#if defined(LWS_WITH_TLS)
wsi->tls.use_ssl = ssl;
#else
if (ssl) {
lwsl_err("%s: not configured for ssl\n", __func__);
return NULL;
goto bail;
}
#endif
@ -880,41 +883,33 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
_lws_header_table_reset(wsi->http.ah);
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, address))
return NULL;
goto bail;
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, host))
return NULL;
goto bail;
if (origin[0])
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ORIGIN,
origin))
return NULL;
if (protocol[0])
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS,
protocol))
return NULL;
if (method[0])
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_METHOD,
method))
return NULL;
p = stash;
for (n = 0; n < (int)LWS_ARRAY_SIZE(hnames2); n++) {
if (lws_hdr_simple_create(wsi, hnames2[n], p))
goto bail;
p += lws_hdr_total_length(wsi, hnames2[n]) + 1;
}
if (iface[0])
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_IFACE,
iface))
return NULL;
if (alpn[0])
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ALPN,
alpn))
return NULL;
stash[0] = '/';
lws_strncpy(&stash[1], path, size - 1);
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, stash))
goto bail;
origin[0] = '/';
strncpy(&origin[1], path, sizeof(origin) - 2);
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, origin))
return NULL;
lws_free_set_NULL(stash);
*pwsi = lws_client_connect_2(wsi);
return *pwsi;
bail:
lws_free_set_NULL(stash);
return NULL;
}
#if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_WITH_HUBBUB)
@ -1048,10 +1043,22 @@ html_parser_cb(const hubbub_token *token, void *pw)
#endif
static const uint8_t hnames[] = {
_WSI_TOKEN_CLIENT_PEER_ADDRESS,
_WSI_TOKEN_CLIENT_URI,
_WSI_TOKEN_CLIENT_HOST,
_WSI_TOKEN_CLIENT_ORIGIN,
_WSI_TOKEN_CLIENT_SENT_PROTOCOLS,
_WSI_TOKEN_CLIENT_METHOD,
_WSI_TOKEN_CLIENT_IFACE,
_WSI_TOKEN_CLIENT_ALPN
};
struct lws *
lws_http_client_connect_via_info2(struct lws *wsi)
{
struct client_info_stash *stash = wsi->stash;
int n;
lwsl_debug("%s: %p (stash %p)\n", __func__, wsi, stash);
@ -1060,7 +1067,7 @@ lws_http_client_connect_via_info2(struct lws *wsi)
wsi->opaque_user_data = wsi->stash->opaque_user_data;
if (stash->method && !strcmp(stash->method, "RAW"))
if (stash->cis[CIS_METHOD] && !strcmp(stash->cis[CIS_METHOD], "RAW"))
goto no_ah;
/*
@ -1068,44 +1075,14 @@ lws_http_client_connect_via_info2(struct lws *wsi)
* stash them... we only need during connect phase so into a temp
* allocated stash
*/
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
stash->address))
goto bail1;
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, stash->path))
goto bail1;
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, stash->host))
goto bail1;
if (stash->origin)
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 (stash->protocol)
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS,
stash->protocol))
goto bail1;
if (stash->method)
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_METHOD,
stash->method))
goto bail1;
if (stash->iface)
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_IFACE,
stash->iface))
goto bail1;
if (stash->alpn)
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ALPN,
stash->alpn))
goto bail1;
for (n = 0; n < (int)LWS_ARRAY_SIZE(hnames); n++)
if (hnames[n] && stash->cis[n])
if (lws_hdr_simple_create(wsi, hnames[n], stash->cis[n]))
goto bail1;
#if defined(LWS_WITH_SOCKS5)
if (!wsi->vhost->socks_proxy_port)
lws_client_stash_destroy(wsi);
lws_free_set_NULL(wsi->stash);
#endif
no_ah:

View file

@ -250,7 +250,7 @@ socks_reply_fail:
lwsl_client("socks connect OK\n");
/* free stash since we are done with it */
lws_client_stash_destroy(wsi);
lws_free_set_NULL(wsi->stash);
if (lws_hdr_simple_create(wsi,
_WSI_TOKEN_CLIENT_PEER_ADDRESS,
wsi->vhost->socks_proxy_address)) {
@ -705,7 +705,7 @@ lws_client_interpret_server_handshake(struct lws *wsi)
char *p, *q;
char new_path[300];
lws_client_stash_destroy(wsi);
lws_free_set_NULL(wsi->stash);
ah = wsi->http.ah;
if (!wsi->do_ws) {

View file

@ -155,7 +155,7 @@ rops_client_bind_raw_proxy(struct lws *wsi,
/* finalize */
if (!wsi->user_space && wsi->stash->method)
if (!wsi->user_space && wsi->stash->cis[CIS_METHOD])
if (lws_ensure_user_space(wsi))
return 1;

View file

@ -197,7 +197,7 @@ rops_client_bind_raw_skt(struct lws *wsi,
/* finalize */
if (!wsi->user_space && wsi->stash->method)
if (!wsi->user_space && wsi->stash->cis[CIS_METHOD])
if (lws_ensure_user_space(wsi))
return 1;