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:
parent
0f7f27801e
commit
092ebf9879
9 changed files with 301 additions and 19 deletions
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
36
minimal-examples/http-server/minimal-http-server-cgi/my-cgi-script.sh
Executable file
36
minimal-examples/http-server/minimal-http-server-cgi/my-cgi-script.sh
Executable 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
|
||||
|
Loading…
Add table
Reference in a new issue