Improve cgi support so it's capable of running cgit
This commit is contained in:
Andy Green 2016-03-20 11:55:25 +08:00
parent 1e5a9ad2dc
commit 1a13885afd
13 changed files with 168 additions and 44 deletions

View file

@ -13,6 +13,8 @@ env:
- LWS_METHOD=http2 CMAKE_ARGS="-DLWS_WITH_HTTP2=ON"
- LWS_METHOD=nossl CMAKE_ARGS="-DLWS_WITH_SSL=OFF"
- LWS_METHOD=nodaemon CMAKE_ARGS="-DLWS_WITHOUT_DAEMONIZE=ON"
- LWS_METHOD=cgi CMAKE_ARGS="-DLWS_WITH_CGI=ON"
os:
- linux
- osx

View file

@ -344,6 +344,7 @@ CHECK_FUNCTION_EXISTS(realloc LWS_HAVE_REALLOC)
CHECK_FUNCTION_EXISTS(socket LWS_HAVE_SOCKET)
CHECK_FUNCTION_EXISTS(strerror LWS_HAVE_STRERROR)
CHECK_FUNCTION_EXISTS(vfork LWS_HAVE_VFORK)
CHECK_FUNCTION_EXISTS(execvpe LWS_HAVE_EXECVPE)
CHECK_FUNCTION_EXISTS(getifaddrs LWS_HAVE_GETIFADDRS)
CHECK_FUNCTION_EXISTS(snprintf LWS_HAVE_SNPRINTF)
CHECK_FUNCTION_EXISTS(_snprintf LWS_HAVE__SNPRINTF)

View file

@ -111,7 +111,8 @@ lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd,
a simple api.
LWS_VISIBLE LWS_EXTERN int
lws_cgi(struct lws *wsi, char * const *exec_array, int timeout_secs);
lws_cgi(struct lws *wsi, char * const *exec_array, int script_uri_path_len,
int timeout_secs);
LWS_VISIBLE LWS_EXTERN int
lws_cgi_kill(struct lws *wsi);
@ -131,6 +132,9 @@ $ wget http://localhost:7681/cgitest --post-file=hello.txt -O- --quiet
lwstest script
read="hello"
The test script returns text/html table showing /proc/meminfo. But the cgi
support is complete enough to run cgit cgi.
4) There is a helper api for forming logging timestamps
LWS_VISIBLE int

View file

