2017-10-16 12:52:32 +08:00
|
|
|
/*
|
2019-08-14 10:44:14 +01:00
|
|
|
* libwebsockets - small server side websockets and web server implementation
|
2017-10-16 12:52:32 +08:00
|
|
|
*
|
2019-08-14 10:44:14 +01:00
|
|
|
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
2017-10-16 12:52:32 +08:00
|
|
|
*
|
2019-08-14 10:44:14 +01:00
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to
|
|
|
|
* deal in the Software without restriction, including without limitation the
|
|
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
2017-10-16 12:52:32 +08:00
|
|
|
*
|
2019-08-14 10:44:14 +01:00
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
2017-10-16 12:52:32 +08:00
|
|
|
*
|
2019-08-14 10:44:14 +01:00
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
|
|
* IN THE SOFTWARE.
|
2017-10-16 12:52:32 +08:00
|
|
|
*/
|
|
|
|
|
2019-12-22 03:38:05 +00:00
|
|
|
#if !defined(_GNU_SOURCE)
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#endif
|
2019-06-07 11:11:46 +01:00
|
|
|
|
2019-08-15 10:49:52 +01:00
|
|
|
#include "private-lib-core.h"
|
2017-10-16 12:52:32 +08:00
|
|
|
|
|
|
|
#if defined(WIN32) || defined(_WIN32)
|
|
|
|
#else
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static const char *hex = "0123456789ABCDEF";
|
|
|
|
|
|
|
|
static int
|
|
|
|
urlencode(const char *in, int inlen, char *out, int outlen)
|
|
|
|
{
|
|
|
|
char *start = out, *end = out + outlen;
|
|
|
|
|
|
|
|
while (inlen-- && out < end - 4) {
|
|
|
|
if ((*in >= 'A' && *in <= 'Z') ||
|
|
|
|
(*in >= 'a' && *in <= 'z') ||
|
|
|
|
(*in >= '0' && *in <= '9') ||
|
|
|
|
*in == '-' ||
|
|
|
|
*in == '_' ||
|
|
|
|
*in == '.' ||
|
|
|
|
*in == '~') {
|
|
|
|
*out++ = *in++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (*in == ' ') {
|
|
|
|
*out++ = '+';
|
|
|
|
in++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
*out++ = '%';
|
|
|
|
*out++ = hex[(*in) >> 4];
|
|
|
|
*out++ = hex[(*in++) & 15];
|
|
|
|
}
|
|
|
|
*out = '\0';
|
|
|
|
|
|
|
|
if (out >= end - 4)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return out - start;
|
|
|
|
}
|
|
|
|
|
2020-07-17 15:35:28 +01:00
|
|
|
static void
|
|
|
|
lws_cgi_grace(lws_sorted_usec_list_t *sul)
|
|
|
|
{
|
|
|
|
struct lws_cgi *cgi = lws_container_of(sul, struct lws_cgi, sul_grace);
|
|
|
|
|
|
|
|
/* act on the reap cb from earlier */
|
|
|
|
|
2020-08-02 08:20:35 +01:00
|
|
|
lwsl_info("%s: wsi %p\n", __func__, cgi->wsi);
|
2020-07-17 15:35:28 +01:00
|
|
|
|
|
|
|
if (!cgi->wsi->http.cgi->post_in_expected)
|
|
|
|
cgi->wsi->http.cgi->cgi_transaction_over = 1;
|
|
|
|
|
|
|
|
lws_callback_on_writable(cgi->wsi);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
lws_cgi_reap_cb(void *opaque, lws_usec_t *accounting, siginfo_t *si,
|
|
|
|
int we_killed_him)
|
|
|
|
{
|
|
|
|
struct lws *wsi = (struct lws *)opaque;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The cgi has come to an end, by itself or with a signal...
|
|
|
|
*/
|
|
|
|
|
2020-08-02 08:20:35 +01:00
|
|
|
lwsl_info("%s: wsi %p post_in_expected %d\n", __func__, wsi,
|
2020-07-17 15:35:28 +01:00
|
|
|
(int)wsi->http.cgi->post_in_expected);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Grace period to handle the incoming stdout
|
|
|
|
*/
|
|
|
|
|
fakewsi: replace with smaller substructure
Currently we always reserve a fakewsi per pt so events that don't have a related actual
wsi, like vhost-protocol-init or vhost cert init via protocol callback can make callbacks
that look reasonable to user protocol handler code expecting a valid wsi every time.
This patch splits out stuff that user callbacks often unconditionally expect to be in
a wsi, like context pointer, vhost pointer etc into a substructure, which is composed
into struct lws at the top of it. Internal references (struct lws is opaque, so there
are only internal references) are all updated to go via the substructre, the compiler
should make that a NOP.
Helpers are added when fakewsi is used and referenced.
If not PLAT_FREERTOS, we continue to provide a full fakewsi in the pt as before,
although the helpers improve consistency by zeroing down the substructure. There is
a huge amount of user code out there over the last 10 years that did not always have
the minimal examples to follow, some of it does some unexpected things.
If it is PLAT_FREERTOS, that is a newer thing in lws and users have the benefit of
being able to follow the minimal examples' approach. For PLAT_FREERTOS we don't
reserve the fakewsi in the pt any more, saving around 800 bytes. The helpers then
create a struct lws_a (the substructure) on the stack, zero it down (but it is only
like 4 pointers) and prepare it with whatever we know like the context.
Then we cast it to a struct lws * and use it in the user protocol handler call.
In this case, the remainder of the struct lws is undefined. However the amount of
old protocol handlers that might touch things outside of the substructure in
PLAT_FREERTOS is very limited compared to legacy lws user code and the saving is
significant on constrained devices.
User handlers should not be touching everything in a wsi every time anyway, there
are several cases where there is no valid wsi to do the call with. Dereference of
things outside the substructure should only happen when the callback reason shows
there is a valid wsi bound to the activity (as in all the minimal examples).
2020-07-19 08:33:46 +01:00
|
|
|
lws_sul_schedule(wsi->a.context, wsi->tsi, &wsi->http.cgi->sul_grace,
|
2020-07-17 15:35:28 +01:00
|
|
|
lws_cgi_grace, 1 * LWS_US_PER_SEC);
|
|
|
|
}
|
|
|
|
|
2020-01-02 08:32:23 +00:00
|
|
|
int
|
2018-11-23 08:47:56 +08:00
|
|
|
lws_cgi(struct lws *wsi, const char * const *exec_array,
|
|
|
|
int script_uri_path_len, int timeout_secs,
|
|
|
|
const struct lws_protocol_vhost_options *mp_cgienv)
|
2017-10-16 12:52:32 +08:00
|
|
|
{
|
fakewsi: replace with smaller substructure
Currently we always reserve a fakewsi per pt so events that don't have a related actual
wsi, like vhost-protocol-init or vhost cert init via protocol callback can make callbacks
that look reasonable to user protocol handler code expecting a valid wsi every time.
This patch splits out stuff that user callbacks often unconditionally expect to be in
a wsi, like context pointer, vhost pointer etc into a substructure, which is composed
into struct lws at the top of it. Internal references (struct lws is opaque, so there
are only internal references) are all updated to go via the substructre, the compiler
should make that a NOP.
Helpers are added when fakewsi is used and referenced.
If not PLAT_FREERTOS, we continue to provide a full fakewsi in the pt as before,
although the helpers improve consistency by zeroing down the substructure. There is
a huge amount of user code out there over the last 10 years that did not always have
the minimal examples to follow, some of it does some unexpected things.
If it is PLAT_FREERTOS, that is a newer thing in lws and users have the benefit of
being able to follow the minimal examples' approach. For PLAT_FREERTOS we don't
reserve the fakewsi in the pt any more, saving around 800 bytes. The helpers then
create a struct lws_a (the substructure) on the stack, zero it down (but it is only
like 4 pointers) and prepare it with whatever we know like the context.
Then we cast it to a struct lws * and use it in the user protocol handler call.
In this case, the remainder of the struct lws is undefined. However the amount of
old protocol handlers that might touch things outside of the substructure in
PLAT_FREERTOS is very limited compared to legacy lws user code and the saving is
significant on constrained devices.
User handlers should not be touching everything in a wsi every time anyway, there
are several cases where there is no valid wsi to do the call with. Dereference of
things outside the substructure should only happen when the callback reason shows
there is a valid wsi bound to the activity (as in all the minimal examples).
2020-07-19 08:33:46 +01:00
|
|
|
struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
|
2020-02-12 10:12:39 +00:00
|
|
|
struct lws_spawn_piped_info info;
|
2018-05-18 08:39:44 +08:00
|
|
|
char *env_array[30], cgi_path[500], e[1024], *p = e,
|
2018-01-04 12:07:56 +08:00
|
|
|
*end = p + sizeof(e) - 1, tok[256], *t, *sum, *sumend;
|
2017-10-16 12:52:32 +08:00
|
|
|
struct lws_cgi *cgi;
|
2018-05-18 08:39:44 +08:00
|
|
|
int n, m = 0, i, uritok = -1, c;
|
2017-10-16 12:52:32 +08:00
|
|
|
|
|
|
|
/*
|
2020-10-11 07:43:46 +01:00
|
|
|
* give the cgi stream wsi a cgi struct
|
2017-10-16 12:52:32 +08:00
|
|
|
*/
|
|
|
|
|
2018-04-27 19:16:50 +08:00
|
|
|
wsi->http.cgi = lws_zalloc(sizeof(*wsi->http.cgi), "new cgi");
|
|
|
|
if (!wsi->http.cgi) {
|
2017-10-16 12:52:32 +08:00
|
|
|
lwsl_err("%s: OOM\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-04-27 19:16:50 +08:00
|
|
|
wsi->http.cgi->response_code = HTTP_STATUS_OK;
|
2017-10-16 12:52:32 +08:00
|
|
|
|
2018-04-27 19:16:50 +08:00
|
|
|
cgi = wsi->http.cgi;
|
2017-10-16 12:52:32 +08:00
|
|
|
cgi->wsi = wsi; /* set cgi's owning wsi */
|
2018-01-04 12:07:56 +08:00
|
|
|
sum = cgi->summary;
|
|
|
|
sumend = sum + strlen(cgi->summary) - 1;
|
2017-10-16 12:52:32 +08:00
|
|
|
|
|
|
|
if (timeout_secs)
|
|
|
|
lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, timeout_secs);
|
|
|
|
|
|
|
|
/* the cgi stdout is always sending us http1.x header data first */
|
|
|
|
wsi->hdr_state = LCHS_HEADER;
|
|
|
|
|
|
|
|
/* add us to the pt list of active cgis */
|
2018-04-27 19:16:50 +08:00
|
|
|
lwsl_debug("%s: adding cgi %p to list\n", __func__, wsi->http.cgi);
|
2018-04-27 15:20:56 +08:00
|
|
|
cgi->cgi_list = pt->http.cgi_list;
|
|
|
|
pt->http.cgi_list = cgi;
|
2017-10-16 12:52:32 +08:00
|
|
|
|
2018-01-04 12:07:56 +08:00
|
|
|
sum += lws_snprintf(sum, sumend - sum, "%s ", exec_array[0]);
|
|
|
|
|
2018-06-16 09:35:07 +08:00
|
|
|
if (0) {
|
2018-11-23 08:47:56 +08:00
|
|
|
char *pct = lws_hdr_simple_ptr(wsi,
|
|
|
|
WSI_TOKEN_HTTP_CONTENT_ENCODING);
|
2018-06-16 09:35:07 +08:00
|
|
|
|
|
|
|
if (pct && !strcmp(pct, "gzip"))
|
|
|
|
wsi->http.cgi->gzip_inflate = 1;
|
|
|
|
}
|
|
|
|
|
2017-10-16 12:52:32 +08:00
|
|
|
/* prepare his CGI env */
|
|
|
|
|
|
|
|
n = 0;
|
|
|
|
|
2019-10-07 16:57:58 +08:00
|
|
|
if (lws_is_ssl(wsi)) {
|
|
|
|
env_array[n++] = p;
|
|
|
|
p += lws_snprintf(p, end - p, "HTTPS=ON");
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
|
2018-04-27 15:20:56 +08:00
|
|
|
if (wsi->http.ah) {
|
2017-10-16 12:52:32 +08:00
|
|
|
static const unsigned char meths[] = {
|
|
|
|
WSI_TOKEN_GET_URI,
|
|
|
|
WSI_TOKEN_POST_URI,
|
2020-02-28 10:31:04 +00:00
|
|
|
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
|
2017-10-16 12:52:32 +08:00
|
|
|
WSI_TOKEN_OPTIONS_URI,
|
|
|
|
WSI_TOKEN_PUT_URI,
|
|
|
|
WSI_TOKEN_PATCH_URI,
|
|
|
|
WSI_TOKEN_DELETE_URI,
|
2020-02-28 10:31:04 +00:00
|
|
|
#endif
|
2017-10-16 12:52:32 +08:00
|
|
|
WSI_TOKEN_CONNECT,
|
|
|
|
WSI_TOKEN_HEAD_URI,
|
|
|
|
#ifdef LWS_WITH_HTTP2
|
|
|
|
WSI_TOKEN_HTTP_COLON_PATH,
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
static const char * const meth_names[] = {
|
2020-02-28 10:31:04 +00:00
|
|
|
"GET", "POST",
|
|
|
|
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
|
|
|
|
"OPTIONS", "PUT", "PATCH", "DELETE",
|
|
|
|
#endif
|
2018-05-18 08:39:44 +08:00
|
|
|
"CONNECT", "HEAD", ":path"
|
2017-10-16 12:52:32 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
if (script_uri_path_len >= 0)
|
2018-08-16 19:10:32 +08:00
|
|
|
for (m = 0; m < (int)LWS_ARRAY_SIZE(meths); m++)
|
2017-10-16 12:52:32 +08:00
|
|
|
if (lws_hdr_total_length(wsi, meths[m]) >=
|
|
|
|
script_uri_path_len) {
|
|
|
|
uritok = meths[m];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (script_uri_path_len < 0 && uritok < 0)
|
2020-02-12 10:12:39 +00:00
|
|
|
goto bail;
|
2017-10-16 12:52:32 +08:00
|
|
|
// if (script_uri_path_len < 0)
|
|
|
|
// uritok = 0;
|
|
|
|
|
|
|
|
if (m >= 0) {
|
|
|
|
env_array[n++] = p;
|
2020-03-10 06:45:24 +00:00
|
|
|
if (m < (int)LWS_ARRAY_SIZE(meths) - 1) {
|
2017-10-16 12:52:32 +08:00
|
|
|
p += lws_snprintf(p, end - p,
|
|
|
|
"REQUEST_METHOD=%s",
|
|
|
|
meth_names[m]);
|
2018-05-18 08:39:44 +08:00
|
|
|
sum += lws_snprintf(sum, sumend - sum, "%s ",
|
|
|
|
meth_names[m]);
|
2020-02-28 10:31:04 +00:00
|
|
|
#if defined(LWS_ROLE_H2)
|
2018-01-04 12:07:56 +08:00
|
|
|
} else {
|
2017-10-16 12:52:32 +08:00
|
|
|
p += lws_snprintf(p, end - p,
|
|
|
|
"REQUEST_METHOD=%s",
|
|
|
|
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD));
|
2018-01-04 12:07:56 +08:00
|
|
|
sum += lws_snprintf(sum, sumend - sum, "%s ",
|
2018-05-18 08:39:44 +08:00
|
|
|
lws_hdr_simple_ptr(wsi,
|
|
|
|
WSI_TOKEN_HTTP_COLON_METHOD));
|
2020-02-28 10:31:04 +00:00
|
|
|
#endif
|
2018-01-04 12:07:56 +08:00
|
|
|
}
|
2017-10-16 12:52:32 +08:00
|
|
|
p++;
|
|
|
|
}
|
|
|
|
|
2018-01-04 12:07:56 +08:00
|
|
|
if (uritok >= 0)
|
|
|
|
sum += lws_snprintf(sum, sumend - sum, "%s ",
|
2018-05-18 08:39:44 +08:00
|
|
|
lws_hdr_simple_ptr(wsi, uritok));
|
2018-01-04 12:07:56 +08:00
|
|
|
|
2017-10-16 12:52:32 +08:00
|
|
|
env_array[n++] = p;
|
|
|
|
p += lws_snprintf(p, end - p, "QUERY_STRING=");
|
|
|
|
/* dump the individual URI Arg parameters */
|
|
|
|
m = 0;
|
|
|
|
while (script_uri_path_len >= 0) {
|
|
|
|
i = lws_hdr_copy_fragment(wsi, tok, sizeof(tok),
|
|
|
|
WSI_TOKEN_HTTP_URI_ARGS, m);
|
|
|
|
if (i < 0)
|
|
|
|
break;
|
|
|
|
t = tok;
|
|
|
|
while (*t && *t != '=' && p < end - 4)
|
|
|
|
*p++ = *t++;
|
|
|
|
if (*t == '=')
|
|
|
|
*p++ = *t++;
|
|
|
|
i = urlencode(t, i- (t - tok), p, end - p);
|
|
|
|
if (i > 0) {
|
|
|
|
p += i;
|
|
|
|
*p++ = '&';
|
|
|
|
}
|
|
|
|
m++;
|
|
|
|
}
|
|
|
|
if (m)
|
|
|
|
p--;
|
|
|
|
*p++ = '\0';
|
|
|
|
|
2018-05-18 08:39:44 +08:00
|
|
|
if (uritok >= 0) {
|
|
|
|
strcpy(cgi_path, "REQUEST_URI=");
|
|
|
|
c = lws_hdr_copy(wsi, cgi_path + 12,
|
|
|
|
sizeof(cgi_path) - 12, uritok);
|
|
|
|
if (c < 0)
|
2020-02-12 10:12:39 +00:00
|
|
|
goto bail;
|
2018-05-18 08:39:44 +08:00
|
|
|
|
|
|
|
cgi_path[sizeof(cgi_path) - 1] = '\0';
|
|
|
|
env_array[n++] = cgi_path;
|
|
|
|
}
|
|
|
|
|
2018-01-04 12:07:56 +08:00
|
|
|
sum += lws_snprintf(sum, sumend - sum, "%s", env_array[n - 1]);
|
|
|
|
|
2017-10-16 12:52:32 +08:00
|
|
|
if (script_uri_path_len >= 0) {
|
|
|
|
env_array[n++] = p;
|
|
|
|
p += lws_snprintf(p, end - p, "PATH_INFO=%s",
|
2018-05-18 08:39:44 +08:00
|
|
|
cgi_path + 12 + script_uri_path_len);
|
2017-10-16 12:52:32 +08:00
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
2020-02-28 10:31:04 +00:00
|
|
|
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
|
2017-10-16 12:52:32 +08:00
|
|
|
if (script_uri_path_len >= 0 &&
|
|
|
|
lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_REFERER)) {
|
|
|
|
env_array[n++] = p;
|
|
|
|
p += lws_snprintf(p, end - p, "HTTP_REFERER=%s",
|
|
|
|
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_REFERER));
|
|
|
|
p++;
|
|
|
|
}
|
2020-02-28 10:31:04 +00:00
|
|
|
#endif
|
2017-10-16 12:52:32 +08:00
|
|
|
if (script_uri_path_len >= 0 &&
|
|
|
|
lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
|
|
|
|
env_array[n++] = p;
|
|
|
|
p += lws_snprintf(p, end - p, "HTTP_HOST=%s",
|
|
|
|
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
if (script_uri_path_len >= 0 &&
|
|
|
|
lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) {
|
|
|
|
env_array[n++] = p;
|
2017-12-01 11:07:00 +08:00
|
|
|
p += lws_snprintf(p, end - p, "HTTP_COOKIE=");
|
|
|
|
m = lws_hdr_copy(wsi, p, end - p, WSI_TOKEN_HTTP_COOKIE);
|
|
|
|
if (m > 0)
|
|
|
|
p += lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE);
|
|
|
|
*p++ = '\0';
|
2017-10-16 12:52:32 +08:00
|
|
|
}
|
2020-02-28 10:31:04 +00:00
|
|
|
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
|
2017-10-16 12:52:32 +08:00
|
|
|
if (script_uri_path_len >= 0 &&
|
|
|
|
lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT)) {
|
|
|
|
env_array[n++] = p;
|
2018-06-16 09:35:07 +08:00
|
|
|
p += lws_snprintf(p, end - p, "HTTP_USER_AGENT=%s",
|
2017-10-16 12:52:32 +08:00
|
|
|
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_USER_AGENT));
|
|
|
|
p++;
|
|
|
|
}
|
2020-02-28 10:31:04 +00:00
|
|
|
#endif
|
2018-06-16 09:35:07 +08:00
|
|
|
if (script_uri_path_len >= 0 &&
|
|
|
|
lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_ENCODING)) {
|
|
|
|
env_array[n++] = p;
|
|
|
|
p += lws_snprintf(p, end - p, "HTTP_CONTENT_ENCODING=%s",
|
2018-11-23 08:47:56 +08:00
|
|
|
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_ENCODING));
|
2018-06-16 09:35:07 +08:00
|
|
|
p++;
|
|
|
|
}
|
|
|
|
if (script_uri_path_len >= 0 &&
|
|
|
|
lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT)) {
|
|
|
|
env_array[n++] = p;
|
|
|
|
p += lws_snprintf(p, end - p, "HTTP_ACCEPT=%s",
|
|
|
|
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT));
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
if (script_uri_path_len >= 0 &&
|
|
|
|
lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING)) {
|
|
|
|
env_array[n++] = p;
|
|
|
|
p += lws_snprintf(p, end - p, "HTTP_ACCEPT_ENCODING=%s",
|
2018-11-23 08:47:56 +08:00
|
|
|
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING));
|
2018-06-16 09:35:07 +08:00
|
|
|
p++;
|
|
|
|
}
|
2017-10-16 12:52:32 +08:00
|
|
|
if (script_uri_path_len >= 0 &&
|
|
|
|
uritok == WSI_TOKEN_POST_URI) {
|
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) {
|
|
|
|
env_array[n++] = p;
|
|
|
|
p += lws_snprintf(p, end - p, "CONTENT_TYPE=%s",
|
|
|
|
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE));
|
|
|
|
p++;
|
|
|
|
}
|
2018-06-16 09:35:07 +08:00
|
|
|
if (!wsi->http.cgi->gzip_inflate &&
|
|
|
|
lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
|
2017-10-16 12:52:32 +08:00
|
|
|
env_array[n++] = p;
|
|
|
|
p += lws_snprintf(p, end - p, "CONTENT_LENGTH=%s",
|
|
|
|
lws_hdr_simple_ptr(wsi,
|
|
|
|
WSI_TOKEN_HTTP_CONTENT_LENGTH));
|
|
|
|
p++;
|
|
|
|
}
|
2018-06-16 09:35:07 +08:00
|
|
|
|
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH))
|
|
|
|
wsi->http.cgi->post_in_expected =
|
2018-11-23 08:47:56 +08:00
|
|
|
atoll(lws_hdr_simple_ptr(wsi,
|
|
|
|
WSI_TOKEN_HTTP_CONTENT_LENGTH));
|
2017-10-16 12:52:32 +08:00
|
|
|
}
|
2018-06-16 09:35:07 +08:00
|
|
|
|
|
|
|
|
2019-10-07 16:57:58 +08:00
|
|
|
env_array[n++] = p;
|
|
|
|
p += lws_snprintf(p, end - p, "PATH=/bin:/usr/bin:/usr/local/bin:/var/www/cgi-bin");
|
|
|
|
p++;
|
2017-10-16 12:52:32 +08:00
|
|
|
|
|
|
|
env_array[n++] = p;
|
2019-10-07 16:57:58 +08:00
|
|
|
p += lws_snprintf(p, end - p, "SCRIPT_PATH=%s", exec_array[0]);
|
|
|
|
p++;
|
2017-10-16 12:52:32 +08:00
|
|
|
|
|
|
|
while (mp_cgienv) {
|
|
|
|
env_array[n++] = p;
|
|
|
|
p += lws_snprintf(p, end - p, "%s=%s", mp_cgienv->name,
|
|
|
|
mp_cgienv->value);
|
2018-06-16 09:35:07 +08:00
|
|
|
if (!strcmp(mp_cgienv->name, "GIT_PROJECT_ROOT")) {
|
|
|
|
wsi->http.cgi->implied_chunked = 1;
|
|
|
|
wsi->http.cgi->explicitly_chunked = 1;
|
|
|
|
}
|
2018-05-18 08:39:44 +08:00
|
|
|
lwsl_info(" Applying mount-specific cgi env '%s'\n",
|
2017-10-16 12:52:32 +08:00
|
|
|
env_array[n - 1]);
|
|
|
|
p++;
|
|
|
|
mp_cgienv = mp_cgienv->next;
|
|
|
|
}
|
|
|
|
|
2019-10-07 16:57:58 +08:00
|
|
|
env_array[n++] = p;
|
2020-02-12 10:12:39 +00:00
|
|
|
p += lws_snprintf(p, end - p, "SERVER_SOFTWARE=lws");
|
2019-10-07 16:57:58 +08:00
|
|
|
p++;
|
|
|
|
|
2017-10-16 12:52:32 +08:00
|
|
|
env_array[n] = NULL;
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
for (m = 0; m < n; m++)
|
2018-06-16 09:35:07 +08:00
|
|
|
lwsl_notice(" %s\n", env_array[m]);
|
2017-10-16 12:52:32 +08:00
|
|
|
#endif
|
|
|
|
|
2020-02-12 10:12:39 +00:00
|
|
|
memset(&info, 0, sizeof(info));
|
|
|
|
info.env_array = env_array;
|
|
|
|
info.exec_array = exec_array;
|
|
|
|
info.max_log_lines = 20000;
|
|
|
|
info.opt_parent = wsi;
|
|
|
|
info.timeout_us = 5 * 60 * LWS_US_PER_SEC;
|
|
|
|
info.tsi = wsi->tsi;
|
fakewsi: replace with smaller substructure
Currently we always reserve a fakewsi per pt so events that don't have a related actual
wsi, like vhost-protocol-init or vhost cert init via protocol callback can make callbacks
that look reasonable to user protocol handler code expecting a valid wsi every time.
This patch splits out stuff that user callbacks often unconditionally expect to be in
a wsi, like context pointer, vhost pointer etc into a substructure, which is composed
into struct lws at the top of it. Internal references (struct lws is opaque, so there
are only internal references) are all updated to go via the substructre, the compiler
should make that a NOP.
Helpers are added when fakewsi is used and referenced.
If not PLAT_FREERTOS, we continue to provide a full fakewsi in the pt as before,
although the helpers improve consistency by zeroing down the substructure. There is
a huge amount of user code out there over the last 10 years that did not always have
the minimal examples to follow, some of it does some unexpected things.
If it is PLAT_FREERTOS, that is a newer thing in lws and users have the benefit of
being able to follow the minimal examples' approach. For PLAT_FREERTOS we don't
reserve the fakewsi in the pt any more, saving around 800 bytes. The helpers then
create a struct lws_a (the substructure) on the stack, zero it down (but it is only
like 4 pointers) and prepare it with whatever we know like the context.
Then we cast it to a struct lws * and use it in the user protocol handler call.
In this case, the remainder of the struct lws is undefined. However the amount of
old protocol handlers that might touch things outside of the substructure in
PLAT_FREERTOS is very limited compared to legacy lws user code and the saving is
significant on constrained devices.
User handlers should not be touching everything in a wsi every time anyway, there
are several cases where there is no valid wsi to do the call with. Dereference of
things outside the substructure should only happen when the callback reason shows
there is a valid wsi bound to the activity (as in all the minimal examples).
2020-07-19 08:33:46 +01:00
|
|
|
info.vh = wsi->a.vhost;
|
2020-02-12 10:12:39 +00:00
|
|
|
info.ops = &role_ops_cgi;
|
|
|
|
info.plsp = &wsi->http.cgi->lsp;
|
2020-07-17 15:35:28 +01:00
|
|
|
info.opaque = wsi;
|
|
|
|
info.reap_cb = lws_cgi_reap_cb;
|
2020-02-12 10:12:39 +00:00
|
|
|
|
2017-10-16 12:52:32 +08:00
|
|
|
/*
|
|
|
|
* Actually having made the env, as a cgi we don't need the ah
|
|
|
|
* any more
|
|
|
|
*/
|
2020-02-12 10:12:39 +00:00
|
|
|
if (script_uri_path_len >= 0) {
|
2017-10-16 12:52:32 +08:00
|
|
|
lws_header_table_detach(wsi, 0);
|
2020-02-12 10:12:39 +00:00
|
|
|
info.disable_ctrlc = 1;
|
2017-10-16 12:52:32 +08:00
|
|
|
}
|
|
|
|
|
2020-02-12 10:12:39 +00:00
|
|
|
wsi->http.cgi->lsp = lws_spawn_piped(&info);
|
|
|
|
if (!wsi->http.cgi->lsp) {
|
|
|
|
lwsl_err("%s: spawn failed\n", __func__);
|
|
|
|
goto bail;
|
2017-10-16 12:52:32 +08:00
|
|
|
}
|
|
|
|
|
2020-02-12 10:12:39 +00:00
|
|
|
/* we are the parent process */
|
2017-10-16 12:52:32 +08:00
|
|
|
|
fakewsi: replace with smaller substructure
Currently we always reserve a fakewsi per pt so events that don't have a related actual
wsi, like vhost-protocol-init or vhost cert init via protocol callback can make callbacks
that look reasonable to user protocol handler code expecting a valid wsi every time.
This patch splits out stuff that user callbacks often unconditionally expect to be in
a wsi, like context pointer, vhost pointer etc into a substructure, which is composed
into struct lws at the top of it. Internal references (struct lws is opaque, so there
are only internal references) are all updated to go via the substructre, the compiler
should make that a NOP.
Helpers are added when fakewsi is used and referenced.
If not PLAT_FREERTOS, we continue to provide a full fakewsi in the pt as before,
although the helpers improve consistency by zeroing down the substructure. There is
a huge amount of user code out there over the last 10 years that did not always have
the minimal examples to follow, some of it does some unexpected things.
If it is PLAT_FREERTOS, that is a newer thing in lws and users have the benefit of
being able to follow the minimal examples' approach. For PLAT_FREERTOS we don't
reserve the fakewsi in the pt any more, saving around 800 bytes. The helpers then
create a struct lws_a (the substructure) on the stack, zero it down (but it is only
like 4 pointers) and prepare it with whatever we know like the context.
Then we cast it to a struct lws * and use it in the user protocol handler call.
In this case, the remainder of the struct lws is undefined. However the amount of
old protocol handlers that might touch things outside of the substructure in
PLAT_FREERTOS is very limited compared to legacy lws user code and the saving is
significant on constrained devices.
User handlers should not be touching everything in a wsi every time anyway, there
are several cases where there is no valid wsi to do the call with. Dereference of
things outside the substructure should only happen when the callback reason shows
there is a valid wsi bound to the activity (as in all the minimal examples).
2020-07-19 08:33:46 +01:00
|
|
|
wsi->a.context->count_cgi_spawned++;
|
2017-10-16 12:52:32 +08:00
|
|
|
|
2020-02-12 10:12:39 +00:00
|
|
|
/* inform cgi owner of the child PID */
|
fakewsi: replace with smaller substructure
Currently we always reserve a fakewsi per pt so events that don't have a related actual
wsi, like vhost-protocol-init or vhost cert init via protocol callback can make callbacks
that look reasonable to user protocol handler code expecting a valid wsi every time.
This patch splits out stuff that user callbacks often unconditionally expect to be in
a wsi, like context pointer, vhost pointer etc into a substructure, which is composed
into struct lws at the top of it. Internal references (struct lws is opaque, so there
are only internal references) are all updated to go via the substructre, the compiler
should make that a NOP.
Helpers are added when fakewsi is used and referenced.
If not PLAT_FREERTOS, we continue to provide a full fakewsi in the pt as before,
although the helpers improve consistency by zeroing down the substructure. There is
a huge amount of user code out there over the last 10 years that did not always have
the minimal examples to follow, some of it does some unexpected things.
If it is PLAT_FREERTOS, that is a newer thing in lws and users have the benefit of
being able to follow the minimal examples' approach. For PLAT_FREERTOS we don't
reserve the fakewsi in the pt any more, saving around 800 bytes. The helpers then
create a struct lws_a (the substructure) on the stack, zero it down (but it is only
like 4 pointers) and prepare it with whatever we know like the context.
Then we cast it to a struct lws * and use it in the user protocol handler call.
In this case, the remainder of the struct lws is undefined. However the amount of
old protocol handlers that might touch things outside of the substructure in
PLAT_FREERTOS is very limited compared to legacy lws user code and the saving is
significant on constrained devices.
User handlers should not be touching everything in a wsi every time anyway, there
are several cases where there is no valid wsi to do the call with. Dereference of
things outside the substructure should only happen when the callback reason shows
there is a valid wsi bound to the activity (as in all the minimal examples).
2020-07-19 08:33:46 +01:00
|
|
|
n = user_callback_handle_rxflow(wsi->a.protocol->callback, wsi,
|
2020-02-12 10:12:39 +00:00
|
|
|
LWS_CALLBACK_CGI_PROCESS_ATTACH,
|
|
|
|
wsi->user_space, NULL, cgi->lsp->child_pid);
|
|
|
|
(void)n;
|
2017-10-16 12:52:32 +08:00
|
|
|
|
2020-02-12 10:12:39 +00:00
|
|
|
return 0;
|
2017-10-16 12:52:32 +08:00
|
|
|
|
2020-02-12 10:12:39 +00:00
|
|
|
bail:
|
2020-07-17 15:35:28 +01:00
|
|
|
lws_sul_cancel(&wsi->http.cgi->sul_grace);
|
2018-04-27 19:16:50 +08:00
|
|
|
lws_free_set_NULL(wsi->http.cgi);
|
2017-10-16 12:52:32 +08:00
|
|
|
|
|
|
|
lwsl_err("%s: failed\n", __func__);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we have to parse out these headers in the CGI output */
|
|
|
|
|
|
|
|
static const char * const significant_hdr[SIGNIFICANT_HDR_COUNT] = {
|
|
|
|
"content-length: ",
|
|
|
|
"location: ",
|
|
|
|
"status: ",
|
|
|
|
"transfer-encoding: chunked",
|
2018-06-16 09:35:07 +08:00
|
|
|
"content-encoding: gzip",
|
2017-10-16 12:52:32 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
enum header_recode {
|
|
|
|
HR_NAME,
|
|
|
|
HR_WHITESPACE,
|
|
|
|
HR_ARG,
|
|
|
|
HR_CRLF,
|
|
|
|
};
|
|
|
|
|
2020-01-02 08:32:23 +00:00
|
|
|
int
|
2017-10-16 12:52:32 +08:00
|
|
|
lws_cgi_write_split_stdout_headers(struct lws *wsi)
|
|
|
|
{
|
|
|
|
int n, m, cmd;
|
2019-04-05 21:19:09 +08:00
|
|
|
unsigned char buf[LWS_PRE + 4096], *start = &buf[LWS_PRE], *p = start,
|
2017-10-16 12:52:32 +08:00
|
|
|
*end = &buf[sizeof(buf) - 1 - LWS_PRE], *name,
|
|
|
|
*value = NULL;
|
|
|
|
char c, hrs;
|
|
|
|
|
2018-04-27 19:16:50 +08:00
|
|
|
if (!wsi->http.cgi)
|
2017-10-16 12:52:32 +08:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
while (wsi->hdr_state != LHCS_PAYLOAD) {
|
|
|
|
/*
|
|
|
|
* We have to separate header / finalize and payload chunks,
|
|
|
|
* since they need to be handled separately
|
|
|
|
*/
|
|
|
|
switch (wsi->hdr_state) {
|
|
|
|
case LHCS_RESPONSE:
|
|
|
|
lwsl_debug("LHCS_RESPONSE: issuing response %d\n",
|
2018-04-27 19:16:50 +08:00
|
|
|
wsi->http.cgi->response_code);
|
2017-10-16 12:52:32 +08:00
|
|
|
if (lws_add_http_header_status(wsi,
|
2018-11-23 08:47:56 +08:00
|
|
|
wsi->http.cgi->response_code,
|
2017-10-16 12:52:32 +08:00
|
|
|
&p, end))
|
|
|
|
return 1;
|
2018-04-27 19:16:50 +08:00
|
|
|
if (!wsi->http.cgi->explicitly_chunked &&
|
|
|
|
!wsi->http.cgi->content_length &&
|
2017-10-16 12:52:32 +08:00
|
|
|
lws_add_http_header_by_token(wsi,
|
|
|
|
WSI_TOKEN_HTTP_TRANSFER_ENCODING,
|
|
|
|
(unsigned char *)"chunked", 7, &p, end))
|
|
|
|
return 1;
|
2019-12-23 11:31:57 +00:00
|
|
|
if (!(wsi->mux_substream))
|
2017-10-16 12:52:32 +08:00
|
|
|
if (lws_add_http_header_by_token(wsi,
|
|
|
|
WSI_TOKEN_CONNECTION,
|
|
|
|
(unsigned char *)"close", 5,
|
|
|
|
&p, end))
|
|
|
|
return 1;
|
|
|
|
n = lws_write(wsi, start, p - start,
|
|
|
|
LWS_WRITE_HTTP_HEADERS | LWS_WRITE_NO_FIN);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* so we have a bunch of http/1 style ascii headers
|
2018-04-27 19:16:50 +08:00
|
|
|
* starting from wsi->http.cgi->headers_buf through
|
|
|
|
* wsi->http.cgi->headers_pos. These are OK for http/1
|
2017-10-16 12:52:32 +08:00
|
|
|
* connections, but they're no good for http/2 conns.
|
|
|
|
*
|
|
|
|
* Let's redo them at headers_pos forward using the
|
|
|
|
* correct coding for http/1 or http/2
|
|
|
|
*/
|
2019-12-23 11:31:57 +00:00
|
|
|
if (!wsi->mux_substream)
|
2017-10-16 12:52:32 +08:00
|
|
|
goto post_hpack_recode;
|
|
|
|
|
2018-04-27 19:16:50 +08:00
|
|
|
p = wsi->http.cgi->headers_start;
|
2018-11-23 08:47:56 +08:00
|
|
|
wsi->http.cgi->headers_start =
|
|
|
|
wsi->http.cgi->headers_pos;
|
|
|
|
wsi->http.cgi->headers_dumped =
|
|
|
|
wsi->http.cgi->headers_start;
|
2017-10-16 12:52:32 +08:00
|
|
|
hrs = HR_NAME;
|
|
|
|
name = buf;
|
|
|
|
|
2018-04-27 19:16:50 +08:00
|
|
|
while (p < wsi->http.cgi->headers_start) {
|
2017-10-16 12:52:32 +08:00
|
|
|
switch (hrs) {
|
|
|
|
case HR_NAME:
|
|
|
|
/*
|
|
|
|
* in http/2 upper-case header names
|
|
|
|
* are illegal. So convert to lower-
|
|
|
|
* case.
|
|
|
|
*/
|
|
|
|
if (name - buf > 64)
|
|
|
|
return -1;
|
|
|
|
if (*p != ':') {
|
|
|
|
if (*p >= 'A' && *p <= 'Z')
|
|
|
|
*name++ = (*p++) +
|
|
|
|
('a' - 'A');
|
|
|
|
else
|
|
|
|
*name++ = *p++;
|
|
|
|
} else {
|
|
|
|
p++;
|
|
|
|
*name++ = '\0';
|
|
|
|
value = name;
|
|
|
|
hrs = HR_WHITESPACE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case HR_WHITESPACE:
|
|
|
|
if (*p == ' ') {
|
|
|
|
p++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
hrs = HR_ARG;
|
|
|
|
/* fallthru */
|
|
|
|
case HR_ARG:
|
|
|
|
if (name > end - 64)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (*p != '\x0a' && *p != '\x0d') {
|
|
|
|
*name++ = *p++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
hrs = HR_CRLF;
|
|
|
|
/* fallthru */
|
|
|
|
case HR_CRLF:
|
|
|
|
if ((*p != '\x0a' && *p != '\x0d') ||
|
2018-04-27 19:16:50 +08:00
|
|
|
p + 1 == wsi->http.cgi->headers_start) {
|
2017-10-16 12:52:32 +08:00
|
|
|
*name = '\0';
|
|
|
|
if ((strcmp((const char *)buf,
|
|
|
|
"transfer-encoding")
|
|
|
|
)) {
|
|
|
|
lwsl_debug("+ %s: %s\n",
|
|
|
|
buf, value);
|
|
|
|
if (
|
|
|
|
lws_add_http_header_by_name(wsi, buf,
|
|
|
|
(unsigned char *)value, name - value,
|
2018-04-27 19:16:50 +08:00
|
|
|
(unsigned char **)&wsi->http.cgi->headers_pos,
|
|
|
|
(unsigned char *)wsi->http.cgi->headers_end))
|
2017-10-16 12:52:32 +08:00
|
|
|
return 1;
|
|
|
|
hrs = HR_NAME;
|
|
|
|
name = buf;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
p++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
post_hpack_recode:
|
|
|
|
/* finalize cached headers before dumping them */
|
|
|
|
if (lws_finalize_http_header(wsi,
|
2018-11-23 08:47:56 +08:00
|
|
|
(unsigned char **)&wsi->http.cgi->headers_pos,
|
|
|
|
(unsigned char *)wsi->http.cgi->headers_end)) {
|
2017-10-16 12:52:32 +08:00
|
|
|
|
|
|
|
lwsl_notice("finalize failed\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
wsi->hdr_state = LHCS_DUMP_HEADERS;
|
|
|
|
wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_HEADERS;
|
|
|
|
lws_callback_on_writable(wsi);
|
|
|
|
/* back to the loop for writeability again */
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case LHCS_DUMP_HEADERS:
|
|
|
|
|
2018-11-23 08:47:56 +08:00
|
|
|
n = wsi->http.cgi->headers_pos -
|
|
|
|
wsi->http.cgi->headers_dumped;
|
2017-10-16 12:52:32 +08:00
|
|
|
if (n > 512)
|
|
|
|
n = 512;
|
|
|
|
|
|
|
|
lwsl_debug("LHCS_DUMP_HEADERS: %d\n", n);
|
|
|
|
|
|
|
|
cmd = LWS_WRITE_HTTP_HEADERS_CONTINUATION;
|
2018-04-27 19:16:50 +08:00
|
|
|
if (wsi->http.cgi->headers_dumped + n !=
|
2020-07-17 15:35:28 +01:00
|
|
|
wsi->http.cgi->headers_pos) {
|
2017-10-16 12:52:32 +08:00
|
|
|
lwsl_notice("adding no fin flag\n");
|
|
|
|
cmd |= LWS_WRITE_NO_FIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
m = lws_write(wsi,
|
2018-11-23 08:47:56 +08:00
|
|
|
(unsigned char *)wsi->http.cgi->headers_dumped,
|
2017-10-16 12:52:32 +08:00
|
|
|
n, cmd);
|
|
|
|
if (m < 0) {
|
|
|
|
lwsl_debug("%s: write says %d\n", __func__, m);
|
|
|
|
return -1;
|
|
|
|
}
|
2018-04-27 19:16:50 +08:00
|
|
|
wsi->http.cgi->headers_dumped += n;
|
2018-11-23 08:47:56 +08:00
|
|
|
if (wsi->http.cgi->headers_dumped ==
|
|
|
|
wsi->http.cgi->headers_pos) {
|
2017-10-16 12:52:32 +08:00
|
|
|
wsi->hdr_state = LHCS_PAYLOAD;
|
2018-04-27 19:16:50 +08:00
|
|
|
lws_free_set_NULL(wsi->http.cgi->headers_buf);
|
2020-07-17 15:35:28 +01:00
|
|
|
lwsl_debug("%s: freed cgi headers\n", __func__);
|
|
|
|
|
|
|
|
if (wsi->http.cgi->post_in_expected) {
|
|
|
|
lwsl_notice("%s: post data still expected, asking for writeable\n", __func__);
|
|
|
|
lws_callback_on_writable(wsi);
|
|
|
|
}
|
|
|
|
|
2017-10-16 12:52:32 +08:00
|
|
|
} else {
|
2018-11-23 08:47:56 +08:00
|
|
|
wsi->reason_bf |=
|
|
|
|
LWS_CB_REASON_AUX_BF__CGI_HEADERS;
|
2017-10-16 12:52:32 +08:00
|
|
|
lws_callback_on_writable(wsi);
|
|
|
|
}
|
|
|
|
|
2020-07-17 15:35:28 +01:00
|
|
|
/*
|
|
|
|
* writeability becomes uncertain now we wrote
|
2017-10-16 12:52:32 +08:00
|
|
|
* something, we must return to the event loop
|
|
|
|
*/
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-04-27 19:16:50 +08:00
|
|
|
if (!wsi->http.cgi->headers_buf) {
|
2018-11-23 08:47:56 +08:00
|
|
|
/* if we don't already have a headers buf, cook one */
|
2017-10-16 12:52:32 +08:00
|
|
|
n = 2048;
|
2019-12-23 11:31:57 +00:00
|
|
|
if (wsi->mux_substream)
|
2017-10-16 12:52:32 +08:00
|
|
|
n = 4096;
|
2018-04-27 19:16:50 +08:00
|
|
|
wsi->http.cgi->headers_buf = lws_malloc(n + LWS_PRE,
|
2017-10-16 12:52:32 +08:00
|
|
|
"cgi hdr buf");
|
2018-04-27 19:16:50 +08:00
|
|
|
if (!wsi->http.cgi->headers_buf) {
|
2017-10-16 12:52:32 +08:00
|
|
|
lwsl_err("OOM\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
lwsl_debug("allocated cgi hdrs\n");
|
2018-11-23 08:47:56 +08:00
|
|
|
wsi->http.cgi->headers_start =
|
|
|
|
wsi->http.cgi->headers_buf + LWS_PRE;
|
2018-04-27 19:16:50 +08:00
|
|
|
wsi->http.cgi->headers_pos = wsi->http.cgi->headers_start;
|
|
|
|
wsi->http.cgi->headers_dumped = wsi->http.cgi->headers_pos;
|
2018-11-23 08:47:56 +08:00
|
|
|
wsi->http.cgi->headers_end =
|
|
|
|
wsi->http.cgi->headers_buf + n - 1;
|
2017-10-16 12:52:32 +08:00
|
|
|
|
|
|
|
for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++) {
|
2018-04-27 19:16:50 +08:00
|
|
|
wsi->http.cgi->match[n] = 0;
|
|
|
|
wsi->http.cgi->lp = 0;
|
2017-10-16 12:52:32 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-12 10:12:39 +00:00
|
|
|
n = lws_get_socket_fd(wsi->http.cgi->lsp->stdwsi[LWS_STDOUT]);
|
2017-10-16 12:52:32 +08:00
|
|
|
if (n < 0)
|
|
|
|
return -1;
|
|
|
|
n = read(n, &c, 1);
|
|
|
|
if (n < 0) {
|
|
|
|
if (errno != EAGAIN) {
|
|
|
|
lwsl_debug("%s: read says %d\n", __func__, n);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
n = 0;
|
|
|
|
|
2018-11-23 08:47:56 +08:00
|
|
|
if (wsi->http.cgi->headers_pos >=
|
|
|
|
wsi->http.cgi->headers_end - 4) {
|
2017-10-16 12:52:32 +08:00
|
|
|
lwsl_notice("CGI hdrs > buf size\n");
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!n)
|
|
|
|
goto agin;
|
|
|
|
|
|
|
|
lwsl_debug("-- 0x%02X %c %d %d\n", (unsigned char)c, c,
|
2018-04-27 19:16:50 +08:00
|
|
|
wsi->http.cgi->match[1], wsi->hdr_state);
|
2017-10-16 12:52:32 +08:00
|
|
|
if (!c)
|
|
|
|
return -1;
|
|
|
|
switch (wsi->hdr_state) {
|
|
|
|
case LCHS_HEADER:
|
|
|
|
hdr:
|
|
|
|
for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++) {
|
|
|
|
/*
|
|
|
|
* significant headers with
|
|
|
|
* numeric decimal payloads
|
|
|
|
*/
|
2018-04-27 19:16:50 +08:00
|
|
|
if (!significant_hdr[n][wsi->http.cgi->match[n]] &&
|
2017-10-16 12:52:32 +08:00
|
|
|
(c >= '0' && c <= '9') &&
|
2018-04-27 19:16:50 +08:00
|
|
|
wsi->http.cgi->lp < (int)sizeof(wsi->http.cgi->l) - 1) {
|
|
|
|
wsi->http.cgi->l[wsi->http.cgi->lp++] = c;
|
|
|
|
wsi->http.cgi->l[wsi->http.cgi->lp] = '\0';
|
2017-10-16 12:52:32 +08:00
|
|
|
switch (n) {
|
|
|
|
case SIGNIFICANT_HDR_CONTENT_LENGTH:
|
2018-04-27 19:16:50 +08:00
|
|
|
wsi->http.cgi->content_length =
|
|
|
|
atoll(wsi->http.cgi->l);
|
2017-10-16 12:52:32 +08:00
|
|
|
break;
|
|
|
|
case SIGNIFICANT_HDR_STATUS:
|
2018-04-27 19:16:50 +08:00
|
|
|
wsi->http.cgi->response_code =
|
|
|
|
atol(wsi->http.cgi->l);
|
2017-10-16 12:52:32 +08:00
|
|
|
lwsl_debug("Status set to %d\n",
|
2018-04-27 19:16:50 +08:00
|
|
|
wsi->http.cgi->response_code);
|
2017-10-16 12:52:32 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* hits up to the NUL are sticky until next hdr */
|
2018-04-27 19:16:50 +08:00
|
|
|
if (significant_hdr[n][wsi->http.cgi->match[n]]) {
|
2017-10-16 12:52:32 +08:00
|
|
|
if (tolower(c) ==
|
2018-04-27 19:16:50 +08:00
|
|
|
significant_hdr[n][wsi->http.cgi->match[n]])
|
|
|
|
wsi->http.cgi->match[n]++;
|
2017-10-16 12:52:32 +08:00
|
|
|
else
|
2018-04-27 19:16:50 +08:00
|
|
|
wsi->http.cgi->match[n] = 0;
|
2017-10-16 12:52:32 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* some cgi only send us \x0a for EOL */
|
|
|
|
if (c == '\x0a') {
|
|
|
|
wsi->hdr_state = LCHS_SINGLE_0A;
|
2018-04-27 19:16:50 +08:00
|
|
|
*wsi->http.cgi->headers_pos++ = '\x0d';
|
2017-10-16 12:52:32 +08:00
|
|
|
}
|
2018-04-27 19:16:50 +08:00
|
|
|
*wsi->http.cgi->headers_pos++ = c;
|
2017-10-16 12:52:32 +08:00
|
|
|
if (c == '\x0d')
|
|
|
|
wsi->hdr_state = LCHS_LF1;
|
|
|
|
|
|
|
|
if (wsi->hdr_state != LCHS_HEADER &&
|
|
|
|
!significant_hdr[SIGNIFICANT_HDR_TRANSFER_ENCODING]
|
2018-04-27 19:16:50 +08:00
|
|
|
[wsi->http.cgi->match[
|
2017-10-16 12:52:32 +08:00
|
|
|
SIGNIFICANT_HDR_TRANSFER_ENCODING]]) {
|
2018-06-16 09:35:07 +08:00
|
|
|
lwsl_info("cgi produced chunked\n");
|
2018-04-27 19:16:50 +08:00
|
|
|
wsi->http.cgi->explicitly_chunked = 1;
|
2017-10-16 12:52:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* presence of Location: mandates 302 retcode */
|
|
|
|
if (wsi->hdr_state != LCHS_HEADER &&
|
|
|
|
!significant_hdr[SIGNIFICANT_HDR_LOCATION][
|
2018-11-23 08:47:56 +08:00
|
|
|
wsi->http.cgi->match[SIGNIFICANT_HDR_LOCATION]]) {
|
2017-10-16 12:52:32 +08:00
|
|
|
lwsl_debug("CGI: Location hdr seen\n");
|
2018-04-27 19:16:50 +08:00
|
|
|
wsi->http.cgi->response_code = 302;
|
2017-10-16 12:52:32 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case LCHS_LF1:
|
2018-04-27 19:16:50 +08:00
|
|
|
*wsi->http.cgi->headers_pos++ = c;
|
2017-10-16 12:52:32 +08:00
|
|
|
if (c == '\x0a') {
|
|
|
|
wsi->hdr_state = LCHS_CR2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* we got \r[^\n]... it's unreasonable */
|
|
|
|
lwsl_debug("%s: funny CRLF 0x%02X\n", __func__,
|
|
|
|
(unsigned char)c);
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
case LCHS_CR2:
|
|
|
|
if (c == '\x0d') {
|
|
|
|
/* drop the \x0d */
|
|
|
|
wsi->hdr_state = LCHS_LF2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
wsi->hdr_state = LCHS_HEADER;
|
|
|
|
for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++)
|
2018-04-27 19:16:50 +08:00
|
|
|
wsi->http.cgi->match[n] = 0;
|
|
|
|
wsi->http.cgi->lp = 0;
|
2017-10-16 12:52:32 +08:00
|
|
|
goto hdr;
|
|
|
|
|
|
|
|
case LCHS_LF2:
|
|
|
|
case LCHS_SINGLE_0A:
|
|
|
|
m = wsi->hdr_state;
|
|
|
|
if (c == '\x0a') {
|
|
|
|
lwsl_debug("Content-Length: %lld\n",
|
|
|
|
(unsigned long long)
|
2018-04-27 19:16:50 +08:00
|
|
|
wsi->http.cgi->content_length);
|
2017-10-16 12:52:32 +08:00
|
|
|
wsi->hdr_state = LHCS_RESPONSE;
|
|
|
|
/*
|
|
|
|
* drop the \0xa ... finalize
|
|
|
|
* will add it if needed (HTTP/1)
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (m == LCHS_LF2)
|
|
|
|
/* we got \r\n\r[^\n]... unreasonable */
|
|
|
|
return -1;
|
|
|
|
/* we got \x0anext header, it's reasonable */
|
2018-04-27 19:16:50 +08:00
|
|
|
*wsi->http.cgi->headers_pos++ = c;
|
2017-10-16 12:52:32 +08:00
|
|
|
wsi->hdr_state = LCHS_HEADER;
|
|
|
|
for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++)
|
2018-04-27 19:16:50 +08:00
|
|
|
wsi->http.cgi->match[n] = 0;
|
|
|
|
wsi->http.cgi->lp = 0;
|
2017-10-16 12:52:32 +08:00
|
|
|
break;
|
|
|
|
case LHCS_PAYLOAD:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
agin:
|
|
|
|
/* ran out of input, ended the hdrs, or filled up the hdrs buf */
|
|
|
|
if (!n || wsi->hdr_state == LHCS_PAYLOAD)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* payload processing */
|
|
|
|
|
2019-12-23 11:31:57 +00:00
|
|
|
m = !wsi->http.cgi->implied_chunked && !wsi->mux_substream &&
|
2019-11-18 09:19:08 +00:00
|
|
|
// !wsi->http.cgi->explicitly_chunked &&
|
2018-08-01 06:52:03 +08:00
|
|
|
!wsi->http.cgi->content_length;
|
2020-02-12 10:12:39 +00:00
|
|
|
n = lws_get_socket_fd(wsi->http.cgi->lsp->stdwsi[LWS_STDOUT]);
|
2017-10-16 12:52:32 +08:00
|
|
|
if (n < 0)
|
|
|
|
return -1;
|
2018-08-01 06:52:03 +08:00
|
|
|
n = read(n, start, sizeof(buf) - LWS_PRE);
|
2017-10-16 12:52:32 +08:00
|
|
|
|
|
|
|
if (n < 0 && errno != EAGAIN) {
|
|
|
|
lwsl_debug("%s: stdout read says %d\n", __func__, n);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (n > 0) {
|
2019-11-15 19:04:12 +00:00
|
|
|
// lwsl_hexdump_notice(buf, n);
|
|
|
|
|
2019-12-23 11:31:57 +00:00
|
|
|
if (!wsi->mux_substream && m) {
|
2017-10-16 12:52:32 +08:00
|
|
|
char chdr[LWS_HTTP_CHUNK_HDR_SIZE];
|
|
|
|
m = lws_snprintf(chdr, LWS_HTTP_CHUNK_HDR_SIZE - 3,
|
|
|
|
"%X\x0d\x0a", n);
|
|
|
|
memmove(start + m, start, n);
|
|
|
|
memcpy(start, chdr, m);
|
|
|
|
memcpy(start + m + n, "\x0d\x0a", 2);
|
|
|
|
n += m + 2;
|
|
|
|
}
|
2019-11-15 19:04:12 +00:00
|
|
|
|
2019-04-05 21:19:09 +08:00
|
|
|
|
|
|
|
#if defined(LWS_WITH_HTTP2)
|
2019-12-23 11:31:57 +00:00
|
|
|
if (wsi->mux_substream) {
|
2019-04-05 21:19:09 +08:00
|
|
|
struct lws *nwsi = lws_get_network_wsi(wsi);
|
|
|
|
|
|
|
|
__lws_set_timeout(wsi,
|
|
|
|
PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 31);
|
|
|
|
|
|
|
|
if (!nwsi->immortal_substream_count)
|
|
|
|
__lws_set_timeout(nwsi,
|
|
|
|
PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 31);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-10-16 12:52:32 +08:00
|
|
|
cmd = LWS_WRITE_HTTP;
|
2018-11-23 08:47:56 +08:00
|
|
|
if (wsi->http.cgi->content_length_seen + n ==
|
|
|
|
wsi->http.cgi->content_length)
|
2017-10-16 12:52:32 +08:00
|
|
|
cmd = LWS_WRITE_HTTP_FINAL;
|
2018-06-16 09:35:07 +08:00
|
|
|
|
2017-10-16 12:52:32 +08:00
|
|
|
m = lws_write(wsi, (unsigned char *)start, n, cmd);
|
|
|
|
//lwsl_notice("write %d\n", m);
|
|
|
|
if (m < 0) {
|
|
|
|
lwsl_debug("%s: stdout write says %d\n", __func__, m);
|
|
|
|
return -1;
|
|
|
|
}
|
2018-04-27 19:16:50 +08:00
|
|
|
wsi->http.cgi->content_length_seen += n;
|
2017-10-16 12:52:32 +08:00
|
|
|
} else {
|
2019-11-15 19:04:12 +00:00
|
|
|
|
2019-12-23 11:31:57 +00:00
|
|
|
if (!wsi->mux_substream && m) {
|
2019-11-18 09:19:08 +00:00
|
|
|
uint8_t term[LWS_PRE + 6];
|
2019-11-15 19:04:12 +00:00
|
|
|
|
2020-08-02 08:20:35 +01:00
|
|
|
lwsl_info("%s: sent trailer\n", __func__);
|
2019-11-18 09:19:08 +00:00
|
|
|
memcpy(term + LWS_PRE, (uint8_t *)"0\x0d\x0a\x0d\x0a", 5);
|
2019-11-15 19:04:12 +00:00
|
|
|
|
2019-11-18 09:19:08 +00:00
|
|
|
if (lws_write(wsi, term + LWS_PRE, 5,
|
|
|
|
LWS_WRITE_HTTP_FINAL) != 5)
|
|
|
|
return -1;
|
2019-11-15 19:04:12 +00:00
|
|
|
|
2019-11-18 09:19:08 +00:00
|
|
|
wsi->http.cgi->cgi_transaction_over = 1;
|
2019-11-15 19:04:12 +00:00
|
|
|
|
2019-11-18 09:19:08 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2019-11-15 19:04:12 +00:00
|
|
|
|
2017-10-16 12:52:32 +08:00
|
|
|
if (wsi->cgi_stdout_zero_length) {
|
|
|
|
lwsl_debug("%s: stdout is POLLHUP'd\n", __func__);
|
2019-12-23 11:31:57 +00:00
|
|
|
if (wsi->mux_substream)
|
2017-10-16 12:52:32 +08:00
|
|
|
m = lws_write(wsi, (unsigned char *)start, 0,
|
|
|
|
LWS_WRITE_HTTP_FINAL);
|
2019-01-29 12:25:20 +08:00
|
|
|
else
|
|
|
|
return -1;
|
2017-10-16 12:52:32 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
wsi->cgi_stdout_zero_length = 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-01-02 08:32:23 +00:00
|
|
|
int
|
2017-10-16 12:52:32 +08:00
|
|
|
lws_cgi_kill(struct lws *wsi)
|
|
|
|
{
|
|
|
|
struct lws_cgi_args args;
|
2020-02-12 10:12:39 +00:00
|
|
|
pid_t pid;
|
|
|
|
int n, m;
|
2017-10-16 12:52:32 +08:00
|
|
|
|
|
|
|
lwsl_debug("%s: %p\n", __func__, wsi);
|
|
|
|
|
2020-02-12 10:12:39 +00:00
|
|
|
if (!wsi->http.cgi || !wsi->http.cgi->lsp)
|
2017-10-16 12:52:32 +08:00
|
|
|
return 0;
|
|
|
|
|
2020-02-12 10:12:39 +00:00
|
|
|
pid = wsi->http.cgi->lsp->child_pid;
|
2017-10-16 12:52:32 +08:00
|
|
|
|
2020-02-12 10:12:39 +00:00
|
|
|
args.stdwsi = &wsi->http.cgi->lsp->stdwsi[0];
|
|
|
|
lws_spawn_piped_kill_child_process(wsi->http.cgi->lsp);
|
|
|
|
/* that has invalidated and NULL'd wsi->http.cgi->lsp */
|
2017-10-16 12:52:32 +08:00
|
|
|
|
2020-02-12 10:12:39 +00:00
|
|
|
if (pid != -1) {
|
|
|
|
m = wsi->http.cgi->being_closed;
|
fakewsi: replace with smaller substructure
Currently we always reserve a fakewsi per pt so events that don't have a related actual
wsi, like vhost-protocol-init or vhost cert init via protocol callback can make callbacks
that look reasonable to user protocol handler code expecting a valid wsi every time.
This patch splits out stuff that user callbacks often unconditionally expect to be in
a wsi, like context pointer, vhost pointer etc into a substructure, which is composed
into struct lws at the top of it. Internal references (struct lws is opaque, so there
are only internal references) are all updated to go via the substructre, the compiler
should make that a NOP.
Helpers are added when fakewsi is used and referenced.
If not PLAT_FREERTOS, we continue to provide a full fakewsi in the pt as before,
although the helpers improve consistency by zeroing down the substructure. There is
a huge amount of user code out there over the last 10 years that did not always have
the minimal examples to follow, some of it does some unexpected things.
If it is PLAT_FREERTOS, that is a newer thing in lws and users have the benefit of
being able to follow the minimal examples' approach. For PLAT_FREERTOS we don't
reserve the fakewsi in the pt any more, saving around 800 bytes. The helpers then
create a struct lws_a (the substructure) on the stack, zero it down (but it is only
like 4 pointers) and prepare it with whatever we know like the context.
Then we cast it to a struct lws * and use it in the user protocol handler call.
In this case, the remainder of the struct lws is undefined. However the amount of
old protocol handlers that might touch things outside of the substructure in
PLAT_FREERTOS is very limited compared to legacy lws user code and the saving is
significant on constrained devices.
User handlers should not be touching everything in a wsi every time anyway, there
are several cases where there is no valid wsi to do the call with. Dereference of
things outside the substructure should only happen when the callback reason shows
there is a valid wsi bound to the activity (as in all the minimal examples).
2020-07-19 08:33:46 +01:00
|
|
|
n = user_callback_handle_rxflow(wsi->a.protocol->callback, wsi,
|
2017-10-16 12:52:32 +08:00
|
|
|
LWS_CALLBACK_CGI_TERMINATED,
|
2018-11-23 08:47:56 +08:00
|
|
|
wsi->user_space, (void *)&args,
|
2020-02-12 10:12:39 +00:00
|
|
|
pid);
|
2019-11-18 09:19:08 +00:00
|
|
|
if (n && !m)
|
2018-02-03 13:48:18 +08:00
|
|
|
lws_close_free_wsi(wsi, 0, "lws_cgi_kill");
|
2017-10-16 12:52:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-09-18 13:09:32 +01:00
|
|
|
int
|
2017-10-16 12:52:32 +08:00
|
|
|
lws_cgi_kill_terminated(struct lws_context_per_thread *pt)
|
|
|
|
{
|
|
|
|
struct lws_cgi **pcgi, *cgi = NULL;
|
|
|
|
int status, n = 1;
|
|
|
|
|
|
|
|
while (n > 0) {
|
|
|
|
/* find finished guys but don't reap yet */
|
|
|
|
n = waitpid(-1, &status, WNOHANG);
|
|
|
|
if (n <= 0)
|
|
|
|
continue;
|
|
|
|
lwsl_debug("%s: observed PID %d terminated\n", __func__, n);
|
|
|
|
|
2018-04-27 15:20:56 +08:00
|
|
|
pcgi = &pt->http.cgi_list;
|
2017-10-16 12:52:32 +08:00
|
|
|
|
|
|
|
/* check all the subprocesses on the cgi list */
|
|
|
|
while (*pcgi) {
|
|
|
|
/* get the next one first as list may change */
|
|
|
|
cgi = *pcgi;
|
|
|
|
pcgi = &(*pcgi)->cgi_list;
|
|
|
|
|
2020-02-12 10:12:39 +00:00
|
|
|
if (cgi->lsp->child_pid <= 0)
|
2017-10-16 12:52:32 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/* finish sending cached headers */
|
|
|
|
if (cgi->headers_buf)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* wait for stdout to be drained */
|
|
|
|
if (cgi->content_length > cgi->content_length_seen)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (cgi->content_length) {
|
2018-11-23 08:47:56 +08:00
|
|
|
lwsl_debug("%s: wsi %p: expected content "
|
|
|
|
"length seen: %lld\n", __func__,
|
|
|
|
cgi->wsi,
|
|
|
|
(unsigned long long)cgi->content_length_seen);
|
2017-10-16 12:52:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* reap it */
|
|
|
|
waitpid(n, &status, WNOHANG);
|
|
|
|
/*
|
|
|
|
* he's already terminated so no need for kill()
|
|
|
|
* but we should do the terminated cgi callback
|
|
|
|
* and close him if he's not already closing
|
|
|
|
*/
|
2020-02-12 10:12:39 +00:00
|
|
|
if (n == cgi->lsp->child_pid) {
|
2017-10-16 12:52:32 +08:00
|
|
|
lwsl_debug("%s: found PID %d on cgi list\n",
|
|
|
|
__func__, n);
|
|
|
|
|
|
|
|
if (!cgi->content_length) {
|
|
|
|
/*
|
|
|
|
* well, if he sends chunked...
|
|
|
|
* give him 2s after the
|
|
|
|
* cgi terminated to send buffered
|
|
|
|
*/
|
|
|
|
cgi->chunked_grace++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* defeat kill() */
|
2020-02-12 10:12:39 +00:00
|
|
|
cgi->lsp->child_pid = 0;
|
2017-10-16 12:52:32 +08:00
|
|
|
lws_cgi_kill(cgi->wsi);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
cgi = NULL;
|
|
|
|
}
|
|
|
|
/* if not found on the cgi list, as he's one of ours, reap */
|
|
|
|
if (!cgi) {
|
|
|
|
lwsl_debug("%s: reading PID %d although no cgi match\n",
|
|
|
|
__func__, n);
|
|
|
|
waitpid(n, &status, WNOHANG);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-27 15:20:56 +08:00
|
|
|
pcgi = &pt->http.cgi_list;
|
2017-10-16 12:52:32 +08:00
|
|
|
|
|
|
|
/* check all the subprocesses on the cgi list */
|
|
|
|
while (*pcgi) {
|
|
|
|
/* get the next one first as list may change */
|
|
|
|
cgi = *pcgi;
|
|
|
|
pcgi = &(*pcgi)->cgi_list;
|
|
|
|
|
2020-07-17 15:35:28 +01:00
|
|
|
if (!cgi || !cgi->lsp || cgi->lsp->child_pid <= 0)
|
2017-10-16 12:52:32 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/* we deferred killing him after reaping his PID */
|
|
|
|
if (cgi->chunked_grace) {
|
|
|
|
cgi->chunked_grace++;
|
|
|
|
if (cgi->chunked_grace < 2)
|
|
|
|
continue;
|
|
|
|
goto finish_him;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* finish sending cached headers */
|
|
|
|
if (cgi->headers_buf)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* wait for stdout to be drained */
|
|
|
|
if (cgi->content_length > cgi->content_length_seen)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (cgi->content_length)
|
2018-11-23 08:47:56 +08:00
|
|
|
lwsl_debug("%s: wsi %p: expected "
|
|
|
|
"content len seen: %lld\n", __func__,
|
|
|
|
cgi->wsi,
|
2017-10-16 12:52:32 +08:00
|
|
|
(unsigned long long)cgi->content_length_seen);
|
|
|
|
|
|
|
|
/* reap it */
|
2020-02-12 10:12:39 +00:00
|
|
|
if (waitpid(cgi->lsp->child_pid, &status, WNOHANG) > 0) {
|
2017-10-16 12:52:32 +08:00
|
|
|
|
|
|
|
if (!cgi->content_length) {
|
|
|
|
/*
|
|
|
|
* well, if he sends chunked...
|
|
|
|
* give him 2s after the
|
|
|
|
* cgi terminated to send buffered
|
|
|
|
*/
|
|
|
|
cgi->chunked_grace++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
finish_him:
|
|
|
|
lwsl_debug("%s: found PID %d on cgi list\n",
|
2020-02-12 10:12:39 +00:00
|
|
|
__func__, cgi->lsp->child_pid);
|
2017-10-16 12:52:32 +08:00
|
|
|
|
|
|
|
/* defeat kill() */
|
2020-02-12 10:12:39 +00:00
|
|
|
cgi->lsp->child_pid = 0;
|
2017-10-16 12:52:32 +08:00
|
|
|
lws_cgi_kill(cgi->wsi);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-01-02 08:32:23 +00:00
|
|
|
struct lws *
|
2017-10-16 12:52:32 +08:00
|
|
|
lws_cgi_get_stdwsi(struct lws *wsi, enum lws_enum_stdinouterr ch)
|
|
|
|
{
|
2018-04-27 19:16:50 +08:00
|
|
|
if (!wsi->http.cgi)
|
2017-10-16 12:52:32 +08:00
|
|
|
return NULL;
|
|
|
|
|
2020-02-12 10:12:39 +00:00
|
|
|
return wsi->http.cgi->lsp->stdwsi[ch];
|
2017-10-16 12:52:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lws_cgi_remove_and_kill(struct lws *wsi)
|
|
|
|
{
|
fakewsi: replace with smaller substructure
Currently we always reserve a fakewsi per pt so events that don't have a related actual
wsi, like vhost-protocol-init or vhost cert init via protocol callback can make callbacks
that look reasonable to user protocol handler code expecting a valid wsi every time.
This patch splits out stuff that user callbacks often unconditionally expect to be in
a wsi, like context pointer, vhost pointer etc into a substructure, which is composed
into struct lws at the top of it. Internal references (struct lws is opaque, so there
are only internal references) are all updated to go via the substructre, the compiler
should make that a NOP.
Helpers are added when fakewsi is used and referenced.
If not PLAT_FREERTOS, we continue to provide a full fakewsi in the pt as before,
although the helpers improve consistency by zeroing down the substructure. There is
a huge amount of user code out there over the last 10 years that did not always have
the minimal examples to follow, some of it does some unexpected things.
If it is PLAT_FREERTOS, that is a newer thing in lws and users have the benefit of
being able to follow the minimal examples' approach. For PLAT_FREERTOS we don't
reserve the fakewsi in the pt any more, saving around 800 bytes. The helpers then
create a struct lws_a (the substructure) on the stack, zero it down (but it is only
like 4 pointers) and prepare it with whatever we know like the context.
Then we cast it to a struct lws * and use it in the user protocol handler call.
In this case, the remainder of the struct lws is undefined. However the amount of
old protocol handlers that might touch things outside of the substructure in
PLAT_FREERTOS is very limited compared to legacy lws user code and the saving is
significant on constrained devices.
User handlers should not be touching everything in a wsi every time anyway, there
are several cases where there is no valid wsi to do the call with. Dereference of
things outside the substructure should only happen when the callback reason shows
there is a valid wsi bound to the activity (as in all the minimal examples).
2020-07-19 08:33:46 +01:00
|
|
|
struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
|
2018-04-27 15:20:56 +08:00
|
|
|
struct lws_cgi **pcgi = &pt->http.cgi_list;
|
2017-10-16 12:52:32 +08:00
|
|
|
|
|
|
|
/* remove us from the cgi list */
|
2018-04-27 19:16:50 +08:00
|
|
|
lwsl_debug("%s: remove cgi %p from list\n", __func__, wsi->http.cgi);
|
2017-10-16 12:52:32 +08:00
|
|
|
while (*pcgi) {
|
2018-04-27 19:16:50 +08:00
|
|
|
if (*pcgi == wsi->http.cgi) {
|
2017-10-16 12:52:32 +08:00
|
|
|
/* drop us from the pt cgi list */
|
|
|
|
*pcgi = (*pcgi)->cgi_list;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
pcgi = &(*pcgi)->cgi_list;
|
|
|
|
}
|
2018-04-27 19:16:50 +08:00
|
|
|
if (wsi->http.cgi->headers_buf) {
|
2020-07-17 15:35:28 +01:00
|
|
|
lwsl_debug("%s: close: freed cgi headers\n", __func__);
|
2018-04-27 19:16:50 +08:00
|
|
|
lws_free_set_NULL(wsi->http.cgi->headers_buf);
|
2017-10-16 12:52:32 +08:00
|
|
|
}
|
|
|
|
/* we have a cgi going, we must kill it */
|
2018-04-27 19:16:50 +08:00
|
|
|
wsi->http.cgi->being_closed = 1;
|
2017-10-16 12:52:32 +08:00
|
|
|
lws_cgi_kill(wsi);
|
|
|
|
}
|