commit - d48ffc6605b4a30e0e4356cf2d53383c2a223067
commit + 3634fa709eb3bd3cacb762306ef62ff9698441b3
blob - 7f7e25a75e6ba2fbb4307a284ca9e1a15d229cbf
blob + 72f199e5cae6f54b4d7d57830d9d28c41671e755
--- web/Makefile
+++ web/Makefile
PROG = amused-web
-SOURCES = web.c http.c ../ev.c ../log.c ../playlist.c ../xmalloc.c
+SOURCES = web.c bufio.c http.c ../ev.c ../log.c ../playlist.c ../xmalloc.c
OBJS = ${SOURCES:.c=.o}
blob - 8b1d5a2d69d12e84ecfd77f74f402475834a9147
blob + c95d30f7bb7de7c19f3cf49cef7f94c5d900f638
--- web/http.c
+++ web/http.c
#include <string.h>
#include <unistd.h>
+#include "bufio.h"
#include "http.h"
#include "log.h"
#include "xmalloc.h"
#define nitems(x) (sizeof(x)/sizeof(x[0]))
#endif
-static int
-writeall(struct reswriter *res, const char *buf, size_t buflen)
+int
+http_init(struct client *clt, int fd)
{
- ssize_t nw;
- size_t off;
-
- for (off = 0; off < buflen; off += nw)
- if ((nw = write(res->fd, buf + off, buflen - off)) == 0 ||
- nw == -1) {
- if (nw == 0)
- log_warnx("Unexpected EOF");
- else
- log_warn("write");
- res->err = 1;
- return -1;
- }
-
+ memset(clt, 0, sizeof(*clt));
+ if (bufio_init(&clt->bio) == -1)
+ return -1;
+ bufio_set_fd(&clt->bio, fd);
return 0;
}
int
-http_parse(struct request *req, int fd)
+http_parse(struct client *clt)
{
- ssize_t nr;
- size_t avail, len;
- int done = 0, first = 1;
- char *s, *t, *line, *endln;
+ struct buffer *rbuf = &clt->bio.rbuf;
+ struct request *req = &clt->req;
+ size_t len;
+ uint8_t *endln;
+ char *s, *t, *line;
const char *errstr, *m;
- memset(req, 0, sizeof(*req));
-
- while (!done) {
- if (req->len == sizeof(req->buf)) {
- log_warnx("not enough space");
+ while (!clt->reqdone) {
+ endln = memmem(rbuf->buf, rbuf->len, "\r\n", 2);
+ if (endln == NULL) {
+ errno = EAGAIN;
return -1;
}
- avail = sizeof(req->buf) - req->len;
- nr = read(fd, req->buf + req->len, avail);
- if (nr <= 0) {
- if (errno == EAGAIN || errno == EINTR)
- continue;
- if (nr == 0)
- log_warnx("Unexpected EOF");
- else
- log_warn("read");
- return -1;
- }
- req->len += nr;
+ line = rbuf->buf;
+ if (endln == rbuf->buf)
+ clt->reqdone = 1;
- while ((endln = memmem(req->buf, req->len, "\r\n", 2))) {
- line = req->buf;
- if (endln == req->buf)
- done = 1;
+ len = endln - rbuf->buf + 2;
+ while (len > 0 && (line[len - 1] == '\r' ||
+ line[len - 1] == '\n' || line[len - 1] == ' ' ||
+ line[len - 1] == '\t'))
+ line[--len] = '\0';
- len = endln - req->buf + 2;
- while (len > 0 && (line[len - 1] == '\r' ||
- line[len - 1] == '\n' || line[len - 1] == ' ' ||
- line[len - 1] == '\t'))
- line[--len] = '\0';
-
- if (first) {
- first = 0;
- if (!strncmp("GET ", line, 4)) {
- req->method = METHOD_GET;
- s = line + 4;
- } else if (!strncmp("POST ", line, 5)) {
- req->method = METHOD_POST;
- s = line + 5;
- }
-
- t = strchr(s, ' ');
- if (t == NULL)
- t = s;
- if (*t != '\0')
- *t++ = '\0';
- req->path = xstrdup(s);
- if (!strcmp("HTTP/1.0", t))
- req->version = HTTP_1_0;
- else if (!strcmp("HTTP/1.1", t))
- req->version = HTTP_1_1;
- else {
- log_warnx("unknown http version: %s",
- t);
- return -1;
- }
+ /* first line */
+ if (clt->req.method == METHOD_UNKNOWN) {
+ if (!strncmp("GET ", line, 4)) {
+ req->method = METHOD_GET;
+ s = line + 4;
+ } else if (!strncmp("POST ", line, 5)) {
+ req->method = METHOD_POST;
+ s = line + 5;
+ } else {
+ errno = EINVAL;
+ return -1;
}
- if (!strncasecmp(line, "Content-Length:", 15)) {
- line += 15;
- line += strspn(line, " \t");
- req->clen = strtonum(line, 0, LONG_MAX,
- &errstr);
- if (errstr != NULL) {
- log_warnx("content-length is %s: %s",
- errstr, line);
- return -1;
- }
+ t = strchr(s, ' ');
+ if (t == NULL)
+ t = s;
+ if (*t != '\0')
+ *t++ = '\0';
+ clt->req.path = xstrdup(s);
+ if (!strcmp(t, "HTTP/1.0"))
+ clt->req.version = HTTP_1_0;
+ else if (!strcmp(t, "HTTP/1.1")) {
+ clt->req.version = HTTP_1_1;
+ clt->chunked = 1;
+ } else {
+ log_warnx("unknown http version %s", t);
+ errno = EINVAL;
+ return -1;
}
+ }
- len = endln - req->buf + 2;
- memmove(req->buf, req->buf + len, req->len - len);
- req->len -= len;
- if (done)
- break;
+ if (!strncasecmp(line, "Content-Length:", 15)) {
+ line += 15;
+ line += strspn(line, " \t");
+ clt->req.clen = strtonum(line, 0, LONG_MAX,
+ &errstr);
+ if (errstr) {
+ log_warnx("content-length is %s: %s",
+ errstr, line);
+ errno = EINVAL;
+ return -1;
+ }
}
+
+ buf_drain(rbuf, endln - rbuf->buf + 2);
}
if (req->method == METHOD_GET)
}
int
-http_read(struct request *req, int fd)
+http_read(struct client *clt)
{
+ struct request *req = &clt->req;
size_t left;
- ssize_t nr;
+ size_t nr;
- if (req->clen > sizeof(req->buf) - 1) {
+ if (req->clen > sizeof(clt->buf) - 1) {
log_warnx("POST has more data then what can be accepted");
return -1;
}
/* clients may have sent more data than advertised */
- if (req->clen < req->len)
+ if (req->clen < clt->len)
left = 0;
else
- left = req->clen - req->len;
+ left = req->clen - clt->len;
- while (left > 0) {
- nr = read(fd, req->buf + req->len, left);
- if (nr <= 0) {
- if (nr == -1 && errno == EAGAIN)
- continue;
- if (nr == 0)
- log_warnx("Unexpected EOF");
- else
- log_warn("read");
+ if (left > 0) {
+ nr = bufio_drain(&clt->bio, clt->buf + clt->len, left);
+ clt->len += nr;
+ if (nr < left) {
+ errno = EAGAIN;
return -1;
}
- req->len += nr;
- left -= nr;
}
- req->buf[req->len] = '\0';
- while (req->len > 0 && (req->buf[req->len - 1] == '\r' ||
- (req->buf[req->len - 1] == '\n')))
- req->buf[--req->len] = '\0';
+ clt->buf[clt->len] = '\0';
+ while (clt->len > 0 && (clt->buf[clt->len - 1] == '\r' ||
+ (clt->buf[clt->len - 1] == '\n')))
+ clt->buf[--clt->len] = '\0';
+
return 0;
}
-void
-http_response_init(struct reswriter *res, struct request *req, int fd)
-{
- memset(res, 0, sizeof(*res));
- res->fd = fd;
- res->chunked = req->version == HTTP_1_1;
-}
-
int
-http_reply(struct reswriter *res, int code, const char *reason,
- const char *ctype)
+http_reply(struct client *clt, int code, const char *reason, const char *ctype)
{
const char *location = NULL;
int r;
- res->len = 0; /* discard any leftover from reading */
-
log_debug("> %d %s", code, reason);
if (code >= 300 && code < 400) {
ctype = "text/html;charset=UTF-8";
}
- r = snprintf(res->buf, sizeof(res->buf), "HTTP/1.1 %d %s\r\n"
+ r = bufio_compose_fmt(&clt->bio, "HTTP/1.1 %d %s\r\n"
"Connection: close\r\n"
"Cache-Control: no-store\r\n"
"%s%s%s"
location == NULL ? "" : "Location: ",
location == NULL ? "" : location,
location == NULL ? "" : "\r\n",
- res->chunked ? "Transfer-Encoding: chunked\r\n" : "");
- if (r < 0 || (size_t)r >= sizeof(res->buf))
+ clt->chunked ? "Transfer-Encoding: chunked\r\n" : "");
+ if (r == -1) {
+ clt->err = 1;
return -1;
+ }
- if (writeall(res, res->buf, r) == -1)
- return -1;
-
if (location) {
- if (http_writes(res, "<a href='") == -1 ||
- http_htmlescape(res, location) == -1 ||
- http_writes(res, "'>") == -1 ||
- http_htmlescape(res, reason) == -1 ||
- http_writes(res, "</a>") == -1)
+ if (http_writes(clt, "<a href='") == -1 ||
+ http_htmlescape(clt, location) == -1 ||
+ http_writes(clt, "'>") == -1 ||
+ http_htmlescape(clt, reason) == -1 ||
+ http_writes(clt, "</a>") == -1)
return -1;
}
+ bufio_set_chunked(&clt->bio, clt->chunked);
return 0;
}
int
-http_flush(struct reswriter *res)
+http_flush(struct client *clt)
{
- struct iovec iov[3];
- char buf[64];
- ssize_t nw;
- size_t i, tot;
- int r;
-
- if (res->err)
+ if (clt->err)
return -1;
- if (res->len == 0)
+ if (clt->len == 0)
return 0;
- if (!res->chunked) {
- if (writeall(res, res->buf, res->len) == -1)
- return -1;
- res->len = 0;
- return 0;
- }
-
- r = snprintf(buf, sizeof(buf), "%zx\r\n", res->len);
- if (r < 0 || (size_t)r >= sizeof(buf)) {
- log_warn("snprintf failed");
- res->err = 1;
+ if (bufio_compose(&clt->bio, clt->buf, clt->len) == -1) {
+ clt->err = 1;
return -1;
}
- memset(iov, 0, sizeof(iov));
+ clt->len = 0;
- iov[0].iov_base = buf;
- iov[0].iov_len = r;
-
- iov[1].iov_base = res->buf;
- iov[1].iov_len = res->len;
-
- iov[2].iov_base = "\r\n";
- iov[2].iov_len = 2;
-
- tot = iov[0].iov_len + iov[1].iov_len + iov[2].iov_len;
- while (tot > 0) {
- nw = writev(res->fd, iov, nitems(iov));
- if (nw <= 0) {
- if (nw == -1 && errno == EAGAIN)
- continue;
- if (nw == 0)
- log_warnx("Unexpected EOF");
- else
- log_warn("writev");
- res->err = 1;
- return -1;
- }
-
- tot -= nw;
- for (i = 0; i < nitems(iov); ++i) {
- if (nw < iov[i].iov_len) {
- iov[i].iov_base += nw;
- iov[i].iov_len -= nw;
- break;
- }
- nw -= iov[i].iov_len;
- iov[i].iov_len = 0;
- }
- }
-
- res->len = 0;
return 0;
}
int
-http_write(struct reswriter *res, const char *d, size_t len)
+http_write(struct client *clt, const char *d, size_t len)
{
size_t avail;
- if (res->err)
+ if (clt->err)
return -1;
while (len > 0) {
- avail = sizeof(res->buf) - res->len;
+ avail = sizeof(clt->buf) - clt->len;
if (avail > len)
avail = len;
- memcpy(res->buf + res->len, d, avail);
- res->len += avail;
+ memcpy(clt->buf + clt->len, d, avail);
+ clt->len += avail;
len -= avail;
d += avail;
- if (res->len == sizeof(res->buf)) {
- if (http_flush(res) == -1)
+ if (clt->len == sizeof(clt->buf)) {
+ if (http_flush(clt) == -1)
return -1;
}
}
}
int
-http_writes(struct reswriter *res, const char *str)
+http_writes(struct client *clt, const char *str)
{
- return http_write(res, str, strlen(str));
+ return http_write(clt, str, strlen(str));
}
int
-http_fmt(struct reswriter *res, const char *fmt, ...)
+http_fmt(struct client *clt, const char *fmt, ...)
{
va_list ap;
char *str;
if (r == -1) {
log_warn("vasprintf");
- res->err = 1;
+ clt->err = 1;
return -1;
}
- r = http_write(res, str, r);
+ r = http_write(clt, str, r);
free(str);
return r;
}
int
-http_urlescape(struct reswriter *res, const char *str)
+http_urlescape(struct client *clt, const char *str)
{
int r;
char tmp[4];
(unsigned char)*str);
if (r < 0 || (size_t)r >= sizeof(tmp)) {
log_warn("snprintf failed");
- res->err = 1;
+ clt->err = 1;
return -1;
}
- if (http_write(res, tmp, r) == -1)
+ if (http_write(clt, tmp, r) == -1)
return -1;
- } else if (http_write(res, str, 1) == -1)
+ } else if (http_write(clt, str, 1) == -1)
return -1;
}
}
int
-http_htmlescape(struct reswriter *res, const char *str)
+http_htmlescape(struct client *clt, const char *str)
{
int r;
for (; *str; ++str) {
switch (*str) {
case '<':
- r = http_writes(res, "<");
+ r = http_writes(clt, "<");
break;
case '>':
- r = http_writes(res, ">");
+ r = http_writes(clt, ">");
break;
case '&':
- r = http_writes(res, ">");
+ r = http_writes(clt, ">");
break;
case '"':
- r = http_writes(res, """);
+ r = http_writes(clt, """);
break;
case '\'':
- r = http_writes(res, "'");
+ r = http_writes(clt, "'");
break;
default:
- r = http_write(res, str, 1);
+ r = http_write(clt, str, 1);
break;
}
}
int
-http_close(struct reswriter *res)
+http_close(struct client *clt)
{
- if (!res->chunked)
- return 0;
-
- return writeall(res, "0\r\n\r\n", 5);
+ if (clt->err)
+ return -1;
+ if (clt->len != 0 && http_flush(clt) == -1)
+ return -1;
+ if (bufio_compose(&clt->bio, NULL, 0) == -1)
+ clt->err = 1;
+ return (clt->err ? -1 : 0);
}
void
-http_free_request(struct request *req)
+http_free(struct client *clt)
{
- free(req->path);
- free(req->ctype);
+ free(clt->req.path);
+ free(clt->req.ctype);
+ free(clt->req.body);
+ bufio_free(&clt->bio);
}
blob - 51cbdb77bac5f32482d1db3184660b2a6e2cac12
blob + 78bdce3d680a99c406486891feb0985b42453f0c
--- web/http.h
+++ web/http.h
HTTP_1_1,
};
-struct request {
- char buf[BUFSIZ];
- size_t len;
+struct bufio;
+struct request {
char *path;
int method;
int version;
char *ctype;
+ char *body;
size_t clen;
};
-struct reswriter {
- int fd;
- int err;
- int chunked;
- char buf[BUFSIZ];
- size_t len;
+struct client;
+typedef void (*route_fn)(struct client *);
+
+struct client {
+ char buf[1024];
+ size_t len;
+ struct bufio bio;
+ struct request req;
+ int err;
+ int chunked;
+ int reqdone; /* done parsing the request */
+ int done; /* done handling the client */
+ route_fn route;
};
-int http_parse(struct request *, int);
-int http_read(struct request *, int);
-void http_response_init(struct reswriter *, struct request *, int);
-int http_reply(struct reswriter *, int, const char *, const char *);
-int http_flush(struct reswriter *);
-int http_write(struct reswriter *, const char *, size_t);
-int http_writes(struct reswriter *, const char *);
-int http_fmt(struct reswriter *, const char *, ...);
-int http_urlescape(struct reswriter *, const char *);
-int http_htmlescape(struct reswriter *, const char *);
-int http_close(struct reswriter *);
-void http_free_request(struct request *);
+int http_init(struct client *, int);
+int http_parse(struct client *);
+int http_read(struct client *);
+int http_reply(struct client *, int, const char *, const char *);
+int http_flush(struct client *);
+int http_write(struct client *, const char *, size_t);
+int http_writes(struct client *, const char *);
+int http_fmt(struct client *, const char *, ...);
+int http_urlescape(struct client *, const char *);
+int http_htmlescape(struct client *, const char *);
+int http_close(struct client *);
+void http_free(struct client *);
blob - 9b42ad5d1de05d252f9d2cac33ae314593e68159
blob + 824037445c78d40c98b663473a375c24371fb296
--- web/web.c
+++ web/web.c
#include <unistd.h>
#include "amused.h"
+#include "bufio.h"
#include "ev.h"
#include "http.h"
#include "log.h"
}
static void
-route_notfound(struct reswriter *res, struct request *req)
+route_notfound(struct client *clt)
{
- if (http_reply(res, 404, "Not Found", "text/plain") == -1 ||
- http_writes(res, "Page not found\n") == -1)
+ if (http_reply(clt, 404, "Not Found", "text/plain") == -1 ||
+ http_writes(clt, "Page not found\n") == -1)
return;
}
static void
-render_playlist(struct reswriter *res)
+render_playlist(struct client *clt)
{
struct imsg imsg;
struct player_status ps;
imsg_compose(&ibuf, IMSG_CTL_SHOW, 0, 0, -1, NULL, 0);
imsg_flush(&ibuf);
- http_writes(res, "<section class='playlist-wrapper'>");
- http_writes(res, "<form action=jump method=post"
+ http_writes(clt, "<section class='playlist-wrapper'>");
+ http_writes(clt, "<form action=jump method=post"
" enctype='"FORM_URLENCODED"'>");
- http_writes(res, "<ul class=playlist>");
+ http_writes(clt, "<ul class=playlist>");
done = 0;
while (!done) {
if (!strncmp(p, prefix, prefixlen))
p += prefixlen;
- http_fmt(res, "<li%s>",
+ http_fmt(clt, "<li%s>",
current ? " id=current" : "");
- http_writes(res,
+ http_writes(clt,
"<button type=submit name=jump value=\"");
- http_htmlescape(res, ps.path);
- http_writes(res, "\">");
- http_htmlescape(res, p);
- http_writes(res, "</button></li>");
+ http_htmlescape(clt, ps.path);
+ http_writes(clt, "\">");
+ http_htmlescape(clt, p);
+ http_writes(clt, "</button></li>");
imsg_free(&imsg);
}
}
- http_writes(res, "</ul>");
- http_writes(res, "</form>");
- http_writes(res, "</section>");
+ http_writes(clt, "</ul>");
+ http_writes(clt, "</form>");
+ http_writes(clt, "</section>");
}
static void
-render_controls(struct reswriter *res)
+render_controls(struct client *clt)
{
struct imsg imsg;
struct player_status ps;
else
p = ps.path;
- if (http_writes(res, "<section class=controls>") == -1 ||
- http_writes(res, "<p><a href='#current'>") == -1 ||
- http_htmlescape(res, p) == -1 ||
- http_writes(res, "</a></p>") == -1 ||
- http_writes(res, "<form action=ctrls method=post"
+ if (http_writes(clt, "<section class=controls>") == -1 ||
+ http_writes(clt, "<p><a href='#current'>") == -1 ||
+ http_htmlescape(clt, p) == -1 ||
+ http_writes(clt, "</a></p>") == -1 ||
+ http_writes(clt, "<form action=ctrls method=post"
" enctype='"FORM_URLENCODED"'>") == -1 ||
- http_writes(res, "<button type=submit name=ctl value=prev>"
+ http_writes(clt, "<button type=submit name=ctl value=prev>"
ICON_PREV"</button>") == -1 ||
- http_fmt(res, "<button type=submit name=ctl value=%s>"
+ http_fmt(clt, "<button type=submit name=ctl value=%s>"
"%s</button>", playing ? "pause" : "play",
playing ? ICON_PAUSE : ICON_PLAY) == -1 ||
- http_writes(res, "<button type=submit name=ctl value=next>"
+ http_writes(clt, "<button type=submit name=ctl value=next>"
ICON_NEXT"</button>") == -1 ||
- http_writes(res, "</form>") == -1 ||
- http_writes(res, "<form action=mode method=post"
- " enctype='"FORM_URLENCODED"'>") == -1 ||
- http_fmt(res, "<button%s type=submit name=mode value=all>"
+ http_writes(clt, "</form>") == -1 ||
+ http_writes(clt, "<form action=mode method=post"
+ " enctype='"FORM_URLENCODED"'>") == -1 ||
+ http_fmt(clt, "<button%s type=submit name=mode value=all>"
ICON_REPEAT_ALL"</button>", ac) == -1 ||
- http_fmt(res, "<button%s type=submit name=mode value=one>"
+ http_fmt(clt, "<button%s type=submit name=mode value=one>"
ICON_REPEAT_ONE"</button>", oc) == -1 ||
- http_writes(res, "</form>") == -1 ||
- http_writes(res, "</section>") == -1)
+ http_writes(clt, "</form>") == -1 ||
+ http_writes(clt, "</section>") == -1)
return;
done:
}
static void
-route_home(struct reswriter *res, struct request *req)
+route_home(struct client *clt)
{
- if (http_reply(res, 200, "OK", "text/html;charset=UTF-8") == -1)
+ if (http_reply(clt, 200, "OK", "text/html;charset=UTF-8") == -1)
return;
- if (http_write(res, head, strlen(head)) == -1)
+ if (http_write(clt, head, strlen(head)) == -1)
return;
- if (http_writes(res, "<main>") == -1)
+ if (http_writes(clt, "<main>") == -1)
return;
- if (http_writes(res, "<section class=searchbox>"
+ if (http_writes(clt, "<section class=searchbox>"
"<input type=search name=filter aria-label='Filter playlist'"
" placeholder='Filter playlist' id=search />"
"</section>") == -1)
return;
- render_controls(res);
- render_playlist(res);
+ render_controls(clt);
+ render_playlist(clt);
- if (http_writes(res, "</main>") == -1)
+ if (http_writes(clt, "</main>") == -1)
return;
- http_write(res, foot, strlen(foot));
+ http_write(clt, foot, strlen(foot));
}
static void
-route_jump(struct reswriter *res, struct request *req)
+route_jump(struct client *clt)
{
struct imsg imsg;
struct player_status ps;
char *form, *field;
int found = 0;
- if (http_read(req, res->fd) == -1)
- return;
-
- form = req->buf;
+ form = clt->buf;
while ((field = strsep(&form, "&")) != NULL) {
if (url_decode(field) == -1)
goto badreq;
if (!found)
goto badreq;
- http_reply(res, 302, "See Other", "/");
+ http_reply(clt, 302, "See Other", "/");
return;
badreq:
- http_reply(res, 400, "Bad Request", "text/plain");
- http_writes(res, "Bad Request.\n");
+ http_reply(clt, 400, "Bad Request", "text/plain");
+ http_writes(clt, "Bad Request.\n");
}
static void
-route_controls(struct reswriter *res, struct request *req)
+route_controls(struct client *clt)
{
char *form, *field;
int cmd, found = 0;
- if (http_read(req, res->fd) == -1)
- return;
-
- form = req->buf;
+ form = clt->buf;
while ((field = strsep(&form, "&")) != NULL) {
if (url_decode(field) == -1)
goto badreq;
if (!found)
goto badreq;
- http_reply(res, 302, "See Other", "/");
+ http_reply(clt, 302, "See Other", "/");
return;
badreq:
- http_reply(res, 400, "Bad Request", "text/plain");
- http_writes(res, "Bad Request.\n");
+ http_reply(clt, 400, "Bad Request", "text/plain");
+ http_writes(clt, "Bad Request.\n");
}
static void
-route_mode(struct reswriter *res, struct request *req)
+route_mode(struct client *clt)
{
char *form, *field;
int found = 0;
pm.repeat_one = pm.repeat_all = pm.consume = MODE_UNDEF;
- if (http_read(req, res->fd) == -1)
- return;
-
- form = req->buf;
+ form = clt->buf;
while ((field = strsep(&form, "&")) != NULL) {
if (url_decode(field) == -1)
goto badreq;
if (!found)
goto badreq;
- http_reply(res, 302, "See Other", "/");
+ http_reply(clt, 302, "See Other", "/");
return;
badreq:
- http_reply(res, 400, "Bad Request", "text/plain");
- http_writes(res, "Bad Request.\n");
+ http_reply(clt, 400, "Bad Request", "text/plain");
+ http_writes(clt, "Bad Request.\n");
}
static void
-route_dispatch(struct reswriter *res, struct request *req)
+route_dispatch(struct client *clt)
{
static const struct route {
- int method;
- const char *path;
- void (*fn)(struct reswriter *, struct request *);
+ int method;
+ const char *path;
+ route_fn route;
} routes[] = {
{ METHOD_GET, "/", &route_home },
{ METHOD_POST, "/jump", &route_jump },
{ METHOD_GET, "*", &route_notfound },
{ METHOD_POST, "*", &route_notfound },
};
+ struct request *req = &clt->req;
size_t i;
if ((req->method != METHOD_GET && req->method != METHOD_POST) ||
(req->ctype != NULL && strcmp(req->ctype, FORM_URLENCODED) != 0) ||
req->path == NULL) {
- http_reply(res, 400, "Bad Request", NULL);
+ http_reply(clt, 400, "Bad Request", NULL);
return;
}
if (req->method != routes[i].method ||
fnmatch(routes[i].path, req->path, 0) != 0)
continue;
- routes[i].fn(res, req);
+ clt->done = 1; /* assume with one round is done */
+ clt->route = routes[i].route;
+ clt->route(clt);
+ if (clt->done)
+ http_close(clt);
return;
}
}
static void
+client_ev(int fd, int ev, void *d)
+{
+ struct client *clt = d;
+
+ if (ev & (POLLIN|POLLHUP)) {
+ if (bufio_read(&clt->bio) == -1 && errno != EAGAIN) {
+ log_warn("bufio_read");
+ goto err;
+ }
+ }
+
+ if (ev & POLLOUT) {
+ if (bufio_write(&clt->bio) == -1 && errno != EAGAIN) {
+ log_warn("bufio_read");
+ goto err;
+ }
+ }
+
+ if (clt->route == NULL) {
+ if (http_parse(clt) == -1) {
+ if (errno == EAGAIN)
+ goto again;
+ log_warnx("HTTP parse request failed");
+ goto err;
+ }
+ if (clt->req.method == METHOD_POST &&
+ http_read(clt) == -1) {
+ if (errno == EAGAIN)
+ goto again;
+ log_warnx("failed to read POST data");
+ goto err;
+ }
+ route_dispatch(clt);
+ goto again;
+ }
+
+ if (!clt->done)
+ clt->route(clt);
+
+ again:
+ ev = bufio_pollev(&clt->bio);
+ if (ev == POLLIN && clt->done) {
+ goto err; /* done with this client */
+ }
+
+ ev_add(fd, ev, client_ev, clt);
+ return;
+
+ err:
+ ev_del(fd);
+ http_free(clt);
+}
+
+static void
web_accept(int psock, int ev, void *d)
{
- struct reswriter res;
- struct request req;
+ struct client *clt;
int sock;
if ((sock = accept(psock, NULL, NULL)) == -1) {
warn("accept");
return;
}
- if (http_parse(&req, sock) == -1) {
+ clt = xcalloc(1, sizeof(*clt));
+ if ((clt = calloc(1, sizeof(*clt))) == NULL ||
+ http_init(clt, sock) == -1) {
+ log_warn("failed to initialize client");
+ free(clt);
close(sock);
return;
}
- http_response_init(&res, &req, sock);
- route_dispatch(&res, &req);
- http_flush(&res);
- http_close(&res);
- http_free_request(&req);
- close(sock);
+
+ client_ev(sock, POLLIN, clt);
return;
}