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

cgi: minimal example

This commit is contained in:
Andy Green 2019-11-18 09:19:08 +00:00
parent 0f7f27801e
commit 092ebf9879
9 changed files with 301 additions and 19 deletions

View file

@ -306,7 +306,8 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason,
lws_cgi_remove_and_kill(wsi->parent);
/* end the binding between us and master */
wsi->parent->http.cgi->stdwsi[(int)wsi->cgi_channel] =
if (wsi->parent->http.cgi)
wsi->parent->http.cgi->stdwsi[(int)wsi->cgi_channel] =
NULL;
}
wsi->socket_is_permanently_unusable = 1;

View file

@ -661,7 +661,7 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: %d %" PRIu64 "\n",
wsi->http.cgi->explicitly_chunked,
(uint64_t)wsi->http.cgi->content_length);
if (!wsi->http.cgi->explicitly_chunked &&
if (!(wsi->http.cgi->explicitly_chunked && wsi->http2_substream) &&
!wsi->http.cgi->content_length) {
/* send terminating chunk */
lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: ending\n");
@ -670,6 +670,10 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, 3);
break;
}
if (wsi->http2_substream && !wsi->cgi_stdout_zero_length)
lws_write(wsi, (unsigned char *)buf + LWS_PRE, 0,
LWS_WRITE_HTTP_FINAL);
if (lws_http_transaction_completed(wsi))
return -1;
return 0;

View file

