diff --git a/include/libwebsockets/lws-spa.h b/include/libwebsockets/lws-spa.h index 89627c318..b2f45534b 100644 --- a/include/libwebsockets/lws-spa.h +++ b/include/libwebsockets/lws-spa.h @@ -86,6 +86,9 @@ struct lws_spa; * * Creates a urldecode parser and initializes it. * + * It's recommended to use the newer api, lws_spa_create_via_info() + * instead. + * * opt_cb can be NULL if you just want normal name=value parsing, however * if one or more entries in your form are bulk data (file transfer), you * can provide this callback and filter on the name callback parameter to @@ -97,6 +100,36 @@ lws_spa_create(struct lws *wsi, const char * const *param_names, int count_params, int max_storage, lws_spa_fileupload_cb opt_cb, void *opt_data); +typedef struct lws_spa_create_info { + const char * const *param_names; /* array of form parameter names, like "username" */ + int count_params; /* count of param_names */ + int max_storage; /* total amount of form parameter values we can store */ + lws_spa_fileupload_cb opt_cb; /* NULL, or callback to receive file upload data. */ + void *opt_data; /* NULL, or user pointer provided to opt_cb. */ + size_t param_names_stride; /* 0 if param_names is an array of char *. + Else stride to next char * */ + struct lwsac **ac; /* NULL, or pointer to lwsac * to contain all + related heap allocations */ + size_t ac_chunk_size; /* 0 for default, or ac chunk size */ +} lws_spa_create_info_t; + +/** + * lws_spa_create_via_info() - create urldecode parser + * + * \param wsi: lws connection (used to find Content Type) + * \param info: pointer to struct defining the arguments + * + * Creates a urldecode parser and initializes it. + * + * opt_cb can be NULL if you just want normal name=value parsing, however + * if one or more entries in your form are bulk data (file transfer), you + * can provide this callback and filter on the name callback parameter to + * treat that urldecoded data separately. The callback should return -1 + * in case of fatal error, and 0 if OK. + */ +LWS_VISIBLE LWS_EXTERN struct lws_spa * +lws_spa_create_via_info(struct lws *wsi, const lws_spa_create_info_t *info); + /** * lws_spa_process() - parses a chunk of input data * diff --git a/lib/core-net/dummy-callback.c b/lib/core-net/dummy-callback.c index fffe99b4c..010fbb010 100644 --- a/lib/core-net/dummy-callback.c +++ b/lib/core-net/dummy-callback.c @@ -261,8 +261,10 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, #if !defined(LWS_NO_SERVER) case LWS_CALLBACK_HTTP_BODY_COMPLETION: #if defined(LWS_WITH_HTTP_PROXY) - if (wsi->child_list) + if (wsi->child_list) { lwsl_user("%s: LWS_CALLBACK_HTTP_BODY_COMPLETION: %d\n", __func__, (int)len); + break; + } #endif /* fallthru */ case LWS_CALLBACK_HTTP_FILE_COMPLETION: diff --git a/lib/roles/http/server/lws-spa.c b/lib/roles/http/server/lws-spa.c index bc8d3ce64..6300f7d83 100644 --- a/lib/roles/http/server/lws-spa.c +++ b/lib/roles/http/server/lws-spa.c @@ -1,7 +1,7 @@ /* * libwebsockets - Stateful urldecode for POST * - * Copyright (C) 2010-2017 Andy Green + * Copyright (C) 2010-2019 Andy Green * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -45,12 +45,14 @@ static const char * const mp_hdr[] = { "\x0d\x0a" }; -typedef int (*lws_urldecode_stateful_cb)(void *data, +struct lws_spa; + +typedef int (*lws_urldecode_stateful_cb)(struct lws_spa *spa, const char *name, char **buf, int len, int final); struct lws_urldecode_stateful { char *out; - void *data; + struct lws_spa *data; struct lws *wsi; char name[LWS_MAX_ELEM_NAME]; char temp[LWS_MAX_ELEM_NAME]; @@ -74,15 +76,29 @@ struct lws_urldecode_stateful { lws_urldecode_stateful_cb output; }; +struct lws_spa { + struct lws_urldecode_stateful *s; + lws_spa_create_info_t i; + int *param_length; + char finalized; + char **params; + char *storage; + char *end; +}; + static struct lws_urldecode_stateful * -lws_urldecode_s_create(struct lws *wsi, char *out, int out_len, void *data, - lws_urldecode_stateful_cb output) +lws_urldecode_s_create(struct lws_spa *spa, struct lws *wsi, char *out, + int out_len, lws_urldecode_stateful_cb output) { - struct lws_urldecode_stateful *s = lws_zalloc(sizeof(*s), - "stateful urldecode"); + struct lws_urldecode_stateful *s; char buf[205], *p; int m = 0; + if (spa->i.ac) + s = lwsac_use_zero(spa->i.ac, sizeof(*s), spa->i.ac_chunk_size); + else + s = lws_zalloc(sizeof(*s), "stateful urldecode"); + if (!s) return NULL; @@ -94,7 +110,7 @@ lws_urldecode_s_create(struct lws *wsi, char *out, int out_len, void *data, s->mp = 0; s->state = US_NAME; s->name[0] = '\0'; - s->data = data; + s->data = spa; s->wsi = wsi; if (lws_hdr_copy(wsi, buf, sizeof(buf), @@ -403,7 +419,7 @@ done: } static int -lws_urldecode_s_destroy(struct lws_urldecode_stateful *s) +lws_urldecode_s_destroy(struct lws_spa *spa, struct lws_urldecode_stateful *s) { int ret = 0; @@ -418,48 +434,40 @@ lws_urldecode_s_destroy(struct lws_urldecode_stateful *s) if (s->output(s->data, s->name, NULL, 0, LWS_UFS_CLOSE)) return -1; - lws_free(s); + if (!spa->i.ac) + lws_free(s); return ret; } -struct lws_spa { - struct lws_urldecode_stateful *s; - const char * const *param_names; - void *opt_data; - lws_spa_fileupload_cb opt_cb; - int *param_length; - int count_params; - int max_storage; - char finalized; - char **params; - char *storage; - char *end; -}; - static int -lws_urldecode_spa_lookup(struct lws_spa *spa, - const char *name) +lws_urldecode_spa_lookup(struct lws_spa *spa, const char *name) { + const char * const *pp = spa->i.param_names; int n; - for (n = 0; n < spa->count_params; n++) - if (!strcmp(spa->param_names[n], name)) + for (n = 0; n < spa->i.count_params; n++) { + if (!strcmp(*pp, name)) return n; + if (spa->i.param_names_stride) + pp = (const char * const *)(((char *)pp) + spa->i.param_names_stride); + else + pp++; + } + return -1; } static int -lws_urldecode_spa_cb(void *data, const char *name, char **buf, int len, +lws_urldecode_spa_cb(struct lws_spa *spa, const char *name, char **buf, int len, int final) { - struct lws_spa *spa = (struct lws_spa *)data; int n; if (final == LWS_UFS_CLOSE || spa->s->content_disp_filename[0]) { - if (spa->opt_cb) { - n = spa->opt_cb(spa->opt_data, name, + if (spa->i.opt_cb) { + n = spa->i.opt_cb(spa->i.opt_data, name, spa->s->content_disp_filename, buf ? *buf : NULL, len, final); @@ -469,63 +477,88 @@ lws_urldecode_spa_cb(void *data, const char *name, char **buf, int len, return 0; } n = lws_urldecode_spa_lookup(spa, name); - if (n == -1 || !len) /* unrecognized */ return 0; - if (!spa->params[n]) - spa->params[n] = *buf; + if (!spa->i.ac) { + if (!spa->params[n]) + spa->params[n] = *buf; - if ((*buf) + len >= spa->end) { - lwsl_info("%s: exceeded storage\n", __func__); - return -1; + if ((*buf) + len >= spa->end) { + lwsl_info("%s: exceeded storage\n", __func__); + return -1; + } + + /* move it on inside storage */ + (*buf) += len; + *((*buf)++) = '\0'; + + spa->s->out_len -= len + 1; + } else { + spa->params[n] = lwsac_use(spa->i.ac, len + 1, + spa->i.ac_chunk_size); + if (!spa->params[n]) + return -1; + + memcpy(spa->params[n], *buf, len); + spa->params[n][len] = '\0'; } spa->param_length[n] += len; - /* move it on inside storage */ - (*buf) += len; - *((*buf)++) = '\0'; - - spa->s->out_len -= len + 1; - return 0; } -LWS_VISIBLE LWS_EXTERN struct lws_spa * -lws_spa_create(struct lws *wsi, const char * const *param_names, - int count_params, int max_storage, - lws_spa_fileupload_cb opt_cb, void *opt_data) +struct lws_spa * +lws_spa_create_via_info(struct lws *wsi, const lws_spa_create_info_t *i) { - struct lws_spa *spa = lws_zalloc(sizeof(*spa), "spa"); + struct lws_spa *spa; + + if (i->ac) + spa = lwsac_use_zero(i->ac, sizeof(*spa), i->ac_chunk_size); + else + spa = lws_zalloc(sizeof(*spa), "spa"); if (!spa) return NULL; - spa->param_names = param_names; - spa->count_params = count_params; - spa->max_storage = max_storage; - spa->opt_cb = opt_cb; - spa->opt_data = opt_data; + spa->i = *i; + if (!spa->i.max_storage) + spa->i.max_storage = 512; + + if (i->ac) + spa->storage = lwsac_use(i->ac, spa->i.max_storage, + i->ac_chunk_size); + else + spa->storage = lws_malloc(spa->i.max_storage, "spa"); - spa->storage = lws_malloc(max_storage, "spa"); if (!spa->storage) goto bail2; - spa->end = spa->storage + max_storage - 1; - if (count_params) { - spa->params = lws_zalloc(sizeof(char *) * count_params, "spa params"); + spa->end = spa->storage + i->max_storage - 1; + + if (i->count_params) { + if (i->ac) + spa->params = lwsac_use_zero(i->ac, + sizeof(char *) * i->count_params, i->ac_chunk_size); + else + spa->params = lws_zalloc(sizeof(char *) * i->count_params, + "spa params"); if (!spa->params) goto bail3; } - spa->s = lws_urldecode_s_create(wsi, spa->storage, max_storage, spa, + spa->s = lws_urldecode_s_create(spa, wsi, spa->storage, i->max_storage, lws_urldecode_spa_cb); if (!spa->s) goto bail4; - if (count_params) { - spa->param_length = lws_zalloc(sizeof(int) * count_params, + if (i->count_params) { + if (i->ac) + spa->param_length = lwsac_use_zero(i->ac, + sizeof(int) * i->count_params, i->ac_chunk_size); + else + spa->param_length = lws_zalloc(sizeof(int) * i->count_params, "spa param len"); if (!spa->param_length) goto bail5; @@ -536,57 +569,80 @@ lws_spa_create(struct lws *wsi, const char * const *param_names, return spa; bail5: - lws_urldecode_s_destroy(spa->s); + lws_urldecode_s_destroy(spa, spa->s); bail4: - lws_free(spa->params); + if (!i->ac) + lws_free(spa->params); bail3: - lws_free(spa->storage); + if (!i->ac) + lws_free(spa->storage); bail2: - lws_free(spa); + if (!i->ac) + lws_free(spa); + + if (i->ac) + lwsac_free(i->ac); return NULL; } -LWS_VISIBLE LWS_EXTERN int -lws_spa_process(struct lws_spa *ludspa, const char *in, int len) +struct lws_spa * +lws_spa_create(struct lws *wsi, const char * const *param_names, + int count_params, int max_storage, + lws_spa_fileupload_cb opt_cb, void *opt_data) { - if (!ludspa) { + lws_spa_create_info_t i; + + memset(&i, 0, sizeof(i)); + i.count_params = count_params; + i.max_storage = max_storage; + i.opt_cb = opt_cb; + i.opt_data = opt_data; + i.param_names = param_names; + + return lws_spa_create_via_info(wsi, &i); +} + +int +lws_spa_process(struct lws_spa *spa, const char *in, int len) +{ + if (!spa) { lwsl_err("%s: NULL spa\n", __func__); return -1; } /* we reject any junk after the last part arrived and we finalized */ - if (ludspa->finalized) + if (spa->finalized) return 0; - return lws_urldecode_s_process(ludspa->s, in, len); + return lws_urldecode_s_process(spa->s, in, len); } -LWS_VISIBLE LWS_EXTERN int -lws_spa_get_length(struct lws_spa *ludspa, int n) +int +lws_spa_get_length(struct lws_spa *spa, int n) { - if (n >= ludspa->count_params) + if (n >= spa->i.count_params) return 0; - return ludspa->param_length[n]; + return spa->param_length[n]; } -LWS_VISIBLE LWS_EXTERN const char * -lws_spa_get_string(struct lws_spa *ludspa, int n) +const char * +lws_spa_get_string(struct lws_spa *spa, int n) { - if (n >= ludspa->count_params) + if (n >= spa->i.count_params) return NULL; - return ludspa->params[n]; + return spa->params[n]; } -LWS_VISIBLE LWS_EXTERN int +int lws_spa_finalize(struct lws_spa *spa) { if (!spa) return 0; if (spa->s) { - lws_urldecode_s_destroy(spa->s); + lws_urldecode_s_destroy(spa, spa->s); spa->s = NULL; } @@ -595,7 +651,7 @@ lws_spa_finalize(struct lws_spa *spa) return 0; } -LWS_VISIBLE LWS_EXTERN int +int lws_spa_destroy(struct lws_spa *spa) { int n = 0; @@ -603,15 +659,16 @@ lws_spa_destroy(struct lws_spa *spa) lwsl_info("%s: destroy spa %p\n", __func__, spa); if (spa->s) - lws_urldecode_s_destroy(spa->s); + lws_urldecode_s_destroy(spa, spa->s); - lwsl_debug("%s %p %p %p %p\n", __func__, spa->param_length, - spa->params, spa->storage, spa); - - lws_free(spa->param_length); - lws_free(spa->params); - lws_free(spa->storage); - lws_free(spa); + if (spa->i.ac) + lwsac_free(spa->i.ac); + else { + lws_free(spa->param_length); + lws_free(spa->params); + lws_free(spa->storage); + lws_free(spa); + } return n; } diff --git a/minimal-examples/http-server/minimal-http-server-form-post-lwsac/CMakeLists.txt b/minimal-examples/http-server/minimal-http-server-form-post-lwsac/CMakeLists.txt new file mode 100644 index 000000000..eec5b06a8 --- /dev/null +++ b/minimal-examples/http-server/minimal-http-server-form-post-lwsac/CMakeLists.txt @@ -0,0 +1,77 @@ +cmake_minimum_required(VERSION 2.8) +include(CheckCSourceCompiles) + +set(SAMP lws-minimal-http-server-form-post-lwsac) +set(SRCS minimal-http-server-form-post.c) + +# If we are being built as part of lws, confirm current build config supports +# reqconfig, else skip building ourselves. +# +# If we are being built externally, confirm installed lws was configured to +# support reqconfig, else error out with a helpful message about the problem. +# +MACRO(require_lws_config reqconfig _val result) + + if (DEFINED ${reqconfig}) + if (${reqconfig}) + set (rq 1) + else() + set (rq 0) + endif() + else() + set(rq 0) + endif() + + if (${_val} EQUAL ${rq}) + set(SAME 1) + else() + set(SAME 0) + endif() + + if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) + if (${_val}) + message("${SAMP}: skipping as lws being built without ${reqconfig}") + else() + message("${SAMP}: skipping as lws built with ${reqconfig}") + endif() + set(${result} 0) + else() + if (LWS_WITH_MINIMAL_EXAMPLES) + set(MET ${SAME}) + else() + CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) + if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) + set(HAS_${reqconfig} 0) + else() + set(HAS_${reqconfig} 1) + endif() + if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) + set(MET 1) + else() + set(MET 0) + endif() + endif() + if (NOT MET) + if (${_val}) + message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") + else() + message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") + endif() + endif() + endif() +ENDMACRO() + +set(requirements 1) +require_lws_config(LWS_ROLE_H1 1 requirements) +require_lws_config(LWS_WITHOUT_SERVER 0 requirements) + +if (requirements) + add_executable(${SAMP} ${SRCS}) + + if (websockets_shared) + target_link_libraries(${SAMP} websockets_shared) + add_dependencies(${SAMP} websockets_shared) + else() + target_link_libraries(${SAMP} websockets) + endif() +endif() diff --git a/minimal-examples/http-server/minimal-http-server-form-post-lwsac/README.md b/minimal-examples/http-server/minimal-http-server-form-post-lwsac/README.md new file mode 100644 index 000000000..910b4ceca --- /dev/null +++ b/minimal-examples/http-server/minimal-http-server-form-post-lwsac/README.md @@ -0,0 +1,23 @@ +# lws minimal http server form POST lwsac + +Shows how to parse the form using an lwsac to hold the form data + +## build + +``` + $ cmake . && make +``` + +## usage + +``` + $ ./lws-minimal-http-server-form-post-lwsac +[2018/03/29 08:29:41:7044] USER: LWS minimal http server form POST | visit http://localhost:7681 +[2018/03/29 08:29:41:7044] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off +[2018/03/29 08:29:49:8601] USER: text1: (len 4) 'xxxx' +[2018/03/29 08:29:49:8601] USER: send: (len 6) 'Submit' +``` + +Visit http://localhost:7681, submit the form. + +The form parameters are dumped to the log and you are redirected to a different page. diff --git a/minimal-examples/http-server/minimal-http-server-form-post-lwsac/localhost-100y.cert b/minimal-examples/http-server/minimal-http-server-form-post-lwsac/localhost-100y.cert new file mode 100644 index 000000000..6f372db40 --- /dev/null +++ b/minimal-examples/http-server/minimal-http-server-form-post-lwsac/localhost-100y.cert @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +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 +e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= +-----END CERTIFICATE----- diff --git a/minimal-examples/http-server/minimal-http-server-form-post-lwsac/localhost-100y.key b/minimal-examples/http-server/minimal-http-server-form-post-lwsac/localhost-100y.key new file mode 100644 index 000000000..148f8598e --- /dev/null +++ b/minimal-examples/http-server/minimal-http-server-form-post-lwsac/localhost-100y.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +fBrpEY1IATtPq1taBZZogRqI3rOkkPk= +-----END PRIVATE KEY----- diff --git a/minimal-examples/http-server/minimal-http-server-form-post-lwsac/minimal-http-server-form-post.c b/minimal-examples/http-server/minimal-http-server-form-post-lwsac/minimal-http-server-form-post.c new file mode 100644 index 000000000..1616d0718 --- /dev/null +++ b/minimal-examples/http-server/minimal-http-server-form-post-lwsac/minimal-http-server-form-post.c @@ -0,0 +1,217 @@ +/* + * lws-minimal-http-server-form-post-lwsac + * + * Copyright (C) 2018 Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * This demonstrates a minimal http server that performs POST with a couple + * of parameters. It dumps the parameters to the console log and redirects + * to another page. + */ + +#include +#include +#include + +/* + * Unlike ws, http is a stateless protocol. This pss only exists for the + * duration of a single http transaction. With http/1.1 keep-alive and http/2, + * that is unrelated to (shorter than) the lifetime of the network connection. + */ +struct pss { + struct lws_spa *spa; + struct lwsac *ac; +}; + +static int interrupted; + +static const char * const param_names[] = { + "text1", + "send", +}; + +enum enum_param_names { + EPN_TEXT1, + EPN_SEND, +}; + +static int +callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, + void *in, size_t len) +{ + struct pss *pss = (struct pss *)user; + uint8_t buf[LWS_PRE + LWS_RECOMMENDED_MIN_HEADER_SPACE], *start = &buf[LWS_PRE], + *p = start, *end = &buf[sizeof(buf) - 1]; + int n; + + switch (reason) { + case LWS_CALLBACK_HTTP: + + /* + * Manually report that our form target URL exists + * + * you can also do this by adding a mount for the form URL + * to the protocol with type LWSMPRO_CALLBACK, then no need + * to trap LWS_CALLBACK_HTTP. + */ + + if (!strcmp((const char *)in, "/form1")) + /* assertively allow it to exist in the URL space */ + return 0; + + /* default to 404-ing the URL if not mounted */ + break; + + case LWS_CALLBACK_HTTP_BODY: + + /* create the POST argument parser if not already existing */ + + if (!pss->spa) { + lws_spa_create_info_t i; + + memset(&i, 0, sizeof(i)); + i.param_names = param_names; + i.count_params = LWS_ARRAY_SIZE(param_names); + i.ac = &pss->ac; + i.ac_chunk_size = 512; + + pss->spa = lws_spa_create_via_info(wsi, &i); /* no file upload */ + if (!pss->spa) + return -1; + } + + /* let it parse the POST data */ + + if (lws_spa_process(pss->spa, in, (int)len)) + return -1; + break; + + case LWS_CALLBACK_HTTP_BODY_COMPLETION: + + /* inform the spa no more payload data coming */ + + lwsl_user("LWS_CALLBACK_HTTP_BODY_COMPLETION\n"); + lws_spa_finalize(pss->spa); + + /* we just dump the decoded things to the log */ + + for (n = 0; n < (int)LWS_ARRAY_SIZE(param_names); n++) { + if (!lws_spa_get_string(pss->spa, n)) + lwsl_user("%s: undefined\n", param_names[n]); + else + lwsl_user("%s: (len %d) '%s'\n", + param_names[n], + lws_spa_get_length(pss->spa, n), + lws_spa_get_string(pss->spa, n)); + } + + lwsac_free(&pss->ac); + + /* + * Our response is to redirect to a static page. We could + * have generated a dynamic html page here instead. + */ + + if (lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY, + (unsigned char *)"after-form1.html", + 16, &p, end) < 0) + return -1; + break; + + case LWS_CALLBACK_HTTP_DROP_PROTOCOL: + /* called when our wsi user_space is going to be destroyed */ + if (pss->spa) { + lws_spa_destroy(pss->spa); + pss->spa = NULL; + } + lwsac_free(&pss->ac); + break; + + default: + break; + } + + return lws_callback_http_dummy(wsi, reason, user, in, len); +} + +static struct lws_protocols protocols[] = { + { "http", callback_http, sizeof(struct pss), 0 }, + { NULL, NULL, 0, 0 } /* terminator */ +}; + +/* default mount serves the URL space from ./mount-origin */ + +static const struct lws_http_mount mount = { + /* .mount_next */ NULL, /* linked-list "next" */ + /* .mountpoint */ "/", /* mountpoint URL */ + /* .origin */ "./mount-origin", /* serve from dir */ + /* .def */ "index.html", /* default filename */ + /* .protocol */ NULL, + /* .cgienv */ NULL, + /* .extra_mimetypes */ NULL, + /* .interpret */ NULL, + /* .cgi_timeout */ 0, + /* .cache_max_age */ 0, + /* .auth_mask */ 0, + /* .cache_reusable */ 0, + /* .cache_revalidate */ 0, + /* .cache_intermediaries */ 0, + /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ + /* .mountpoint_len */ 1, /* char count */ + /* .basic_auth_login_file */ NULL, +}; + +void sigint_handler(int sig) +{ + interrupted = 1; +} + +int main(int argc, const char **argv) +{ + struct lws_context_creation_info info; + struct lws_context *context; + const char *p; + int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE + /* for LLL_ verbosity above NOTICE to be built into lws, + * lws must have been configured and built with + * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ + /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ + /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ + /* | LLL_DEBUG */; + + signal(SIGINT, sigint_handler); + + if ((p = lws_cmdline_option(argc, argv, "-d"))) + logs = atoi(p); + + lws_set_log_level(logs, NULL); + lwsl_user("LWS minimal http server POST | visit http://localhost:7681\n"); + + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ + info.port = 7681; + info.protocols = protocols; + info.mounts = &mount; + info.options = + LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; + + if (lws_cmdline_option(argc, argv, "-s")) { + info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + info.ssl_cert_filepath = "localhost-100y.cert"; + info.ssl_private_key_filepath = "localhost-100y.key"; + } + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + while (n >= 0 && !interrupted) + n = lws_service(context, 1000); + + lws_context_destroy(context); + + return 0; +}