diff --git a/README.lwsws.md b/README.lwsws.md index a49ee01f..da109e85 100644 --- a/README.lwsws.md +++ b/README.lwsws.md @@ -15,8 +15,9 @@ It enables libuv and plugin support automatically. Configuration ------------- -lwsws uses JSON config files, there is a single file intended for global -settings +lwsws uses JSON config files, they're pure JSON but # may be used to turn the rest of the line into a comment. + +There is a single file intended for global settings /etc/lwsws/conf @@ -189,7 +190,14 @@ Mount protocols are used to control what kind of translation happens This allows you to customize one cgi depending on the mountpoint (and / or vhost). -Currently only a fixed set of mimetypes are supported. + It's also possible to set the cgi timeout (in secs) per cgi:// mount, like this + +``` + "cgi-timeout": "30" +``` + + +Note: currently only a fixed set of mimetypes are supported. Plugins diff --git a/lib/context.c b/lib/context.c index d8a04a8c..85d70947 100644 --- a/lib/context.c +++ b/lib/context.c @@ -52,7 +52,8 @@ static const char * const mount_protocols[] = { LWS_VISIBLE LWS_EXTERN int lws_write_http_mount(struct lws_http_mount *next, struct lws_http_mount **res, void *store, const char *mountpoint, const char *origin, - const char *def, struct lws_protocol_vhost_options *cgienv) + const char *def, struct lws_protocol_vhost_options *cgienv, + int cgi_timeout) { struct lws_http_mount *m; void *orig = store; @@ -71,6 +72,8 @@ lws_write_http_mount(struct lws_http_mount *next, struct lws_http_mount **res, m->mountpoint_len = (unsigned char)strlen(mountpoint); m->mount_next = NULL; m->cgienv = cgienv; + m->cgi_timeout = cgi_timeout; + if (next) next->mount_next = m; @@ -83,7 +86,7 @@ lws_write_http_mount(struct lws_http_mount *next, struct lws_http_mount **res, } if (n == ARRAY_SIZE(mount_protocols)) { - lwsl_err("unsupported protocol://\n"); + lwsl_err("unsupported protocol:// %s\n", origin); return 0; /* ie, fail */ } @@ -586,6 +589,8 @@ lws_create_context(struct lws_context_creation_info *info) lws_context_init_ssl_library(info); + context->user_space = info->user; + /* * if he's not saying he'll make his own vhosts later then act * compatibly and make a default vhost using the data in the info @@ -598,8 +603,6 @@ lws_create_context(struct lws_context_creation_info *info) lws_context_init_extensions(info, context); - context->user_space = info->user; - lwsl_notice(" mem: per-conn: %5u bytes + protocol rx buf\n", sizeof(struct lws)); diff --git a/lib/daemonize.c b/lib/daemonize.c index 5fbd60a7..e9162949 100644 --- a/lib/daemonize.c +++ b/lib/daemonize.c @@ -173,7 +173,7 @@ lws_daemonize(const char *_lock_path) * Change the current working directory. This prevents the current * directory from being locked; hence not being able to remove it. */ - if (chdir("/") < 0) { + if (chdir("/tmp") < 0) { fprintf(stderr, "unable to change directory to %s, code %d (%s)", "/", errno, strerror(errno)); diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index b85e6e9b..5fe0bdc8 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -530,6 +530,17 @@ lws_close_free_wsi_final(struct lws *wsi) wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY, wsi->user_space, NULL, 0); +#ifdef LWS_WITH_CGI + if (wsi->cgi) { + for (n = 0; n < 6; n++) + if (wsi->cgi->pipe_fds[n / 2][n & 1] >= 0) { + lwsl_notice(" closing %d\n", wsi->cgi->pipe_fds[n / 2][n & 1]); + close(wsi->cgi->pipe_fds[n / 2][n & 1]); + } + lws_free(wsi->cgi); + } +#endif + lws_free_wsi(wsi); } @@ -1644,7 +1655,7 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len char *env_array[30], cgi_path[400], e[1024], *p = e, *end = p + sizeof(e) - 1, tok[256], *t; struct lws_cgi *cgi; - int n, m, i; + int n, m, i, uritok = WSI_TOKEN_GET_URI; /* * give the master wsi a cgi struct @@ -1674,6 +1685,9 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len cgi->stdwsi[n]->cgi_channel = n; cgi->stdwsi[n]->vhost = wsi->vhost; + lwsl_err("%s: cgi %p: pipe fd %d -> fd %d / %d\n", __func__, wsi, n, + cgi->pipe_fds[n][!!(n == 0)], cgi->pipe_fds[n][!(n == 0)]); + /* read side is 0, stdin we want the write side, others read */ cgi->stdwsi[n]->sock = cgi->pipe_fds[n][!!(n == 0)]; fcntl(cgi->pipe_fds[n][!!(n == 0)], F_SETFL, O_NONBLOCK); @@ -1712,11 +1726,13 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len if (lws_is_ssl(wsi)) env_array[n++] = "HTTPS=ON"; if (wsi->u.hdr.ah) { + if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) + uritok = WSI_TOKEN_POST_URI; snprintf(cgi_path, sizeof(cgi_path) - 1, "REQUEST_URI=%s", - lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI)); + lws_hdr_simple_ptr(wsi, uritok)); cgi_path[sizeof(cgi_path) - 1] = '\0'; env_array[n++] = cgi_path; - if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) + if (uritok == WSI_TOKEN_POST_URI) env_array[n++] = "REQUEST_METHOD=POST"; else env_array[n++] = "REQUEST_METHOD=GET"; @@ -1748,7 +1764,7 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len env_array[n++] = p; p += snprintf(p, end - p, "PATH_INFO=%s", - lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI) + + lws_hdr_simple_ptr(wsi, uritok) + script_uri_path_len); p++; } @@ -1764,12 +1780,32 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST)); p++; } + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) { + env_array[n++] = p; + p += snprintf(p, end - p, "HTTP_COOKIE=%s", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COOKIE)); + 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++; } + if (uritok == WSI_TOKEN_POST_URI) { + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) { + env_array[n++] = p; + p += snprintf(p, end - p, "CONTENT_TYPE=%s", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)); + p++; + } + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) { + env_array[n++] = p; + p += snprintf(p, end - p, "CONTENT_LENGTH=%s", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)); + p++; + } + } env_array[n++] = p; p += snprintf(p, end - p, "SCRIPT_PATH=%s", exec_array[0]) + 1; @@ -1808,7 +1844,7 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len return 0; /* somewhere we can at least read things and enter it */ - if (chdir("/")) + if (chdir("/tmp")) lwsl_notice("%s: Failed to chdir\n", __func__); /* We are the forked process, redirect and kill inherited things. @@ -1818,13 +1854,11 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len * process is OK. Stuff that happens after the execvpe() is OK. */ - for (n = 0; n < 3; n++) { + for (n = 0; n < 3; n++) if (dup2(cgi->pipe_fds[n][!(n == 0)], n) < 0) { lwsl_err("%s: stdin dup2 failed\n", __func__); goto bail3; } - close(cgi->pipe_fds[n][!(n == 0)]); - } #if !defined(LWS_HAVE_VFORK) || !defined(LWS_HAVE_EXECVPE) for (m = 0; m < n; m++) { @@ -1872,9 +1906,13 @@ bail1: LWS_VISIBLE LWS_EXTERN int lws_cgi_write_split_stdout_headers(struct lws *wsi) { - int n, m; + int n, m, match = 0, lp = 0; + static const char * const content_length = "content-length: "; char buf[LWS_PRE + 1024], *start = &buf[LWS_PRE], *p = start, - *end = &buf[sizeof(buf) - 1 - LWS_PRE], c; + *end = &buf[sizeof(buf) - 1 - LWS_PRE], c, l[12]; + + if (!wsi->cgi) + return -1; while (wsi->hdr_state != LHCS_PAYLOAD) { /* we have to separate header / finalize and @@ -1883,20 +1921,40 @@ lws_cgi_write_split_stdout_headers(struct lws *wsi) */ n = read(lws_get_socket_fd(wsi->cgi->stdwsi[LWS_STDOUT]), &c, 1); if (n < 0) { - if (errno != EAGAIN) + if (errno != EAGAIN) { + lwsl_debug("%s: read says %d\n", __func__, n); return -1; + } else n = 0; } if (n) { - //lwsl_err("-- %c\n", c); + lwsl_debug("-- 0x%02X %c\n", (unsigned char)c, c); switch (wsi->hdr_state) { case LCHS_HEADER: + if (!content_length[match] && + (c >= '0' && c <= '9') && + lp < sizeof(l) - 1) { + l[lp++] = c; + l[lp] = '\0'; + wsi->cgi->content_length = atol(l); + } + if (tolower(c) == content_length[match]) + match++; + else + match = 0; + + /* some cgi only send us \x0a for EOL */ + if (c == '\x0a') { + wsi->hdr_state = LCHS_SINGLE_0A; + *p++ = '\x0d'; + } *p++ = c; if (c == '\x0d') { wsi->hdr_state = LCHS_LF1; break; } + break; case LCHS_LF1: *p++ = c; @@ -1905,17 +1963,24 @@ lws_cgi_write_split_stdout_headers(struct lws *wsi) 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; + match = 0; *p++ = c; break; case LCHS_LF2: + case LCHS_SINGLE_0A: + m = wsi->hdr_state; if (c == '\x0a') { + lwsl_err("Content-Length: %ld\n", wsi->cgi->content_length); wsi->hdr_state = LHCS_PAYLOAD; /* drop the \0xa ... finalize will add it if needed */ lws_finalize_http_header(wsi, @@ -1923,8 +1988,13 @@ lws_cgi_write_split_stdout_headers(struct lws *wsi) (unsigned char *)end); break; } - /* we got \r\n\r[^\n]... it's unreasonable */ - return -1; + if (m == LCHS_LF2) + /* we got \r\n\r[^\n]... it's unreasonable */ + return -1; + /* we got \x0anext header, it's reasonable */ + *p++ = c; + wsi->hdr_state = LCHS_HEADER; + break; case LHCS_PAYLOAD: break; } @@ -1935,8 +2005,10 @@ lws_cgi_write_split_stdout_headers(struct lws *wsi) m = lws_write(wsi, (unsigned char *)start, p - start, LWS_WRITE_HTTP_HEADERS); - if (m < 0) + if (m < 0) { + lwsl_debug("%s: write says %d\n", __func__, m); return -1; + } /* writeability becomes uncertain now we wrote * something, we must return to the event loop */ @@ -1944,18 +2016,22 @@ lws_cgi_write_split_stdout_headers(struct lws *wsi) return 0; } } - //lwsl_err("%s: stdout\n", __func__); + n = read(lws_get_socket_fd(wsi->cgi->stdwsi[LWS_STDOUT]), start, sizeof(buf) - LWS_PRE); - if (n < 0 && errno != EAGAIN) + if (n < 0 && errno != EAGAIN) { + lwsl_debug("%s: stdout read says %d\n", __func__, n); return -1; + } if (n > 0) { - m = lws_write(wsi, (unsigned char *)start, n, - LWS_WRITE_HTTP); + m = lws_write(wsi, (unsigned char *)start, n, LWS_WRITE_HTTP); //lwsl_notice("write %d\n", m); - if (m < 0) + if (m < 0) { + lwsl_debug("%s: stdout write says %d\n", __func__, m); return -1; + } + wsi->cgi->content_length_seen += m; } return 0; @@ -1979,10 +2055,6 @@ lws_cgi_kill(struct lws *wsi) if (!wsi->cgi) return 0; -// lwsl_notice("%s: wsi %p\n", __func__, wsi); - - assert(wsi->cgi); - if (wsi->cgi->pid > 0) { /* kill the process */ n = kill(wsi->cgi->pid, SIGTERM); @@ -2014,16 +2086,6 @@ lws_cgi_kill(struct lws *wsi) pcgi = &(*pcgi)->cgi_list; } - if (!do_close) - for (n = 0 ; n < 3; n++) { - if (wsi->cgi->pipe_fds[n][!!(n == 0)] >= 0) { - close(wsi->cgi->pipe_fds[n][!!(n == 0)]); - wsi->cgi->pipe_fds[n][!!(n == 0)] = -1; - } - } - - lws_free_set_NULL(wsi->cgi); - if (do_close) { lwsl_debug("!!!!! %s: do_close\n", __func__); lws_close_free_wsi(wsi, 0); @@ -2044,8 +2106,15 @@ lws_cgi_kill_terminated(struct lws_context_per_thread *pt) cgi = *pcgi; pcgi = &(*pcgi)->cgi_list; - if (cgi->pid > 0 && - waitpid(cgi->pid, &status, WNOHANG) > 0) { + if (cgi->pid <= 0) + continue; + + /* wait for stdout to be drained */ + if (cgi->content_length > cgi->content_length_seen) + continue; + + if (waitpid(cgi->pid, &status, WNOHANG) > 0) { + lwsl_notice("content length seen: %ld\n", cgi->content_length_seen); cgi->pid = 0; lws_cgi_kill(cgi->wsi); pcgi = &pt->cgi_list; diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index 9996cf2a..b03ce923 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -1551,7 +1551,8 @@ LWS_VISIBLE LWS_EXTERN int lws_write_http_mount(struct lws_http_mount *next, struct lws_http_mount **res, void *store, const char *mountpoint, const char *origin, const char *def, - struct lws_protocol_vhost_options *cgienv); + struct lws_protocol_vhost_options *cgienv, + int cgi_timeout); LWS_VISIBLE LWS_EXTERN void lws_set_log_level(int level, @@ -2011,6 +2012,7 @@ enum lws_cgi_hdr_state { LCHS_CR2, LCHS_LF2, LHCS_PAYLOAD, + LCHS_SINGLE_0A, }; struct lws_cgi_args { diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index cff84623..c2be3e7a 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -624,6 +624,8 @@ struct lws_http_mount { struct lws_protocol_vhost_options *cgienv; + int cgi_timeout; + unsigned char origin_protocol; unsigned char mountpoint_len; }; @@ -1121,6 +1123,8 @@ struct lws_cgi { struct lws_cgi *cgi_list; struct lws *stdwsi[3]; /* points to the associated stdin/out/err wsis */ struct lws *wsi; /* owner */ + unsigned long content_length; + unsigned long content_length_seen; int pipe_fds[3][2]; int pid; diff --git a/lib/server.c b/lib/server.c index aca8ad79..f001b5dd 100644 --- a/lib/server.c +++ b/lib/server.c @@ -234,7 +234,7 @@ int lws_http_serve(struct lws *wsi, char *uri, const char *origin) } lwsl_debug(" %s mode %d\n", path, S_IFMT & st.st_mode); - +#if !defined(WIN32) if ((S_IFMT & st.st_mode) == S_IFLNK) { if (readlink(path, sym, sizeof(sym))) { lwsl_err("Failed to read link %s\n", path); @@ -243,7 +243,7 @@ int lws_http_serve(struct lws *wsi, char *uri, const char *origin) lwsl_debug("symlink %s -> %s\n", path, sym); snprintf(path, sizeof(path) - 1, "%s", sym); } - +#endif if ((S_IFMT & st.st_mode) == S_IFDIR) { lwsl_debug("default filename append to dir\n"); snprintf(path, sizeof(path) - 1, "%s/%s/index.html", @@ -409,6 +409,7 @@ lws_http_action(struct lws *wsi) lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, wsi->context->timeout_secs); #ifdef LWS_OPENSSL_SUPPORT +#if 0 if (wsi->redirect_to_https) { /* * we accepted http:// only so we could redirect to @@ -435,6 +436,7 @@ lws_http_action(struct lws *wsi) return lws_http_transaction_completed(wsi); } +#endif #endif /* can we serve it from the mount list? */ @@ -447,7 +449,9 @@ lws_http_action(struct lws *wsi) uri_ptr[hm->mountpoint_len] == '/' || hm->mountpoint_len == 1) ) { - if (hm->mountpoint_len > best) { + if ((hm->origin_protocol == LWSMPRO_CGI || + lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI)) && + hm->mountpoint_len > best) { best = hm->mountpoint_len; hit = hm; } @@ -476,14 +480,15 @@ lws_http_action(struct lws *wsi) * understands he is one "directory level" down. */ if ((hit->mountpoint_len > 1 || (hit->origin_protocol & 4)) && - (*s != '/' || (hit->origin_protocol & 4))) { + (*s != '/' || (hit->origin_protocol & 4)) && + (hit->origin_protocol != LWSMPRO_CGI)) { unsigned char *start = pt->serv_buf + LWS_PRE, *p = start, *end = p + 512; static const char *oprot[] = { "http://", "https://" }; - // lwsl_err("inin '%s'\n", s); + lwsl_err("Doing 301 '%s' org %s\n", s, hit->origin); if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) goto bail_nuke_ah; @@ -527,7 +532,12 @@ lws_http_action(struct lws *wsi) lwsl_debug("%s: cgi\n", __func__); cmd[0] = hit->origin; - n = lws_cgi(wsi, cmd, hit->mountpoint_len, 5, + + n = 5; + if (hit->cgi_timeout) + n = hit->cgi_timeout; + + n = lws_cgi(wsi, cmd, hit->mountpoint_len, n, hit->cgienv); if (n) { lwsl_err("%s: cgi failed\n"); @@ -545,7 +555,7 @@ lws_http_action(struct lws *wsi) p - (buffer + LWS_PRE), LWS_WRITE_HTTP_HEADERS); - return 0; + goto deal_body; } #endif @@ -569,6 +579,9 @@ lws_http_action(struct lws *wsi) return 1; } +#ifdef LWS_WITH_CGI +deal_body: +#endif /* * If we're not issuing a file, check for content_length or * HTTP keep-alive. No keep-alive header allocation for @@ -611,7 +624,11 @@ lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len) while (len--) { wsi->more_rx_waiting = !!len; - assert(wsi->mode == LWSCM_HTTP_SERVING); + if (wsi->mode != LWSCM_HTTP_SERVING && + wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED) { + lwsl_err("%s: bad wsi mode %d\n", __func__, wsi->mode); + goto bail_nuke_ah; + } if (lws_parse(wsi, *(*buf)++)) { lwsl_info("lws_parse failed\n"); diff --git a/lib/service.c b/lib/service.c index 2e66812b..6ebe7140 100644 --- a/lib/service.c +++ b/lib/service.c @@ -53,6 +53,8 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd) #endif int ret, m, n; + //lwsl_err("%s: %p\n", __func__, wsi); + /* * user callback is lowest priority to get these notifications * actually, since other pending things cannot be disordered @@ -94,6 +96,12 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd) return 0; /* leave POLLOUT active */ } #endif + +#ifdef LWS_WITH_CGI + if (wsi->cgi) + goto user_service_go_again; +#endif + /* Priority 3: pending control packets (pong or close) */ if ((wsi->state == LWSS_ESTABLISHED && @@ -239,9 +247,14 @@ user_service: return 1; } + if (!wsi->hdr_parsing_completed) return 0; +#ifdef LWS_WITH_CGI +user_service_go_again: +#endif + #ifdef LWS_USE_HTTP2 /* * we are the 'network wsi' for potentially many muxed child wsi with @@ -738,9 +751,17 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t case LWSCM_HTTP_SERVING_ACCEPTED: case LWSCM_SERVER_LISTENER: case LWSCM_SSL_ACK_PENDING: - if (wsi->state == LWSS_CLIENT_HTTP_ESTABLISHED) { + if (wsi->state == LWSS_CLIENT_HTTP_ESTABLISHED) + goto handled; + +#ifdef LWS_WITH_CGI + if (wsi->cgi && (pollfd->revents & LWS_POLLOUT)) { + n = lws_handle_POLLOUT_event(wsi, pollfd); + if (n) + goto close_and_handled; goto handled; } +#endif n = lws_server_socket_service(context, wsi, pollfd); if (n) /* closed by above */ return 1; @@ -995,6 +1016,9 @@ drain: args.stdwsi = &wsi->parent->cgi->stdwsi[0]; args.hdr_state = wsi->hdr_state; + //lwsl_err("CGI LWS_STDOUT waiting wsi %p mode %d state %d\n", + // wsi->parent, wsi->parent->mode, wsi->parent->state); + if (user_callback_handle_rxflow( wsi->parent->protocol->callback, wsi->parent, LWS_CALLBACK_CGI, diff --git a/lwsws/conf.c b/lwsws/conf.c index 1cbc5ede..3bb1d5b9 100644 --- a/lwsws/conf.c +++ b/lwsws/conf.c @@ -48,6 +48,7 @@ static const char * const paths_vhosts[] = { "vhosts[].mounts[].mountpoint", "vhosts[].mounts[].origin", "vhosts[].mounts[].default", + "vhosts[].mounts[].cgi-timeout", "vhosts[].mounts[].cgi-env[].*", "vhosts[].ws-protocols[].*.*", "vhosts[].ws-protocols[].*", @@ -65,6 +66,7 @@ enum lejp_vhost_paths { LEJPVP_MOUNTPOINT, LEJPVP_ORIGIN, LEJPVP_DEFAULT, + LEJPVP_CGI_TIMEOUT, LEJPVP_CGI_ENV, LEJPVP_PROTOCOL_NAME_OPT, LEJPVP_PROTOCOL_NAME, @@ -81,6 +83,7 @@ struct jpargs { char *mountpoint, *origin, *def; struct lws_protocol_vhost_options *pvo; struct lws_protocol_vhost_options *mp_cgienv; + int cgi_timeout; }; static void * @@ -192,6 +195,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason) a->origin = NULL; a->def = NULL; a->mp_cgienv = NULL; + a->cgi_timeout = 0; } /* this catches, eg, vhosts[].ws-protocols[].xxx-protocol */ @@ -241,7 +245,8 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason) } n = lws_write_http_mount(a->last, &m, a->p, a->mountpoint, - a->origin, a->def, a->mp_cgienv); + a->origin, a->def, a->mp_cgienv, + a->cgi_timeout); if (!n) return 1; a->p += n; @@ -280,6 +285,9 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason) case LEJPVP_DEFAULT: a->def = a->p; break; + case LEJPVP_CGI_TIMEOUT: + a->cgi_timeout = atoi(ctx->buf); + return 0; case LEJPVP_CGI_ENV: mp_cgienv = lwsws_align(a); a->p += sizeof(*a->mp_cgienv); diff --git a/lwsws/etc-lwsws-conf-EXAMPLE b/lwsws/etc-lwsws-conf-EXAMPLE index ac2c9ac5..e6184d98 100644 --- a/lwsws/etc-lwsws-conf-EXAMPLE +++ b/lwsws/etc-lwsws-conf-EXAMPLE @@ -4,8 +4,8 @@ { "global": { - "uid": "99", - "gid": "99", + "uid": "48", + "gid": "48", "interface": "eth0", "count-threads": "1", "init-ssl": "yes" diff --git a/lwsws/etc-lwsws-conf.d-libwebsockets.org-EXAMPLE b/lwsws/etc-lwsws-conf.d-libwebsockets.org-EXAMPLE deleted file mode 100644 index b4e307db..00000000 --- a/lwsws/etc-lwsws-conf.d-libwebsockets.org-EXAMPLE +++ /dev/null @@ -1,50 +0,0 @@ -# comment - -{ - "vhosts": [ { - "name": "libwebsockets.org", - "port": "443", - "host-ssl-key": "/etc/pki/tls/private/libwebsockets.org.key", - "host-ssl-cert": "/etc/pki/tls/certs/libwebsockets.org.crt", - "host-ssl-ca": "/etc/pki/tls/certs/libwebsockets.org.cer", - "mounts": [{ - "mountpoint": "/", - "origin": "file:///var/www/libwebsockets.org", - "default": "index.html" - }, { - "mountpoint": "/git", - "origin": "http://git.warmcat.com", - "default": "/" - }, { - "mountpoint": "/mailman", - "origin": "cgi://usr/lib/mailman/cgi-bin/", - "default": "/list-info" - }] - }, - { - "name": "libwebsockets.org", # disambiguated by port, must be same for SNI - "port": "7681", - "host-ssl-key": "/etc/pki/tls/private/libwebsockets.org.key", - "host-ssl-cert": "/etc/pki/tls/certs/libwebsockets.org.crt", - "host-ssl-ca": "/etc/pki/tls/certs/libwebsockets.org.cer", - "ws-protocols": [{ - "wsprotocol": "dumb-increment-protocol", - "wsprotocol": "lws-mirror-protocol", - "wsprotocol": "lws-status" - }], - "ws-extensions": [{ - "extension": "permessage-deflate" - }], - "mounts": [{ - "mountpoint": "/", - "origin": "file:///usr/local/share/libwebsockets-test-server", - "default": "test.html" - }] - }, - { - "name": "libwebsockets.org", - "port": "80", - "global-redirect": "https://libwebsockets.org" - }] -} - diff --git a/lwsws/etc-lwsws-conf.d-localhost-EXAMPLE b/lwsws/etc-lwsws-conf.d-localhost-EXAMPLE new file mode 100644 index 00000000..43d0489d --- /dev/null +++ b/lwsws/etc-lwsws-conf.d-localhost-EXAMPLE @@ -0,0 +1,99 @@ +{ + "vhosts": [ { + "name": "localhost", + "port": "80", + "mounts": [{ + "mountpoint": "/", + "origin": "file:///var/www/libwebsockets.org", + "default": "index.html" + }, { + "mountpoint": "/git", + "origin": "cgi:///var/www/cgi-bin/cgit", + "default": "/", + "cgi-env": [{ + "CGIT_CONFIG": "/etc/cgitrc/libwebsockets.org" + }] + }, { + "mountpoint": "/cgit-data", + "origin": "file:///usr/share/cgit", + "default": "/" + }, { + "mountpoint": "/testcgi", + "origin": "cgi:///usr/local/share/libwebsockets-test-server/lws-cgi-test.sh" + }, { + "mountpoint": "/mailman", + "origin": ">http://localhost/mailman/listinfo" + }, { + "mountpoint": "/mailman/listinfo", + "origin": "cgi:///usr/lib/mailman/cgi-bin/listinfo" + }, { + "mountpoint": "/mailman/admin", + "origin": "cgi:///usr/lib/mailman/cgi-bin/admin" + }, { + "mountpoint": "/mailman/admindb", + "origin": "cgi:///usr/lib/mailman/cgi-bin/admindb" + }, { + "mountpoint": "/mailman/confirm", + "origin": "cgi:///usr/lib/mailman/cgi-bin/confirm" + }, { + "mountpoint": "/mailman/create", + "origin": "cgi:///usr/lib/mailman/cgi-bin/create" + }, { + "mountpoint": "/mailman/edithtml", + "origin": "cgi:///usr/lib/mailman/cgi-bin/edithtml" + }, { + "mountpoint": "/mailman/options", + "origin": "cgi:///usr/lib/mailman/cgi-bin/options" + }, { + "mountpoint": "/mailman/private", + "origin": "cgi:///usr/lib/mailman/cgi-bin/private" + }, { + "mountpoint": "/mailman/rmlist", + "origin": "cgi:///usr/lib/mailman/cgi-bin/rmlist" + }, { + "mountpoint": "/mailman/roster", + "origin": "cgi:///usr/lib/mailman/cgi-bin/roster" + }, { + "mountpoint": "/mailman/subscribe", + "origin": "cgi:///usr/lib/mailman/cgi-bin/subscribe" + }, { + "mountpoint": "/pipermail", + "origin": "file:///var/lib/mailman/archives/public", + "default": "index.html" + }, { + "mountpoint": "/testserver", + "origin": "file:///usr/local/share/libwebsockets-test-server", + "default": "test.html" + }], + # which protocols are enabled for this vhost, and optional + # vhost-specific config options for the protocol + # + "ws-protocols": [{ + "warmcat,timezoom": { + "status": "ok" + } + }] + }, + { + "name": "localhost", + "port": "7681", + "host-ssl-key": "/etc/pki/tls/private/libwebsockets.org.key", + "host-ssl-cert": "/etc/pki/tls/certs/libwebsockets.org.crt", + "host-ssl-ca": "/etc/pki/tls/certs/libwebsockets.org.cer", + "mounts": [{ + "mountpoint": "/", + "origin": ">https://localhost" + }] + }, + { + "name": "localhostx", + "port": "80", + "mounts": [{ + "mountpoint": "/", + "origin": ">https://localhost" + }] + } + + ] +} + diff --git a/lwsws/http.c b/lwsws/http.c index ff4ca98b..337981d7 100644 --- a/lwsws/http.c +++ b/lwsws/http.c @@ -71,7 +71,6 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, struct per_session_data__http *pss = (struct per_session_data__http *)user; unsigned char buffer[4096 + LWS_PRE]; - unsigned long amount, file_len, sent; char leaf_path[1024]; const char *mimetype; char *other_headers; @@ -137,9 +136,6 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, lwsl_err("proxy connect fail\n"); break; } - - - break; } #endif @@ -156,89 +152,7 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) return 0; - /* check for the "send a big file by hand" example case */ - lwsl_notice("%s\n", in); - if (!strcmp((const char *)in, "/leaf.jpg")) { - if (strlen(resource_path) > sizeof(leaf_path) - 10) - return -1; - sprintf(leaf_path, "%s/leaf.jpg", resource_path); - - /* well, let's demonstrate how to send the hard way */ - - p = buffer + LWS_PRE; - end = p + sizeof(buffer) - LWS_PRE; - - pss->fd = lws_plat_file_open(wsi, leaf_path, &file_len, - LWS_O_RDONLY); - - if (pss->fd == LWS_INVALID_FILE) { - lwsl_err("faild to open file %s\n", leaf_path); - return -1; - } - - /* - * we will send a big jpeg file, but it could be - * anything. Set the Content-Type: appropriately - * so the browser knows what to do with it. - * - * Notice we use the APIs to build the header, which - * will do the right thing for HTTP 1/1.1 and HTTP2 - * depending on what connection it happens to be working - * on - */ - if (lws_add_http_header_status(wsi, 200, &p, end)) - return 1; - if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SERVER, - (unsigned char *)"libwebsockets", - 13, &p, end)) - return 1; - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_CONTENT_TYPE, - (unsigned char *)"image/jpeg", - 10, &p, end)) - return 1; - if (lws_add_http_header_content_length(wsi, - file_len, &p, - end)) - return 1; - if (lws_finalize_http_header(wsi, &p, end)) - return 1; - - /* - * send the http headers... - * this won't block since it's the first payload sent - * on the connection since it was established - * (too small for partial) - * - * Notice they are sent using LWS_WRITE_HTTP_HEADERS - * which also means you can't send body too in one step, - * this is mandated by changes in HTTP2 - */ - - *p = '\0'; - lwsl_info("%s\n", buffer + LWS_PRE); - - n = lws_write(wsi, buffer + LWS_PRE, - p - (buffer + LWS_PRE), - LWS_WRITE_HTTP_HEADERS); - if (n < 0) { - lws_plat_file_close(wsi, pss->fd); - return -1; - } - /* - * book us a LWS_CALLBACK_HTTP_WRITEABLE callback - */ - lws_callback_on_writable(wsi); - break; - } - - /* if not, send a file the easy way */ - if (!strncmp(in, "/cgit-data/", 11)) { - in = (char *)in + 11; - strcpy(buf, "/usr/share/cgit"); - } else - strcpy(buf, resource_path); - + strcpy(buf, resource_path); if (strcmp(in, "/")) { if (*((const char *)in) != '/') strcat(buf, "/"); @@ -317,17 +231,14 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, goto try_to_reuse; case LWS_CALLBACK_HTTP_WRITEABLE: - lwsl_info("LWS_CALLBACK_HTTP_WRITEABLE\n"); + // lwsl_notice("LWS_CALLBACK_HTTP_WRITEABLE\n"); - if (pss->client_finished) - return -1; - - if (pss->fd == LWS_INVALID_FILE) - goto try_to_reuse; #ifdef LWS_WITH_CGI if (pss->reason_bf & 1) { - if (lws_cgi_write_split_stdout_headers(wsi) < 0) - goto bail; + if (lws_cgi_write_split_stdout_headers(wsi) < 0) { + lwsl_debug("lws_cgi_write_split_stdout_headers says close\n"); + return -1; + } pss->reason_bf &= ~1; break; @@ -348,13 +259,20 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, if (!wsi1) break; if (lws_http_client_read(wsi1, &px, &lenx) < 0) - goto bail; + return -1; if (pss->client_finished) return -1; break; } #endif +#if 0 + if (pss->client_finished) + return -1; + + if (pss->fd == LWS_INVALID_FILE) + goto try_to_reuse; + /* * we can send more of whatever it is we were sending */ @@ -416,16 +334,7 @@ bail: lws_plat_file_close(wsi, pss->fd); return -1; - - /* - * callback for confirming to continue with client IP appear in - * protocol 0 callback since no websocket protocol has been agreed - * yet. You can just ignore this if you won't filter on client IP - * since the default unhandled callback return is 0 meaning let the - * connection continue. - */ - case LWS_CALLBACK_FILTER_NETWORK_CONNECTION: - /* if we returned non-zero from here, we kill the connection */ +#endif break; #ifndef LWS_NO_CLIENT @@ -516,7 +425,7 @@ bail: case LWS_STDIN: /* TBD stdin rx flow control */ break; - case LWS_STDOUT: + case LWS_STDOUT:; pss->reason_bf |= 1; /* when writing to MASTER would not block */ lws_callback_on_writable(wsi); @@ -541,8 +450,10 @@ bail: 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); + pss->args.data[pss->args.len] = '\0'; + //lwsl_err("(stdin fd = %d) %s\n", lws_get_socket_fd(pss->args.stdwsi[LWS_STDIN]), pss->args.data); 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); @@ -558,59 +469,14 @@ bail: */ case LWS_CALLBACK_LOCK_POLL: - /* - * lock mutex to protect pollfd state - * called before any other POLL related callback - * if protecting wsi lifecycle change, len == 1 - */ test_server_lock(len); break; case LWS_CALLBACK_UNLOCK_POLL: - /* - * unlock mutex to protect pollfd state when - * called after any other POLL related callback - * if protecting wsi lifecycle change, len == 1 - */ test_server_unlock(len); break; -#ifdef EXTERNAL_POLL - case LWS_CALLBACK_ADD_POLL_FD: - - if (count_pollfds >= max_poll_elements) { - lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n"); - return 1; - } - - fd_lookup[pa->fd] = count_pollfds; - pollfds[count_pollfds].fd = pa->fd; - pollfds[count_pollfds].events = pa->events; - pollfds[count_pollfds++].revents = 0; - break; - - case LWS_CALLBACK_DEL_POLL_FD: - if (!--count_pollfds) - break; - m = fd_lookup[pa->fd]; - /* have the last guy take up the vacant slot */ - pollfds[m] = pollfds[count_pollfds]; - fd_lookup[pollfds[count_pollfds].fd] = m; - break; - - case LWS_CALLBACK_CHANGE_MODE_POLL_FD: - pollfds[fd_lookup[pa->fd]].events = pa->events; - break; -#endif - case LWS_CALLBACK_GET_THREAD_ID: - /* - * if you will call "lws_callback_on_writable" - * from a different thread, return the caller thread ID - * here so lws can use this information to work out if it - * should signal the poll() loop to exit and restart early - */ - /* return pthread_getthreadid_np(); */ break; diff --git a/lwsws/lejp.h b/lwsws/lejp.h index 5832e998..bbed3005 100644 --- a/lwsws/lejp.h +++ b/lwsws/lejp.h @@ -155,7 +155,7 @@ typedef char (*lejp_callback)(struct lejp_ctx *ctx, char reason); #endif #ifndef LEJP_STRING_CHUNK /* must be >= 30 to assemble floats */ -#define LEJP_STRING_CHUNK 64 +#define LEJP_STRING_CHUNK 128 #endif enum num_flags { diff --git a/test-server/lws-cgi-test.sh b/test-server/lws-cgi-test.sh index 914673ff..cc14af29 100755 --- a/test-server/lws-cgi-test.sh +++ b/test-server/lws-cgi-test.sh @@ -5,12 +5,16 @@ echo -e -n "\x0d\x0a" echo "
" echo "