- avoid closing descriptors before removing them from uloop (#11755, #11830)
- do not auto-initialize ubus if no prefix is set (#11832)
- remove extraneous client context pointer from cgi and lua states
- code cleanups and debug message changes
SVN-Revision: 32651
include $(TOPDIR)/rules.mk
PKG_NAME:=uhttpd
-PKG_RELEASE:=37
+PKG_RELEASE:=38
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
PKG_CONFIG_DEPENDS := \
static void uh_cgi_shutdown(struct uh_cgi_state *state)
{
- close(state->rfd);
- close(state->wfd);
free(state);
}
char buf[UH_LIMIT_MSGHEAD];
struct uh_cgi_state *state = (struct uh_cgi_state *)cl->priv;
- struct http_response *res = &state->cl->response;
- struct http_request *req = &state->cl->request;
+ struct http_response *res = &cl->response;
+ struct http_request *req = &cl->request;
/* there is unread post data waiting */
while (state->content_length > 0)
{
/* remaining data in http head buffer ... */
- if (state->cl->httpbuf.len > 0)
+ if (cl->httpbuf.len > 0)
{
- len = min(state->content_length, state->cl->httpbuf.len);
+ len = min(state->content_length, cl->httpbuf.len);
- D("CGI: Child(%d) feed %d HTTP buffer bytes\n",
- state->cl->proc.pid, len);
+ D("CGI: Child(%d) feed %d HTTP buffer bytes\n", cl->proc.pid, len);
- memcpy(buf, state->cl->httpbuf.ptr, len);
+ memcpy(buf, cl->httpbuf.ptr, len);
- state->cl->httpbuf.len -= len;
- state->cl->httpbuf.ptr +=len;
+ cl->httpbuf.len -= len;
+ cl->httpbuf.ptr +=len;
}
/* read it from socket ... */
else
{
- len = uh_tcp_recv(state->cl, buf,
+ len = uh_tcp_recv(cl, buf,
min(state->content_length, sizeof(buf)));
if ((len < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
break;
D("CGI: Child(%d) feed %d/%d TCP socket bytes\n",
- state->cl->proc.pid, len,
- min(state->content_length, sizeof(buf)));
+ cl->proc.pid, len, min(state->content_length, sizeof(buf)));
}
if (len)
state->content_length = 0;
/* ... write to CGI process */
- len = uh_raw_send(state->wfd, buf, len,
+ len = uh_raw_send(cl->wpipe.fd, buf, len,
cl->server->conf->script_timeout);
/* explicit EOF notification for the child */
if (state->content_length <= 0)
- close(state->wfd);
+ uh_ufd_remove(&cl->wpipe);
}
/* try to read data from child */
- while ((len = uh_raw_recv(state->rfd, buf, sizeof(buf), -1)) > 0)
+ while ((len = uh_raw_recv(cl->rpipe.fd, buf, sizeof(buf), -1)) > 0)
{
/* we have not pushed out headers yet, parse input */
if (!state->header_sent)
if (uh_cgi_header_parse(res, state->httpbuf, len, &hdroff))
{
/* write status */
- ensure_out(uh_http_sendf(state->cl, NULL,
+ ensure_out(uh_http_sendf(cl, NULL,
"HTTP/%.1f %03d %s\r\n"
"Connection: close\r\n",
req->version, res->statuscode, res->statusmsg));
if (!uh_cgi_header_lookup(res, "Location") &&
!uh_cgi_header_lookup(res, "Content-Type"))
{
- ensure_out(uh_http_send(state->cl, NULL,
+ ensure_out(uh_http_send(cl, NULL,
"Content-Type: text/plain\r\n", -1));
}
if ((req->version > 1.0) &&
!uh_cgi_header_lookup(res, "Transfer-Encoding"))
{
- ensure_out(uh_http_send(state->cl, NULL,
+ ensure_out(uh_http_send(cl, NULL,
"Transfer-Encoding: chunked\r\n", -1));
}
/* write headers from CGI program */
foreach_header(i, res->headers)
{
- ensure_out(uh_http_sendf(state->cl, NULL, "%s: %s\r\n",
+ ensure_out(uh_http_sendf(cl, NULL, "%s: %s\r\n",
res->headers[i], res->headers[i+1]));
}
/* terminate header */
- ensure_out(uh_http_send(state->cl, NULL, "\r\n", -1));
+ ensure_out(uh_http_send(cl, NULL, "\r\n", -1));
state->header_sent = true;
if (hdroff < len)
{
D("CGI: Child(%d) relaying %d rest bytes\n",
- state->cl->proc.pid, len - hdroff);
+ cl->proc.pid, len - hdroff);
- ensure_out(uh_http_send(state->cl, req,
+ ensure_out(uh_http_send(cl, req,
&buf[hdroff], len - hdroff));
}
}
* build the required headers here.
*/
- ensure_out(uh_http_sendf(state->cl, NULL,
+ ensure_out(uh_http_sendf(cl, NULL,
"HTTP/%.1f 200 OK\r\n"
"Content-Type: text/plain\r\n"
"%s\r\n",
state->header_sent = true;
D("CGI: Child(%d) relaying %d invalid bytes\n",
- state->cl->proc.pid, len);
+ cl->proc.pid, len);
- ensure_out(uh_http_send(state->cl, req, buf, len));
+ ensure_out(uh_http_send(cl, req, buf, len));
}
}
else
{
/* headers complete, pass through buffer to socket */
- D("CGI: Child(%d) relaying %d normal bytes\n",
- state->cl->proc.pid, len);
-
- ensure_out(uh_http_send(state->cl, req, buf, len));
+ D("CGI: Child(%d) relaying %d normal bytes\n", cl->proc.pid, len);
+ ensure_out(uh_http_send(cl, req, buf, len));
}
}
if ((len == 0) ||
((errno != EAGAIN) && (errno != EWOULDBLOCK) && (len == -1)))
{
- D("CGI: Child(%d) presumed dead [%s]\n",
- state->cl->proc.pid, strerror(errno));
+ D("CGI: Child(%d) presumed dead [%s]\n", cl->proc.pid, strerror(errno));
goto out;
}
out:
if (!state->header_sent)
{
- if (state->cl->timeout.pending)
- uh_http_sendhf(state->cl, 502, "Bad Gateway",
+ if (cl->timeout.pending)
+ uh_http_sendhf(cl, 502, "Bad Gateway",
"The CGI process did not produce any response\n");
else
- uh_http_sendhf(state->cl, 504, "Gateway Timeout",
+ uh_http_sendhf(cl, 504, "Gateway Timeout",
"The CGI process took too long to produce a "
"response\n");
}
else
{
- uh_http_send(state->cl, req, "", 0);
+ uh_http_send(cl, req, "", 0);
}
uh_cgi_shutdown(state);
default:
memset(state, 0, sizeof(*state));
- state->cl = cl;
- state->cl->pipe.fd = rfd[0];
- state->cl->proc.pid = child;
+ cl->rpipe.fd = rfd[0];
+ cl->wpipe.fd = wfd[1];
+ cl->proc.pid = child;
+
+ /* make pipe non-blocking */
+ fd_nonblock(cl->rpipe.fd);
+ fd_nonblock(cl->wpipe.fd);
/* close unneeded pipe ends */
close(rfd[1]);
}
}
- state->rfd = rfd[0];
- fd_nonblock(state->rfd);
-
- state->wfd = wfd[1];
- fd_nonblock(state->wfd);
-
cl->cb = uh_cgi_socket_cb;
cl->priv = state;
struct uh_cgi_state {
- int rfd;
- int wfd;
- struct client *cl;
char httpbuf[UH_LIMIT_MSGHEAD];
int content_length;
bool header_sent;
static void uh_lua_shutdown(struct uh_lua_state *state)
{
- close(state->rfd);
- close(state->wfd);
free(state);
}
while (state->content_length > 0)
{
/* remaining data in http head buffer ... */
- if (state->cl->httpbuf.len > 0)
+ if (cl->httpbuf.len > 0)
{
- len = min(state->content_length, state->cl->httpbuf.len);
+ len = min(state->content_length, cl->httpbuf.len);
- D("Lua: Child(%d) feed %d HTTP buffer bytes\n",
- state->cl->proc.pid, len);
+ D("Lua: Child(%d) feed %d HTTP buffer bytes\n", cl->proc.pid, len);
- memcpy(buf, state->cl->httpbuf.ptr, len);
+ memcpy(buf, cl->httpbuf.ptr, len);
- state->cl->httpbuf.len -= len;
- state->cl->httpbuf.ptr += len;
+ cl->httpbuf.len -= len;
+ cl->httpbuf.ptr += len;
}
/* read it from socket ... */
else
{
- len = uh_tcp_recv(state->cl, buf,
- min(state->content_length, sizeof(buf)));
+ len = uh_tcp_recv(cl, buf, min(state->content_length, sizeof(buf)));
if ((len < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
break;
D("Lua: Child(%d) feed %d/%d TCP socket bytes\n",
- state->cl->proc.pid, len,
- min(state->content_length, sizeof(buf)));
+ cl->proc.pid, len, min(state->content_length, sizeof(buf)));
}
if (len)
state->content_length = 0;
/* ... write to Lua process */
- len = uh_raw_send(state->wfd, buf, len,
+ len = uh_raw_send(cl->wpipe.fd, buf, len,
cl->server->conf->script_timeout);
/* explicit EOF notification for the child */
if (state->content_length <= 0)
- close(state->wfd);
+ uh_ufd_remove(&cl->wpipe);
}
/* try to read data from child */
- while ((len = uh_raw_recv(state->rfd, buf, sizeof(buf), -1)) > 0)
+ while ((len = uh_raw_recv(cl->rpipe.fd, buf, sizeof(buf), -1)) > 0)
{
/* pass through buffer to socket */
- D("Lua: Child(%d) relaying %d normal bytes\n", state->cl->proc.pid, len);
- ensure_out(uh_tcp_send(state->cl, buf, len));
+ D("Lua: Child(%d) relaying %d normal bytes\n", cl->proc.pid, len);
+ ensure_out(uh_tcp_send(cl, buf, len));
state->data_sent = true;
}
((errno != EAGAIN) && (errno != EWOULDBLOCK) && (len == -1)))
{
D("Lua: Child(%d) presumed dead [%s]\n",
- state->cl->proc.pid, strerror(errno));
+ cl->proc.pid, strerror(errno));
goto out;
}
out:
if (!state->data_sent)
{
- if (state->cl->timeout.pending)
- uh_http_sendhf(state->cl, 502, "Bad Gateway",
+ if (cl->timeout.pending)
+ uh_http_sendhf(cl, 502, "Bad Gateway",
"The Lua process did not produce any response\n");
else
- uh_http_sendhf(state->cl, 504, "Gateway Timeout",
+ uh_http_sendhf(cl, 504, "Gateway Timeout",
"The Lua process took too long to produce a "
"response\n");
}
default:
memset(state, 0, sizeof(*state));
- state->cl = cl;
- state->cl->pipe.fd = rfd[0];
- state->cl->proc.pid = child;
+ cl->rpipe.fd = rfd[0];
+ cl->wpipe.fd = wfd[1];
+ cl->proc.pid = child;
+
+ /* make pipe non-blocking */
+ fd_nonblock(cl->rpipe.fd);
+ fd_nonblock(cl->wpipe.fd);
/* close unneeded pipe ends */
close(rfd[1]);
}
}
- state->rfd = rfd[0];
- fd_nonblock(state->rfd);
-
- state->wfd = wfd[1];
- fd_nonblock(state->wfd);
-
cl->cb = uh_lua_socket_cb;
cl->priv = state;
struct uh_lua_state {
- int rfd;
- int wfd;
- struct client *cl;
char httpbuf[UH_LIMIT_MSGHEAD];
int content_length;
bool data_sent;
while (((rv = select(fd+1, write ? NULL : &fds, write ? &fds : NULL,
NULL, &timeout)) < 0) && (errno == EINTR))
{
- D("IO: Socket(%d) select interrupted: %s\n",
+ D("IO: FD(%d) select interrupted: %s\n",
fd, strerror(errno));
continue;
if (rv <= 0)
{
- D("IO: Socket(%d) appears dead (rv=%d)\n", fd, rv);
+ D("IO: FD(%d) appears dead (rv=%d)\n", fd, rv);
return false;
}
{
if (errno == EINTR)
{
- D("IO: Socket(%d) interrupted\n", cl->fd.fd);
+ D("IO: FD(%d) interrupted\n", cl->fd.fd);
continue;
}
else if ((sec > 0) && (errno == EAGAIN || errno == EWOULDBLOCK))
}
else
{
- D("IO: Socket(%d) write error: %s\n", fd, strerror(errno));
+ D("IO: FD(%d) write error: %s\n", fd, strerror(errno));
return -1;
}
}
*/
else if (rv == 0)
{
- D("IO: Socket(%d) closed\n", fd);
+ D("IO: FD(%d) appears closed\n", fd);
return 0;
}
else if (rv < len)
{
- D("IO: Socket(%d) short write %d/%d bytes\n", fd, rv, len);
+ D("IO: FD(%d) short write %d/%d bytes\n", fd, rv, len);
len -= rv;
buf += rv;
continue;
}
else
{
- D("IO: Socket(%d) sent %d/%d bytes\n", fd, rv, len);
+ D("IO: FD(%d) sent %d/%d bytes\n", fd, rv, len);
return rv;
}
}
}
else
{
- D("IO: Socket(%d) read error: %s\n", fd, strerror(errno));
+ D("IO: FD(%d) read error: %s\n", fd, strerror(errno));
return -1;
}
}
else if (rv == 0)
{
- D("IO: Socket(%d) closed\n", fd);
+ D("IO: FD(%d) appears closed\n", fd);
return 0;
}
else
{
- D("IO: Socket(%d) read %d bytes\n", fd, rv);
+ D("IO: FD(%d) read %d bytes\n", fd, rv);
return rv;
}
}
new->fd.fd = sock;
new->server = serv;
+ new->rpipe.fd = -1;
+ new->wpipe.fd = -1;
+
/* get remote endpoint addr */
sl = sizeof(struct sockaddr_in6);
memset(&(new->peeraddr), 0, sl);
uh_clients = new;
serv->n_clients++;
+
+ D("IO: Client(%d) allocated\n", new->fd.fd);
}
return new;
if (cur->proc.pid)
uloop_process_delete(&cur->proc);
- if (cur->pipe.fd)
- uloop_fd_delete(&cur->pipe);
+ D("IO: Client(%d) freeing\n", cur->fd.fd);
- uloop_fd_delete(&cur->fd);
- close(cur->fd.fd);
+ uh_ufd_remove(&cur->rpipe);
+ uh_ufd_remove(&cur->wpipe);
+ uh_ufd_remove(&cur->fd);
- D("IO: Socket(%d) closing\n", cur->fd.fd);
cur->server->n_clients--;
free(cur);
}
+void uh_ufd_add(struct uloop_fd *u, uloop_fd_handler h, unsigned int ev)
+{
+ if (h != NULL)
+ {
+ u->cb = h;
+ uloop_fd_add(u, ev);
+ D("IO: FD(%d) added to uloop\n", u->fd);
+ }
+}
+
+void uh_ufd_remove(struct uloop_fd *u)
+{
+ if (u->cb != NULL)
+ {
+ uloop_fd_delete(u);
+ D("IO: FD(%d) removed from uloop\n", u->fd);
+ u->cb = NULL;
+ }
+
+ if (u->fd > -1)
+ {
+ close(u->fd);
+ D("IO: FD(%d) closed\n", u->fd);
+ u->fd = -1;
+ }
+}
+
+
#ifdef HAVE_CGI
static struct interpreter *uh_interpreters = NULL;
#include <pwd.h>
#include <sys/stat.h>
+#include <libubox/uloop.h>
+
+
#ifdef HAVE_SHADOW
#include <shadow.h>
#endif
void uh_client_shutdown(struct client *cl);
void uh_client_remove(struct client *cl);
-#define uh_client_gc() uh_client_remove(NULL)
+void uh_ufd_add(struct uloop_fd *u, uloop_fd_handler h, unsigned int ev);
+void uh_ufd_remove(struct uloop_fd *u);
#ifdef HAVE_CGI
fd_cloexec(sock);
*max_fd = max(*max_fd, sock);
- l->fd.cb = uh_listener_cb;
- uloop_fd_add(&l->fd, ULOOP_READ);
+ uh_ufd_add(&l->fd, uh_listener_cb, ULOOP_READ);
bound++;
continue;
return false;
}
-static void uh_client_cb(struct uloop_fd *u, unsigned int events);
+static void uh_socket_cb(struct uloop_fd *u, unsigned int events);
static void uh_listener_cb(struct uloop_fd *u, unsigned int events)
{
if ((cl = uh_client_add(new_fd, serv)) != NULL)
{
/* add client socket to global fdset */
- uloop_fd_add(&cl->fd, ULOOP_READ);
+ uh_ufd_add(&cl->fd, uh_socket_cb, ULOOP_READ);
+ fd_cloexec(cl->fd.fd);
#ifdef HAVE_TLS
/* setup client tls context */
}
}
#endif
-
- cl->fd.cb = uh_client_cb;
- fd_cloexec(new_fd);
}
/* insufficient resources */
}
}
-static void uh_pipe_cb(struct uloop_fd *u, unsigned int events)
+static void uh_client_cb(struct client *cl, unsigned int events);
+
+static void uh_rpipe_cb(struct uloop_fd *u, unsigned int events)
{
- struct client *cl = container_of(u, struct client, pipe);
+ struct client *cl = container_of(u, struct client, rpipe);
- if (!u->error)
- {
- D("SRV: Client(%d) pipe(%d) readable\n",
- cl->fd.fd, cl->pipe.fd);
+ D("SRV: Client(%d) rpipe readable\n", cl->fd.fd);
- uh_client_cb(&cl->fd, ULOOP_WRITE);
- }
+ uh_client_cb(cl, ULOOP_WRITE);
+}
+
+static void uh_socket_cb(struct uloop_fd *u, unsigned int events)
+{
+ struct client *cl = container_of(u, struct client, fd);
+
+ D("SRV: Client(%d) socket readable\n", cl->fd.fd);
+
+ uh_client_cb(cl, ULOOP_READ);
}
static void uh_child_cb(struct uloop_process *p, int rv)
{
struct client *cl = container_of(p, struct client, proc);
- D("SRV: Client(%d) child(%d) is dead\n", cl->fd.fd, cl->proc.pid);
+ D("SRV: Client(%d) child(%d) dead\n", cl->fd.fd, cl->proc.pid);
- cl->dead = true;
- cl->fd.eof = true;
- uh_client_cb(&cl->fd, ULOOP_READ | ULOOP_WRITE);
+ uh_client_cb(cl, ULOOP_READ | ULOOP_WRITE);
}
static void uh_kill9_cb(struct uloop_timeout *t)
}
}
-static void uh_client_cb(struct uloop_fd *u, unsigned int events)
+static void uh_client_cb(struct client *cl, unsigned int events)
{
int i;
- struct client *cl;
struct config *conf;
struct http_request *req;
- cl = container_of(u, struct client, fd);
conf = cl->server->conf;
- D("SRV: Client(%d) enter callback\n", u->fd);
+ D("SRV: Client(%d) enter callback\n", cl->fd.fd);
/* undispatched yet */
if (!cl->dispatched)
/* we have no headers yet and this was a write event, ignore... */
if (!(events & ULOOP_READ))
{
- D("SRV: Client(%d) ignoring write event before headers\n", u->fd);
+ D("SRV: Client(%d) ignoring write event before headers\n", cl->fd.fd);
return;
}
/* attempt to receive and parse headers */
if (!(req = uh_http_header_recv(cl)))
{
- D("SRV: Client(%d) failed to receive header\n", u->fd);
+ D("SRV: Client(%d) failed to receive header\n", cl->fd.fd);
uh_client_shutdown(cl);
return;
}
if (strcasecmp(req->headers[i+1], "100-continue"))
{
D("SRV: Client(%d) unknown expect header (%s)\n",
- u->fd, req->headers[i+1]);
+ cl->fd.fd, req->headers[i+1]);
uh_http_response(cl, 417, "Precondition Failed");
uh_client_shutdown(cl);
}
else
{
- D("SRV: Client(%d) sending HTTP/1.1 100 Continue\n", u->fd);
+ D("SRV: Client(%d) sending HTTP/1.1 100 Continue\n", cl->fd.fd);
uh_http_sendf(cl, NULL, "HTTP/1.1 100 Continue\r\n\r\n");
cl->httpbuf.len = 0; /* client will re-send the body */
/* dispatch request */
if (!uh_dispatch_request(cl, req))
{
- D("SRV: Client(%d) failed to dispach request\n", u->fd);
+ D("SRV: Client(%d) failed to dispach request\n", cl->fd.fd);
uh_client_shutdown(cl);
return;
}
/* request handler spawned a pipe, register handler */
- if (cl->pipe.fd)
+ if (cl->rpipe.fd > -1)
{
- D("SRV: Client(%d) pipe(%d) spawned\n", u->fd, cl->pipe.fd);
+ D("SRV: Client(%d) pipe(%d) spawned\n", cl->fd.fd, cl->rpipe.fd);
- cl->pipe.cb = uh_pipe_cb;
- uloop_fd_add(&cl->pipe, ULOOP_READ);
+ uh_ufd_add(&cl->rpipe, uh_rpipe_cb, ULOOP_READ);
}
/* request handler spawned a child, register handler */
if (cl->proc.pid)
{
- D("SRV: Client(%d) child(%d) spawned\n", u->fd, cl->proc.pid);
+ D("SRV: Client(%d) child(%d) spawned\n", cl->fd.fd, cl->proc.pid);
cl->proc.cb = uh_child_cb;
uloop_process_add(&cl->proc);
}
/* header processing complete */
- D("SRV: Client(%d) dispatched\n", u->fd);
+ D("SRV: Client(%d) dispatched\n", cl->fd.fd);
cl->dispatched = true;
}
if (!cl->cb(cl))
{
- D("SRV: Client(%d) response callback signalized EOF\n", u->fd);
+ D("SRV: Client(%d) response callback signalized EOF\n", cl->fd.fd);
uh_client_shutdown(cl);
return;
}
" -L file Lua handler script, omit to disable Lua\n"
#endif
#ifdef HAVE_UBUS
- " -u string URL prefix for HTTP/JSON handler, default is '/ubus'\n"
+ " -u string URL prefix for HTTP/JSON handler\n"
" -U file Override ubus socket path\n"
#endif
#ifdef HAVE_CGI
"Notice: Unable to load ubus plugin - disabling ubus support! "
"(Reason: %s)\n", dlerror());
}
- else
+ else if (conf.ubus_prefix)
{
/* resolve functions */
if (!(conf.ubus_init = dlsym(lib, "uh_ubus_init")) ||
exit(1);
}
- /* default ubus prefix */
- if (!conf.ubus_prefix)
- conf.ubus_prefix = "/ubus";
-
+ /* initialize ubus */
conf.ubus_state = conf.ubus_init(&conf);
}
#endif
SSL *tls;
#endif
struct uloop_fd fd;
- struct uloop_fd pipe;
+ struct uloop_fd rpipe;
+ struct uloop_fd wpipe;
struct uloop_process proc;
struct uloop_timeout timeout;
bool (*cb)(struct client *);