@ -338,7 +338,7 @@ lws_client_reset(struct lws *wsi, int ssl, const char *address, int port, const
return lws_client_connect_2(wsi);
}
#ifdef LWS_WITH_HTTP_PROXY
static hubbub_error
html_parser_cb(const hubbub_token *token, void *pw)
{
@ -446,7 +446,7 @@ html_parser_cb(const hubbub_token *token, void *pw)
return HUBBUB_OK;
}
#endif
/**
* lws_client_connect_via_info() - Connect to another websocket server
* @i:pointer to lws_client_connect_info struct
@ -560,11 +560,12 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
wsi->sibling_list = i->parent_wsi->child_list;
i->parent_wsi->child_list = wsi;
}
#ifdef LWS_WITH_HTTP_PROXY
if (i->uri_replace_to)
wsi->rw = lws_rewrite_create(wsi, html_parser_cb,
i->uri_replace_from,
i->uri_replace_to);
#endif
return wsi;

View file

@ -501,7 +501,7 @@ strtolower(char *s)
* transaction if possible
*/
LWS_VISIBLE int LWS_WARN_UNUSED_RESULT
int LWS_WARN_UNUSED_RESULT
lws_http_transaction_completed_client(struct lws *wsi)
{
lwsl_debug("%s: wsi %p\n", __func__, wsi);
@ -622,7 +622,7 @@ lws_client_interpret_server_handshake(struct lws *wsi)
goto bail2;
}
#ifndef LWS_NO_CLIENT
#ifdef LWS_WITH_HTTP_PROXY
wsi->perform_rewrite = 0;
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) {
if (!strncmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE),

View file

@ -171,7 +171,7 @@ http_postbody:
wsi, LWS_CALLBACK_CGI_STDIN_DATA,
wsi->user_space,
(void *)&args, 0);
if (n < 0)
if ((int)n < 0)
goto bail;
} else {
#endif

View file

@ -391,12 +391,12 @@ just_kill_connection:
lwsl_info("%s: real just_kill_connection: %p (sockfd %d)\n", __func__,
wsi, wsi->sock);
#ifdef LWS_WITH_HTTP_PROXY
if (wsi->rw) {
lws_rewrite_destroy(wsi->rw);
wsi->rw = NULL;
}
#endif
/*
* we won't be servicing or receiving anything further from this guy
* delete socket from the internal poll list if still present
@ -1483,6 +1483,35 @@ lws_socket_bind(struct lws_context *context, int sockfd, int port,
return port;
}
LWS_VISIBLE LWS_EXTERN int
lws_urlencode(const char *in, int inlen, char *out, int outlen)
{
const char *hex = "0123456789ABCDEF";
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++;
else {
*out++ = '%';
*out++ = hex[(*in) >> 4];
*out++ = hex[(*in++) & 15];
}
}
*out = '\0';
if (out >= end - 4)
return -1;
return out - start;
}
LWS_VISIBLE LWS_EXTERN int
lws_is_cgi(struct lws *wsi) {
#ifdef LWS_WITH_CGI
@ -1546,12 +1575,14 @@ lws_create_basic_wsi(struct lws_context *context, int tsi)
*/
LWS_VISIBLE LWS_EXTERN int
lws_cgi(struct lws *wsi, char * const *exec_array, int timeout_secs)
lws_cgi(struct lws *wsi, char * const *exec_array, int script_uri_path_len,
int timeout_secs)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
char *env_array[30], cgi_path[400];
char *env_array[30], cgi_path[400], e[1024], *p = e,
*end = p + sizeof(e) - 1, tok[256];
struct lws_cgi *cgi;
int n;
int n, m, i;
/*
* give the master wsi a cgi struct
@ -1612,8 +1643,11 @@ lws_cgi(struct lws *wsi, char * const *exec_array, int timeout_secs)
/* prepare his CGI env */
n = 0;
if (lws_is_ssl(wsi))
env_array[n++] = "HTTPS=ON";
if (wsi->u.hdr.ah) {
snprintf(cgi_path, sizeof(cgi_path) - 1, "PATH_INFO=%s",
snprintf(cgi_path, sizeof(cgi_path) - 1, "REQUEST_URI=%s",
lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI));
cgi_path[sizeof(cgi_path) - 1] = '\0';
env_array[n++] = cgi_path;
@ -1621,18 +1655,66 @@ lws_cgi(struct lws *wsi, char * const *exec_array, int timeout_secs)
env_array[n++] = "REQUEST_METHOD=POST";
else
env_array[n++] = "REQUEST_METHOD=GET";
env_array[n++] = p;
p += snprintf(p, end - p, "QUERY_STRING=");
/* dump the individual URI Arg parameters */
m = 0;
while (1) {
i = lws_hdr_copy_fragment(wsi, tok, sizeof(tok),
WSI_TOKEN_HTTP_URI_ARGS, m);
if (i < 0)
break;
i = lws_urlencode(tok, i, p, end - p);
p += i;
*p++ = '&';
m++;
}
if (m)
p--;
*p++ = '\0';
env_array[n++] = p;
p += snprintf(p, end - p, "PATH_INFO=%s",
lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI) +
script_uri_path_len);
p++;
}
if (lws_is_ssl(wsi))
env_array[n++] = "HTTPS=ON";
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_REFERER)) {
env_array[n++] = p;
p += snprintf(p, end - p, "HTTP_REFERER=%s",
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_REFERER));
p++;
}
if (lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
env_array[n++] = p;
p += snprintf(p, end - p, "HTTP_HOST=%s",
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
p++;
}
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT)) {
env_array[n++] = p;
p += snprintf(p, end - p, "USER_AGENT=%s",
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_USER_AGENT));
p++;
}
env_array[n++] = p;
p += snprintf(p, end - p, "SCRIPT_PATH=%s", exec_array[2]) + 1;
env_array[n++] = "SERVER_SOFTWARE=libwebsockets";
env_array[n++] = "PATH=/bin:/usr/bin:/usrlocal/bin";
env_array[n++] = "PATH=/bin:/usr/bin:/usr/local/bin:/var/www/cgi-bin";
env_array[n] = NULL;
#if 0
for (m = 0; m < n; m++)
lwsl_err(" %s\n", env_array[m]);
#endif
/* we are ready with the redirection pipes... run the thing */
#ifdef LWS_HAVE_VFORK
cgi->pid = vfork();
#else
#if !defined(LWS_HAVE_VFORK) || !defined(LWS_HAVE_EXECVPE)
cgi->pid = fork();
#else
cgi->pid = vfork();
#endif
if (cgi->pid < 0) {
lwsl_err("fork failed, errno %d", errno);
@ -1658,7 +1740,17 @@ lws_cgi(struct lws *wsi, char * const *exec_array, int timeout_secs)
close(cgi->pipe_fds[n][!(n == 0)]);
}
#if !defined(LWS_HAVE_VFORK) || !defined(LWS_HAVE_EXECVPE)
for (m = 0; m < n; m++) {
p = strchr(env_array[m], '=');
*p++ = '\0';
setenv(env_array[m], p, 1);
}
execvp(exec_array[0], &exec_array[0]);
#else
execvpe(exec_array[0], &exec_array[0], &env_array[0]);
#endif
exit(1);
bail3:
@ -1711,7 +1803,7 @@ lws_cgi_write_split_stdout_headers(struct lws *wsi)
n = 0;
}
if (n) {
lwsl_err("-- %c\n", c);
//lwsl_err("-- %c\n", c);
switch (wsi->hdr_state) {
case LCHS_HEADER:
*p++ = c;
@ -1799,7 +1891,7 @@ lws_cgi_kill(struct lws *wsi)
if (!wsi->cgi)
return 0;
lwsl_notice("%s: wsi %p\n", __func__, wsi);
// lwsl_notice("%s: wsi %p\n", __func__, wsi);
assert(wsi->cgi);

View file

@ -1890,7 +1890,8 @@ struct lws_cgi_args {
};
LWS_VISIBLE LWS_EXTERN int
lws_cgi(struct lws *wsi, char * const *exec_array, int timeout_secs);
lws_cgi(struct lws *wsi, char * const *exec_array, int script_uri_path_len,
int timeout_secs);
LWS_VISIBLE LWS_EXTERN int
lws_cgi_write_split_stdout_headers(struct lws *wsi);

View file

@ -1124,7 +1124,7 @@ struct lws {
BIO *client_bio;
struct lws *pending_read_list_prev, *pending_read_list_next;
#endif
#ifndef LWS_NO_CLIENT
#ifdef LWS_WITH_HTTP_PROXY
struct lws_rewrite *rw;
#endif
#ifdef LWS_LATENCY
@ -1154,6 +1154,8 @@ struct lws {
unsigned int do_ws:1; /* whether we are doing http or ws flow */
unsigned int chunked:1; /* if the clientside connection is chunked */
unsigned int client_rx_avail:1;
#endif
#ifdef LWS_WITH_HTTP_PROXY
unsigned int perform_rewrite:1;
#endif
#ifndef LWS_NO_EXTENSIONS
@ -1530,9 +1532,6 @@ static LWS_INLINE int hstrcmp(hubbub_string *s, const char *p, int len)
return strncmp((const char *)s->ptr, p, len);
}
typedef hubbub_error (*hubbub_callback_t)(const hubbub_token *token, void *pw);
LWS_EXTERN int lws_client_socket_service(struct lws_context *context,
struct lws *wsi,
struct lws_pollfd *pollfd);
LWS_EXTERN struct lws_rewrite *
lws_rewrite_create(struct lws *wsi, hubbub_callback_t cb, const char *from, const char *to);
LWS_EXTERN void
@ -1542,6 +1541,11 @@ lws_rewrite_parse(struct lws_rewrite *r, const unsigned char *in, int in_len);
#endif
#ifndef LWS_NO_CLIENT
LWS_EXTERN int lws_client_socket_service(struct lws_context *context,
struct lws *wsi,
struct lws_pollfd *pollfd);
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
lws_http_transaction_completed_client(struct lws *wsi);
#ifdef LWS_OPENSSL_SUPPORT
LWS_EXTERN int
lws_context_init_client_ssl(struct lws_context_creation_info *info,

View file

@ -473,10 +473,8 @@ lws_service_flag_pending(struct lws_context *context, int tsi)
return forced;
}
#ifndef LWS_NO_CLIENT
/*
*
*/
LWS_VISIBLE int
lws_http_client_read(struct lws *wsi, char **buf, int *len)
{
@ -550,7 +548,7 @@ spin_chunks:
return 0;
if (wsi->u.http.content_remain &&
wsi->u.http.content_remain < *len)
(int)wsi->u.http.content_remain < *len)
n = wsi->u.http.content_remain;
else
n = *len;
@ -559,11 +557,12 @@ spin_chunks:
wsi->chunk_remaining < n)
n = wsi->chunk_remaining;
#ifdef LWS_WITH_HTTP_PROXY
/* hubbub */
if (wsi->perform_rewrite)
lws_rewrite_parse(wsi->rw, (unsigned char *)*buf, n);
else
#endif
if (user_callback_handle_rxflow(wsi->protocol->callback,
wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ,
wsi->user_space, *buf, n))
@ -595,11 +594,12 @@ completed:
wsi->user_space, NULL, 0))
return -1;
if (lws_http_transaction_completed(wsi))
if (lws_http_transaction_completed_client(wsi))
return -1;
return 0;
}
#endif
/**
* lws_service_fd() - Service polled socket with something waiting
@ -878,6 +878,7 @@ read:
}
drain:
#ifndef LWS_NO_CLIENT
if (wsi->mode == LWSCM_HTTP_CLIENT_ACCEPTED) {
/*
@ -894,9 +895,8 @@ drain:
wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP,
wsi->user_space, NULL, 0))
goto close_and_handled;
}
#endif
/*
* give any active extensions a chance to munge the buffer
* before parse. We pass in a pointer to an lws_tokens struct

View file

@ -96,6 +96,9 @@
/* Define to 1 if `vfork' works. */
#cmakedefine LWS_HAVE_WORKING_VFORK
/* Define to 1 if execvpe() exists */
#cmakedefine LWS_HAVE_EXECVPE
/* Define to 1 if you have the <zlib.h> header file. */
#cmakedefine LWS_HAVE_ZLIB_H

