mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-16 00:00:07 +01:00

This is a huge patch that should be a global NOP. For unix type platforms it enables -Wconversion to issue warnings (-> error) for all automatic casts that seem less than ideal but are normally concealed by the toolchain. This is things like passing an int to a size_t argument. Once enabled, I went through all args on my default build (which build most things) and tried to make the removed default cast explicit. With that approach it neither change nor bloat the code, since it compiles to whatever it was doing before, just with the casts made explicit... in a few cases I changed some length args from int to size_t but largely left the causes alone. From now on, new code that is relying on less than ideal casting will complain and nudge me to improve it by warnings.
399 lines
9.2 KiB
C
399 lines
9.2 KiB
C
/*
|
|
* lws-api-test-lws_sequencer
|
|
*
|
|
* Written in 2019 by Andy Green <andy@warmcat.com>
|
|
*
|
|
* This file is made available under the Creative Commons CC0 1.0
|
|
* Universal Public Domain Dedication.
|
|
*
|
|
* This api test uses the lws_sequencer api to make five http client requests
|
|
* to libwebsockets.org in sequence, from inside the event loop. The fourth
|
|
* fourth http client request is directed to port 22 where it stalls
|
|
* triggering the lws_sequencer timeout flow. The fifth is given a nonexistant
|
|
* dns name and is expected to fail.
|
|
*/
|
|
|
|
#include <libwebsockets.h>
|
|
|
|
#include <signal.h>
|
|
|
|
static int interrupted, test_good = 0;
|
|
|
|
enum {
|
|
SEQ1,
|
|
SEQ2,
|
|
SEQ3_404,
|
|
SEQ4_TIMEOUT, /* we expect to timeout */
|
|
SEQ5_BAD_ADDRESS /* we expect the connection to fail */
|
|
};
|
|
|
|
/*
|
|
* This is the user defined struct whose space is allocated along with the
|
|
* sequencer when that is created.
|
|
*
|
|
* You'd put everything your sequencer needs to do its job in here.
|
|
*/
|
|
|
|
struct myseq {
|
|
struct lws_vhost *vhost;
|
|
struct lws *cwsi; /* client wsi for current step if any */
|
|
|
|
int state; /* which test we're on */
|
|
int http_resp;
|
|
};
|
|
|
|
/* sequencer messages specific to this sequencer */
|
|
|
|
enum {
|
|
SEQ_MSG_CLIENT_FAILED = LWSSEQ_USER_BASE,
|
|
SEQ_MSG_CLIENT_DONE,
|
|
};
|
|
|
|
/* this is the sequence of GETs we will do */
|
|
|
|
static const char *url_paths[] = {
|
|
"https://libwebsockets.org/index.html",
|
|
"https://libwebsockets.org/lws.css",
|
|
"https://libwebsockets.org/404.html",
|
|
"https://libwebsockets.org:22", /* this causes us to time out */
|
|
"https://doesntexist.invalid/" /* fail early in connect */
|
|
};
|
|
|
|
|
|
static void
|
|
sigint_handler(int sig)
|
|
{
|
|
interrupted = 1;
|
|
}
|
|
|
|
/*
|
|
* This is the sequencer-aware http protocol handler. It monitors the client
|
|
* http action and queues messages for the sequencer when something definitive
|
|
* happens.
|
|
*/
|
|
|
|
static int
|
|
callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
|
void *in, size_t len)
|
|
{
|
|
struct myseq *s = (struct myseq *)user;
|
|
int seq_msg = SEQ_MSG_CLIENT_FAILED;
|
|
|
|
switch (reason) {
|
|
|
|
/* because we are protocols[0] ... */
|
|
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
|
|
lwsl_notice("CLIENT_CONNECTION_ERROR: %s\n",
|
|
in ? (char *)in : "(null)");
|
|
goto notify;
|
|
|
|
case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
|
|
if (!s)
|
|
return 1;
|
|
s->http_resp = (int)lws_http_client_http_response(wsi);
|
|
lwsl_info("Connected with server response: %d\n", s->http_resp);
|
|
break;
|
|
|
|
/* chunks of chunked content, with header removed */
|
|
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
|
|
lwsl_info("RECEIVE_CLIENT_HTTP_READ: read %d\n", (int)len);
|
|
#if 0 /* enable to dump the html */
|
|
{
|
|
const char *p = in;
|
|
|
|
while (len--)
|
|
if (*p < 0x7f)
|
|
putchar(*p++);
|
|
else
|
|
putchar('.');
|
|
}
|
|
#endif
|
|
return 0; /* don't passthru */
|
|
|
|
/* uninterpreted http content */
|
|
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
|
|
{
|
|
char buffer[1024 + LWS_PRE];
|
|
char *px = buffer + LWS_PRE;
|
|
int lenx = sizeof(buffer) - LWS_PRE;
|
|
|
|
if (lws_http_client_read(wsi, &px, &lenx) < 0)
|
|
return -1;
|
|
}
|
|
return 0; /* don't passthru */
|
|
|
|
case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
|
|
lwsl_notice("LWS_CALLBACK_COMPLETED_CLIENT_HTTP: wsi %p\n",
|
|
wsi);
|
|
if (!s)
|
|
return 1;
|
|
/*
|
|
* We got a definitive transaction completion
|
|
*/
|
|
seq_msg = SEQ_MSG_CLIENT_DONE;
|
|
goto notify;
|
|
|
|
case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
|
|
lwsl_info("LWS_CALLBACK_CLOSED_CLIENT_HTTP\n");
|
|
if (!s)
|
|
return 1;
|
|
|
|
lwsl_user("%s: wsi %p: seq failed at CLOSED_CLIENT_HTTP\n",
|
|
__func__, wsi);
|
|
goto notify;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return lws_callback_http_dummy(wsi, reason, user, in, len);
|
|
|
|
notify:
|
|
/*
|
|
* We only inform the sequencer of a definitive outcome for our step.
|
|
*
|
|
* So once we have informed it, we detach ourselves from the sequencer
|
|
* and the sequencer from ourselves. Wsi may want to live on but after
|
|
* we got our result and moved on to the next test or completed, the
|
|
* sequencer doesn't want to hear from it again.
|
|
*/
|
|
if (!s)
|
|
return 1;
|
|
|
|
lws_set_wsi_user(wsi, NULL);
|
|
s->cwsi = NULL;
|
|
lws_seq_queue_event(lws_seq_from_user(s), seq_msg,
|
|
NULL, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct lws_protocols protocols[] = {
|
|
{ "seq-test-http", callback_http, 0, 0, },
|
|
{ NULL, NULL, 0, 0 }
|
|
};
|
|
|
|
|
|
static int
|
|
sequencer_start_client(struct myseq *s)
|
|
{
|
|
struct lws_client_connect_info i;
|
|
const char *prot, *path1;
|
|
char uri[128], path[128];
|
|
int n;
|
|
|
|
lws_strncpy(uri, url_paths[s->state], sizeof(uri));
|
|
|
|
memset(&i, 0, sizeof i);
|
|
i.context = lws_seq_get_context(lws_seq_from_user(s));
|
|
|
|
if (lws_parse_uri(uri, &prot, &i.address, &i.port, &path1)) {
|
|
lwsl_err("%s: uri error %s\n", __func__, uri);
|
|
}
|
|
|
|
if (!strcmp(prot, "https"))
|
|
i.ssl_connection = LCCSCF_USE_SSL;
|
|
|
|
path[0] = '/';
|
|
n = 1;
|
|
if (path1[0] == '/')
|
|
n = 0;
|
|
lws_strncpy(&path[n], path1, sizeof(path) - 1);
|
|
|
|
i.path = path;
|
|
i.host = i.address;
|
|
i.origin = i.address;
|
|
i.method = "GET";
|
|
i.vhost = s->vhost;
|
|
i.userdata = s;
|
|
|
|
i.protocol = protocols[0].name;
|
|
i.local_protocol_name = protocols[0].name;
|
|
i.pwsi = &s->cwsi;
|
|
|
|
if (!lws_client_connect_via_info(&i)) {
|
|
lwsl_notice("%s: connecting to %s://%s:%d%s failed\n",
|
|
__func__, prot, i.address, i.port, path);
|
|
|
|
/* we couldn't even get started with the client connection */
|
|
|
|
lws_seq_queue_event(lws_seq_from_user(s),
|
|
SEQ_MSG_CLIENT_FAILED, NULL, NULL);
|
|
|
|
return 1;
|
|
}
|
|
|
|
lws_seq_timeout_us(lws_seq_from_user(s), 3 * LWS_US_PER_SEC);
|
|
|
|
lwsl_notice("%s: wsi %p: connecting to %s://%s:%d%s\n", __func__,
|
|
s->cwsi, prot, i.address, i.port, path);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* The sequencer callback handles queued sequencer messages in the order they
|
|
* were queued. The messages are presented from the event loop thread context
|
|
* even if they were queued from a different thread.
|
|
*/
|
|
|
|
static lws_seq_cb_return_t
|
|
sequencer_cb(struct lws_sequencer *seq, void *user, int event,
|
|
void *data, void *aux)
|
|
{
|
|
struct myseq *s = (struct myseq *)user;
|
|
|
|
switch ((int)event) {
|
|
case LWSSEQ_CREATED: /* our sequencer just got started */
|
|
s->state = SEQ1; /* first thing we'll do is the first url */
|
|
goto step;
|
|
|
|
case LWSSEQ_DESTROYED:
|
|
/*
|
|
* This sequencer is about to be destroyed. If we have any
|
|
* other assets in play, detach them from us.
|
|
*/
|
|
if (s->cwsi)
|
|
lws_set_wsi_user(s->cwsi, NULL);
|
|
|
|
interrupted = 1;
|
|
break;
|
|
|
|
case LWSSEQ_TIMED_OUT: /* current step timed out */
|
|
if (s->state == SEQ4_TIMEOUT) {
|
|
lwsl_user("%s: test %d got expected timeout\n",
|
|
__func__, s->state);
|
|
goto done;
|
|
}
|
|
lwsl_user("%s: seq timed out at step %d\n", __func__, s->state);
|
|
return LWSSEQ_RET_DESTROY;
|
|
|
|
case SEQ_MSG_CLIENT_FAILED:
|
|
if (s->state == SEQ5_BAD_ADDRESS) {
|
|
/*
|
|
* in this specific case, we expect to fail
|
|
*/
|
|
lwsl_user("%s: test %d failed as expected\n",
|
|
__func__, s->state);
|
|
goto done;
|
|
}
|
|
|
|
lwsl_user("%s: seq failed at step %d\n", __func__, s->state);
|
|
|
|
return LWSSEQ_RET_DESTROY;
|
|
|
|
case SEQ_MSG_CLIENT_DONE:
|
|
if (s->state >= SEQ4_TIMEOUT) {
|
|
/*
|
|
* In these specific cases, done would be a failure,
|
|
* we expected to timeout or fail
|
|
*/
|
|
lwsl_user("%s: seq failed at step %d\n", __func__,
|
|
s->state);
|
|
|
|
return LWSSEQ_RET_DESTROY;
|
|
}
|
|
lwsl_user("%s: seq done step %d (resp %d)\n", __func__,
|
|
s->state, s->http_resp);
|
|
|
|
done:
|
|
lws_seq_timeout_us(lws_seq_from_user(s), LWSSEQTO_NONE);
|
|
s->state++;
|
|
if (s->state == LWS_ARRAY_SIZE(url_paths)) {
|
|
/* the sequence has completed */
|
|
lwsl_user("%s: sequence completed OK\n", __func__);
|
|
|
|
test_good = 1;
|
|
|
|
return LWSSEQ_RET_DESTROY;
|
|
}
|
|
|
|
step:
|
|
sequencer_start_client(s);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return LWSSEQ_RET_CONTINUE;
|
|
}
|
|
|
|
int
|
|
main(int argc, const char **argv)
|
|
{
|
|
int n = 1, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
|
|
struct lws_context_creation_info info;
|
|
struct lws_context *context;
|
|
struct lws_sequencer *seq;
|
|
struct lws_vhost *vh;
|
|
lws_seq_info_t i;
|
|
struct myseq *s;
|
|
const char *p;
|
|
|
|
/* the normal lws init */
|
|
|
|
signal(SIGINT, sigint_handler);
|
|
|
|
if ((p = lws_cmdline_option(argc, argv, "-d")))
|
|
logs = atoi(p);
|
|
|
|
lws_set_log_level(logs, NULL);
|
|
lwsl_user("LWS API selftest: lws_sequencer\n");
|
|
|
|
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
|
|
info.port = CONTEXT_PORT_NO_LISTEN;
|
|
info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT |
|
|
LWS_SERVER_OPTION_EXPLICIT_VHOSTS;
|
|
info.protocols = protocols;
|
|
|
|
#if defined(LWS_WITH_MBEDTLS) || defined(USE_WOLFSSL)
|
|
/*
|
|
* OpenSSL uses the system trust store. mbedTLS has to be told which
|
|
* CA to trust explicitly.
|
|
*/
|
|
info.client_ssl_ca_filepath = "./libwebsockets.org.cer";
|
|
#endif
|
|
|
|
context = lws_create_context(&info);
|
|
if (!context) {
|
|
lwsl_err("lws init failed\n");
|
|
return 1;
|
|
}
|
|
|
|
vh = lws_create_vhost(context, &info);
|
|
if (!vh) {
|
|
lwsl_err("Failed to create first vhost\n");
|
|
goto bail1;
|
|
}
|
|
|
|
/*
|
|
* Create the sequencer... when the event loop starts, it will
|
|
* receive the LWSSEQ_CREATED callback
|
|
*/
|
|
|
|
memset(&i, 0, sizeof(i));
|
|
i.context = context;
|
|
i.user_size = sizeof(struct myseq);
|
|
i.puser = (void **)&s;
|
|
i.cb = sequencer_cb;
|
|
i.name = "seq";
|
|
|
|
seq = lws_seq_create(&i);
|
|
if (!seq) {
|
|
lwsl_err("%s: unable to create sequencer\n", __func__);
|
|
goto bail1;
|
|
}
|
|
s->vhost = vh;
|
|
|
|
/* the usual lws event loop */
|
|
|
|
while (n >= 0 && !interrupted)
|
|
n = lws_service(context, 0);
|
|
|
|
bail1:
|
|
lwsl_user("Completed: %s\n", !test_good ? "FAIL" : "PASS");
|
|
|
|
lws_context_destroy(context);
|
|
|
|
return !test_good;
|
|
}
|