@ -920,7 +920,7 @@ agin:
/* payload processing */
m = !wsi->http.cgi->implied_chunked && !wsi->http2_substream &&
!wsi->http.cgi->explicitly_chunked &&
// !wsi->http.cgi->explicitly_chunked &&
!wsi->http.cgi->content_length;
n = lws_get_socket_fd(wsi->http.cgi->stdwsi[LWS_STDOUT]);
if (n < 0)
@ -972,21 +972,20 @@ agin:
wsi->http.cgi->content_length_seen += n;
} else {
if (m) {
uint8_t term[LWS_PRE + 6];
if (!wsi->http2_substream && m) {
uint8_t term[LWS_PRE + 6];
memcpy(term + LWS_PRE, (uint8_t *)"0\x0d\x0a\x0d\x0a", 5);
lwsl_notice("%s: sent trailer\n", __func__);
memcpy(term + LWS_PRE, (uint8_t *)"0\x0d\x0a\x0d\x0a", 5);
if (lws_write(wsi, term + LWS_PRE, 5,
LWS_WRITE_HTTP_FINAL) != 5)
return -1;
wsi->http.cgi->cgi_transaction_over = 1;
return 0;
}
if (lws_write(wsi, term + LWS_PRE, 5,
LWS_WRITE_HTTP_FINAL) != 5)
return -1;
wsi->http.cgi->cgi_transaction_over = 1;
return 0;
}
if (wsi->cgi_stdout_zero_length) {
lwsl_debug("%s: stdout is POLLHUP'd\n", __func__);
@ -1064,12 +1063,14 @@ handled:
args.stdwsi = &wsi->http.cgi->stdwsi[0];
if (wsi->http.cgi->pid != -1) {
int m = wsi->http.cgi->being_closed;
n = user_callback_handle_rxflow(wsi->protocol->callback, wsi,
LWS_CALLBACK_CGI_TERMINATED,
wsi->user_space, (void *)&args,
wsi->http.cgi->pid);
wsi->http.cgi->pid = -1;
if (n && !wsi->http.cgi->being_closed)
if (wsi->http.cgi)
wsi->http.cgi->pid = -1;
if (n && !m)
lws_close_free_wsi(wsi, 0, "lws_cgi_kill");
}

View file

@ -271,6 +271,7 @@ struct _lws_http_mode_related {
unsigned int content_length_explicitly_zero:1;
unsigned int did_stream_close:1;
unsigned int multipart:1;
unsigned int cgi_transaction_complete:1;
unsigned int multipart_issue_boundary:1;
};

View file

@ -2153,10 +2153,13 @@ bail_nuke_ah:
}
#endif
LWS_VISIBLE int LWS_WARN_UNUSED_RESULT
int LWS_WARN_UNUSED_RESULT
lws_http_transaction_completed(struct lws *wsi)
{
int n = NO_PENDING_TIMEOUT;
int n;
if (wsi->http.cgi_transaction_complete)
return 0;
if (lws_has_buffered_out(wsi)
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
@ -2208,7 +2211,11 @@ lws_http_transaction_completed(struct lws *wsi)
#endif
lws_access_log(wsi);
if (!wsi->hdr_parsing_completed) {
if (!wsi->hdr_parsing_completed
#if defined(LWS_WITH_CGI)
&& !wsi->http.cgi
#endif
) {
char peer[64];
#if !defined(LWS_PLAT_OPTEE)
@ -2222,6 +2229,26 @@ lws_http_transaction_completed(struct lws *wsi)
return 0;
}
#if defined(LWS_WITH_CGI)
if (wsi->http.cgi) {
lwsl_debug("%s: cleaning cgi\n", __func__);
wsi->http.cgi_transaction_complete = 1;
lws_cgi_remove_and_kill(wsi);
for (n = 0; n < 3; n++) {
if (wsi->http.cgi->pipe_fds[n][!!(n == 0)] == 0)
lwsl_err("ZERO FD IN CGI CLOSE");
if (wsi->http.cgi->pipe_fds[n][!!(n == 0)] >= 0) {
close(wsi->http.cgi->pipe_fds[n][!!(n == 0)]);
wsi->http.cgi->pipe_fds[n][!!(n == 0)] = LWS_SOCK_INVALID;
}
}
lws_free_set_NULL(wsi->http.cgi);
wsi->http.cgi_transaction_complete = 0;
}
#endif
/* if we can't go back to accept new headers, drop the connection */
if (wsi->http2_substream)
return 1;
@ -2259,6 +2286,7 @@ lws_http_transaction_completed(struct lws *wsi)
lws_vfs_file_close(&wsi->http.fop_fd);
#endif
n = NO_PENDING_TIMEOUT;
if (wsi->vhost->keepalive_timeout)
n = PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE;
lws_set_timeout(wsi, n, wsi->vhost->keepalive_timeout);

View file

@ -0,0 +1,80 @@
cmake_minimum_required(VERSION 2.8)
include(CheckCSourceCompiles)
set(SAMP lws-minimal-http-server-cgi)
set(SRCS minimal-http-server.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 <libwebsockets.h>\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_WITH_CGI 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()

View file

@ -0,0 +1,28 @@
# lws minimal http server-cgi
## build
```
$ cmake . && make
```
## usage
This example runs a script ./my-cgi-script.sh when you vist /
The script dumps some information from /proc on stdout, which
is proxied back to the browser, script output on stderr is
printed in the console.
It's able to serve the script output over h1 using chunked encoding,
and over h2 having stripped the chunked encoding from the script
output.
```
$ ./lws-minimal-http-server-cgi
[2019/11/18 16:31:29:5481] U: LWS minimal http server | visit http://localhost:7681
[2019/11/18 16:31:40:2176] N: CGI-stderr: lwstest script stderr: REQUEST_METHOD was GET
```
Visit http://localhost:7681

View file

@ -0,0 +1,103 @@
/*
* lws-minimal-http-server-cgi
*
* Written in 2010-2019 by Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
* This demonstrates the most minimal http server you can make with lws.
*
* To keep it simple, it serves stuff from the subdirectory
* "./mount-origin" of the directory it was started in.
* You can change that by changing mount.origin below.
*/
#include <libwebsockets.h>
#include <string.h>
#include <signal.h>
static int interrupted;
static char cgi_script_fullpath[256];
static const struct lws_http_mount mount = {
/* .mount_next */ NULL, /* linked-list "next" */
/* .mountpoint */ "/", /* mountpoint URL */
/* .origin */ cgi_script_fullpath, /* cgi script */
/* .def */ "/", /* 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_CGI, /* 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 | visit http://localhost:7681\n");
{
char cwd[128];
cwd[0] = '\0';
getcwd(cwd, sizeof(cwd));
lws_snprintf(cgi_script_fullpath, sizeof(cgi_script_fullpath),
"%s/my-cgi-script.sh", cwd);
}
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
info.port = 7681;
info.mounts = &mount;
info.error_document_404 = "/404.html";
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;
}

View file

@ -0,0 +1,36 @@
#!/bin/sh
echo -e -n "content-type: text/html\x0d\x0a"
echo -e -n "transfer-encoding: chunked\x0d\x0a"
echo -e -n "\x0d\x0a"
echo "<html><body>"
echo "<h1>lwstest script stdout</h1>"
>&2 echo -n "lwstest script stderr: REQUEST_METHOD was $REQUEST_METHOD"
echo "<h2>REQUEST_METHOD=$REQUEST_METHOD</h2>"
if [ "$REQUEST_METHOD" = "POST" ] ; then
>&2 echo "lwstest script stderr: doing read"
echo "CONTENT_LENGTH=$CONTENT_LENGTH"
read -n $CONTENT_LENGTH line
>&2 echo "lwstest script stderr: done read"
echo "read=\"$line\""
else
echo "<table>"
echo "<tr><td colspan=\"2\" style=\"font-size:120%;text-align:center\">/proc/meminfo</td></tr>"
cat /proc/meminfo | while read line ; do
A=`echo "$line" | cut -d: -f1`
B=`echo "$line" | tr -s ' ' | cut -d' ' -f2-`
echo -e "<tr><td style=\"background-color:#f0e8c0\">$A</td>"
echo -e "<td style=\"text-align:right\">$B</td></tr>"
done
echo "</table>"
fi
echo "<br/>done"
echo "</body></html>"
exit 0