View file

@ -104,6 +104,9 @@ const char * get_mimetype(const char *file)
if (!strcmp(&file[n - 5], ".html"))
return "text/html";
if (!strcmp(&file[n - 4], ".css"))
return "text/css";
return NULL;
}
@ -117,7 +120,7 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
void *in, size_t len)
{
struct per_session_data__http *pss =
(struct per_session_data__http *)user, *pss1;
(struct per_session_data__http *)user;
unsigned char buffer[4096 + LWS_PRE];
unsigned long amount, file_len, sent;
char leaf_path[1024];
@ -126,7 +129,10 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
unsigned char *end;
struct timeval tv;
unsigned char *p;
#ifndef LWS_NO_CLIENT
struct per_session_data__http *pss1;
struct lws *wsi1;
#endif
char buf[256];
char b64[64];
int n, m;
@ -178,7 +184,7 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
i.port = 80;
i.ssl_connection = 0;
if (p[10])
i.path = in + 10;
i.path = (char *)in + 10;
else
i.path = rootpath;
i.host = "git.libwebsockets.org";
@ -198,23 +204,26 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
}
#endif
#if 1
/* this example server has no concept of directories */
if (strchr((const char *)in + 1, '/')) {
lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
goto try_to_reuse;
}
#endif
#ifdef LWS_WITH_CGI
if (!strcmp(in, "/cgitest")) {
if (!strncmp(in, "/cgitest", 8)) {
static char *cmd[] = {
"/bin/sh",
"-c",
INSTALL_DATADIR"/libwebsockets-test-server/lws-cgi-test.sh",
// "/var/www/cgi-bin/cgit",
NULL
};
lwsl_notice("%s: cgitest\n", __func__);
n = lws_cgi(wsi, cmd, 5);
n = lws_cgi(wsi, cmd, 8, 5);
if (n) {
lwsl_err("%s: cgi failed\n");
return -1;
@ -320,11 +329,16 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
}
/* if not, send a file the easy way */
strcpy(buf, resource_path);
if (!strncmp(in, "/cgit-data/", 11)) {
in = (char *)in + 11;
strcpy(buf, "/usr/share/cgit");
} else
strcpy(buf, resource_path);
if (strcmp(in, "/")) {
if (*((const char *)in) != '/')
strcat(buf, "/");
strncat(buf, in, sizeof(buf) - strlen(resource_path));
strncat(buf, in, sizeof(buf) - strlen(buf) - 1);
} else /* default file to serve */
strcat(buf, "/test.html");
buf[sizeof(buf) - 1] = '\0';
@ -510,7 +524,7 @@ bail:
/* if we returned non-zero from here, we kill the connection */
break;
#ifndef LWS_WITH_CLIENT
#ifndef LWS_NO_CLIENT
case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: {
char ctype[64], ctlen = 0;
lwsl_err("LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP\n");
@ -618,16 +632,16 @@ bail:
break;
case LWS_CALLBACK_CGI_TERMINATED:
lwsl_notice("LWS_CALLBACK_CGI_TERMINATED\n");
//lwsl_notice("LWS_CALLBACK_CGI_TERMINATED\n");
/* because we sent on openended http, close the connection */
return -1;
case LWS_CALLBACK_CGI_STDIN_DATA: /* POST body for stdin */
lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA\n");
//lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA\n");
pss->args = *((struct lws_cgi_args *)in);
n = write(lws_get_socket_fd(pss->args.stdwsi[LWS_STDIN]),
pss->args.data, pss->args.len);
lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: write says %d", n);
//lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: write says %d", n);
if (n < pss->args.len)
lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: sent %d only %d went",
n, pss->args.len);

View file

@ -69,6 +69,8 @@ struct per_session_data__http {
lws_filefd_type fd;
#ifdef LWS_WITH_CGI
struct lws_cgi_args args;
#endif
#if defined(LWS_WITH_CGI) || !defined(LWS_NO_CLIENT)
int reason_bf;
#endif
unsigned int client_finished:1;