Commit Diff


commit - 6eaaaab872315df9626e7237d13ed8d5aeb81fc5
commit + 6be8143330044a05a09e0ea4510d28027f79c13a
blob - f6f8be81a73ad894f9c8ba465171001997a72ff4
blob + 1a65819f0cf1669eabd51b18d3eb54dae677dbf0
--- amused.c
+++ amused.c
@@ -23,7 +23,6 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
-#include <poll.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -108,13 +107,13 @@ main_dispatch_player(int sig, int event, void *d)
 	ssize_t		 n;
 	int		 shut = 0;
 
-	if (event & POLLIN) {
+	if (event & EV_READ) {
 		if ((n = imsg_read(imsgbuf)) == -1 && errno != EAGAIN)
 			fatal("imsg_read error");
 		if (n == 0)	/* Connection closed */
 			shut = 1;
 	}
-	if (event & POLLOUT) {
+	if (event & EV_WRITE) {
 		if ((n = msgbuf_write(&imsgbuf->w)) == -1 && errno != EAGAIN)
 			fatal("msgbuf_write");
 		if (n == 0)	/* Connection closed */
@@ -277,7 +276,7 @@ amused_main(void)
 	iev_player = xmalloc(sizeof(*iev_player));
 	imsg_init(&iev_player->imsgbuf, pipe_main2player[0]);
 	iev_player->handler = main_dispatch_player;
-	iev_player->events = POLLIN;
+	iev_player->events = EV_READ;
 	ev_add(iev_player->imsgbuf.fd, iev_player->events,
 	    iev_player->handler, iev_player);
 
@@ -365,9 +364,9 @@ spawn_daemon(void)
 void
 imsg_event_add(struct imsgev *iev)
 {
-	iev->events = POLLIN;
+	iev->events = EV_READ;
 	if (iev->imsgbuf.w.queued)
-		iev->events |= POLLOUT;
+		iev->events |= EV_WRITE;
 
 	ev_add(iev->imsgbuf.fd, iev->events, iev->handler, iev);
 }
blob - 257d02f7f78471a6a377b68930cc73a5e8554f20
blob + c1c975ce279ad3396c8951b9dab02f4ef2374241
--- control.c
+++ control.c
@@ -29,7 +29,6 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
-#include <poll.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -45,6 +44,7 @@
 
 struct {
 	int		fd;
+	unsigned int	tout;
 	struct playlist	play;
 	int		tx;
 } control_state = {.fd = -1, .tx = -1};
@@ -113,7 +113,8 @@ control_init(char *path)
 static void
 enable_accept(int fd, int ev, void *bula)
 {
-	ev_add(control_state.fd, POLLIN, control_accept, NULL);
+	control_state.tout = 0;
+	ev_add(control_state.fd, EV_READ, control_accept, NULL);
 }
 
 int
@@ -150,7 +151,9 @@ control_accept(int listenfd, int event, void *bula)
 			struct timeval evtpause = { 1, 0 };
 
 			ev_del(control_state.fd);
-			ev_timer(&evtpause, enable_accept, NULL);
+			control_state.tout = ev_timer(&evtpause, enable_accept, NULL);
+			if (control_state.tout == 0)
+				fatal("ev_timer failed");
 		} else if (errno != EWOULDBLOCK && errno != EINTR &&
 		    errno != ECONNABORTED)
 			log_warn("%s: accept4", __func__);
@@ -171,7 +174,7 @@ control_accept(int listenfd, int event, void *bula)
 
 	imsg_init(&c->iev.imsgbuf, connfd);
 	c->iev.handler = control_dispatch_imsg;
-	c->iev.events = POLLIN;
+	c->iev.events = EV_READ;
 	ev_add(c->iev.imsgbuf.fd, c->iev.events, c->iev.handler, &c->iev);
 
 	TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
@@ -226,9 +229,9 @@ control_close(int fd)
 	close(c->iev.imsgbuf.fd);
 
 	/* Some file descriptors are available again. */
-	if (ev_timer_pending()) {
+	if (ev_timer_pending(control_state.tout)) {
 		ev_timer(NULL, NULL, NULL);
-		ev_add(control_state.fd, POLLIN, control_accept, NULL);
+		ev_add(control_state.fd, EV_READ, control_accept, NULL);
 	}
 
 	free(c);
@@ -285,14 +288,14 @@ control_dispatch_imsg(int fd, int event, void *bula)
 
 	imsgbuf = &c->iev.imsgbuf;
 
-	if (event & POLLIN) {
+	if (event & EV_READ) {
 		if (((n = imsg_read(imsgbuf)) == -1 && errno != EAGAIN) ||
 		    n == 0) {
 			control_close(fd);
 			return;
 		}
 	}
-	if (event & POLLOUT) {
+	if (event & EV_WRITE) {
 		if (msgbuf_write(&imsgbuf->w) <= 0 && errno != EAGAIN) {
 			control_close(fd);
 			return;
blob - 34f6474a60c36c69c8755554776633328c8cb829
blob + ee52583d16e300a724a09907f9e0217847264343
--- ev.c
+++ ev.c
@@ -25,12 +25,15 @@
 
 #include "config.h"
 
+#include <sys/time.h>
+
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
 #include <poll.h>
 #include <signal.h>
 #include <stdlib.h>
+#include <string.h>
 #include <time.h>
 #include <unistd.h>
 
@@ -41,6 +44,12 @@ struct evcb {
 	void		*udata;
 };
 
+struct evtimer {
+	unsigned int	 id;
+	struct timeval	 tv;
+	struct evcb	 cb;
+};
+
 struct evbase {
 	size_t		 len;
 
@@ -53,8 +62,19 @@ struct evbase {
 	int		 sigpipe[2];
 	struct evcb	 sigcb;
 
-	int		 timeout;
-	struct evcb	 toutcb;
+	unsigned int	 tid;
+
+	/*
+	 * Binary heap of timers.  At runtime, new timers are added in
+	 * the ``reserve'', a space after the caninocal end of the
+	 * array, and at the end of every tick they're added to the
+	 * heap.
+	 */
+	struct evtimer	*timers;
+	size_t		 ntimers;
+	size_t		 reserve_from;
+	size_t		 reserve_till;
+	size_t		 timerscap;
 };
 
 static struct evbase	*base;
@@ -98,7 +118,6 @@ ev_init(void)
 
 	base->sigpipe[0] = -1;
 	base->sigpipe[1] = -1;
-	base->timeout = INFTIM;
 
 	if (ev_resize(16) == -1) {
 		free(base->pfds);
@@ -111,16 +130,35 @@ ev_init(void)
 	return 0;
 }
 
+static inline int
+ev2poll(int ev)
+{
+	int	 ret = 0;
+
+	if (ev & EV_READ)
+		ret |= POLLIN;
+	if (ev & EV_WRITE)
+		ret |= POLLOUT;
+
+	return (ret);
+}
+
 int
 ev_add(int fd, int ev, void (*cb)(int, int, void *), void *udata)
 {
-	if (fd >= base->len) {
+	if (fd < 0) {
+		errno = EBADF;
+		return -1;
+	}
+
+	if ((size_t)fd >= base->len) {
 		if (ev_resize(fd + 1) == -1)
 			return -1;
 	}
 
 	base->pfds[fd].fd = fd;
-	base->pfds[fd].events = ev;
+	base->pfds[fd].events = ev2poll(ev);
+	base->pfds[fd].revents = 0;
 
 	base->cbs[fd].cb = cb;
 	base->cbs[fd].udata = udata;
@@ -154,7 +192,7 @@ ev_sigdispatch(int fd, int ev, void *data)
 	if (read(fd, &signo, sizeof(signo)) != sizeof(signo))
 		return;
 
-	base->sigcb.cb(signo, 0, base->sigcb.udata);
+	base->sigcb.cb(signo, EV_SIGNAL, base->sigcb.udata);
 }
 
 int
@@ -171,7 +209,7 @@ ev_signal(int sig, void (*cb)(int, int, void *), void 
 		    fcntl(base->sigpipe[1], F_SETFL, flags | O_NONBLOCK) == -1)
 			return -1;
 
-		if (ev_add(base->sigpipe[0], POLLIN, ev_sigdispatch, NULL)
+		if (ev_add(base->sigpipe[0], EV_READ, ev_sigdispatch, NULL)
 		    == -1)
 			return -1;
 	}
@@ -183,31 +221,148 @@ ev_signal(int sig, void (*cb)(int, int, void *), void 
 	return 0;
 }
 
-int
+unsigned int
 ev_timer(const struct timeval *tv, void (*cb)(int, int, void*), void *udata)
 {
-	base->timeout = INFTIM;
-	if (tv) {
-		base->timeout = tv->tv_sec * 1000;
-		base->timeout += tv->tv_usec / 1000;
+	struct evtimer	*evt;
+	void		*t;
+	size_t		 newcap;
+	unsigned int	 nextid;
+
+	if (tv == NULL) {
+		errno = EINVAL;
+		return 0;
 	}
 
-	base->toutcb.cb = cb;
-	base->toutcb.udata = udata;
+	if (base->reserve_till == base->timerscap) {
+		newcap = base->timerscap + 8;
+		t = recallocarray(base->timers, base->timerscap, newcap,
+		    sizeof(*base->timers));
+		if (t == NULL)
+			return 0;
+		base->timers = t;
+		base->timerscap = newcap;
+	}
 
-	return 0;
-}
+	if ((nextid = ++base->tid) == 0)
+		nextid = ++base->tid;
 
-int
-ev_timer_pending(void)
+	evt = &base->timers[base->reserve_till];
+	evt->id = nextid;
+	memcpy(&evt->tv, tv, sizeof(*tv));
+	evt->cb.cb = cb;
+	evt->cb.udata = udata;
+
+	base->reserve_till++;
+	return (nextid);
+}
+
+static int
+find_timer(unsigned int id, size_t *pos)
 {
-	return base->timeout != INFTIM;
+	size_t		 i;
+
+	if (id == 0)
+		return (0);
+
+	for (i = 0; i < base->ntimers; ++i) {
+		if (base->timers[i].id == id) {
+			*pos = i;
+			return (1);
+		}
+	}
+
+	for (i = base->reserve_from; i < base->reserve_till; ++i) {
+		if (base->timers[i].id == id) {
+			*pos = i;
+			return (1);
+		}
+	}
+
+	return (0);
 }
 
+int
+ev_timer_pending(unsigned int id)
+{
+	size_t		 i;
+
+	return (find_timer(id, &i));
+}
+
+static void
+bubbledown(size_t i)
+{
+	struct evtimer	 tmp;
+	size_t		 l, r, s;
+
+	for (;;) {
+		l = 2 * i + 1;
+		r = 2 * i + 2;
+
+		/* base case: there are no children */
+		if (l >= base->ntimers)
+			return;
+
+		/* find the smaller child */
+		s = r;
+		if (r >= base->ntimers ||
+		    timercmp(&base->timers[l].tv, &base->timers[r].tv, <))
+			s = l;
+
+		/* other base case: it's at the right place */
+		if (timercmp(&base->timers[i].tv, &base->timers[s].tv, <))
+			return;
+
+		/* swap */
+		memcpy(&tmp, &base->timers[s], sizeof(tmp));
+		memcpy(&base->timers[s], &base->timers[i], sizeof(tmp));
+		memcpy(&base->timers[i], &tmp, sizeof(tmp));
+
+		i = s;
+	}
+}
+
+static inline void
+cancel_timer(size_t i)
+{
+	base->ntimers--;
+	if (i != base->ntimers) {
+		memcpy(&base->timers[i], &base->timers[base->ntimers],
+		    sizeof(*base->timers));
+		bubbledown(i);
+	}
+}
+
 int
+ev_timer_cancel(unsigned int id)
+{
+	size_t		 i;
+
+	if (!find_timer(id, &i))
+		return (-1);
+
+	if (i < base->ntimers) {
+		cancel_timer(i);
+		return (0);
+	}
+
+	base->reserve_till--;
+	if (i != base->reserve_till)
+		memcpy(&base->timers[i], &base->timers[base->reserve_till],
+		    sizeof(*base->timers));
+	return (0);
+}
+
+int
 ev_del(int fd)
 {
-	if (fd >= base->len) {
+	if (fd < 0) {
+		errno = EBADF;
+		return -1;
+	}
+
+	if ((size_t)fd >= base->len) {
 		errno = ERANGE;
 		return -1;
 	}
@@ -221,43 +376,125 @@ ev_del(int fd)
 	return 0;
 }
 
+static void
+timerheapify(void)
+{
+	size_t		 i, reserve, gap;
+
+	reserve = base->reserve_till - base->reserve_from;
+	if (reserve == 0)
+		return;
+
+	gap = base->reserve_from - base->ntimers;
+	if (gap != 0) {
+		memmove(&base->timers[base->ntimers],
+		    &base->timers[base->reserve_from],
+		    reserve * sizeof(*base->timers));
+		base->reserve_from -= gap;
+		base->reserve_till -= gap;
+	}
+
+	base->ntimers = base->reserve_till;
+
+	if (base->ntimers < 2)
+		return;
+
+	i = base->ntimers / 2 - 1;
+	for (;;) {
+		bubbledown(i);
+		if (i == 0)
+			break;
+		i--;
+	}
+}
+
+static inline int
+poll2ev(int ev)
+{
+	int r = 0;
+
+	if (ev & (POLLIN|POLLHUP))
+		r |= EV_READ;
+	if (ev & (POLLOUT|POLLWRNORM|POLLWRBAND))
+		r |= EV_WRITE;
+
+	return (r);
+}
+
 int
-ev_loop(void)
+ev_step(void)
 {
 	struct timespec	 elapsed, beg, end;
-	int		 n, em;
+	struct timeval	 tv, sub, *min;
+	struct evcb	 cb;
+	int		 n, msec;
 	size_t		 i;
 
-	while (!ev_stop) {
-		clock_gettime(CLOCK_MONOTONIC, &beg);
-		if ((n = poll(base->pfds, base->len, base->timeout)) == -1) {
-			if (errno != EINTR)
-				return -1;
-		}
-		clock_gettime(CLOCK_MONOTONIC, &end);
+	timerheapify();
+	base->reserve_from = base->ntimers;
+	base->reserve_till = base->ntimers;
 
+	min = NULL;
+	msec = -1;
+	if (base->ntimers) {
+		min = &base->timers[0].tv;
+		msec = min->tv_sec * 1000 + (min->tv_usec + 999) / 1000;
+	}
+
+	clock_gettime(CLOCK_MONOTONIC, &beg);
+	if ((n = poll(base->pfds, base->len, msec)) == -1) {
+		if (errno != EINTR)
+			return -1;
+	}
+
+	if (n == 0 && min)
+		memcpy(&tv, min, sizeof(tv));
+	else {
+		clock_gettime(CLOCK_MONOTONIC, &end);
 		timespecsub(&end, &beg, &elapsed);
-		em = elapsed.tv_sec * 1000 + elapsed.tv_nsec / 1000000;
-		if (base->timeout != INFTIM) {
-			if (base->timeout - em < 0 || n == 0) {
-				base->timeout = INFTIM;
-				base->toutcb.cb(-1, 0, base->toutcb.udata);
-			} else
-				base->timeout -= em;
+		TIMESPEC_TO_TIMEVAL(&tv, &elapsed);
+	}
+
+	for (i = 0; i < base->ntimers && !ev_stop; /* nop */) {
+		timersub(&base->timers[i].tv, &tv, &sub);
+		if (sub.tv_sec <= 0) {
+			/*
+			 * delete the timer before calling its
+			 * callback; protects from timer that
+			 * attempt to delete themselves.
+			 */
+			memcpy(&cb, &base->timers[i].cb, sizeof(cb));
+			cancel_timer(i);
+			cb.cb(-1, EV_TIMEOUT, cb.udata);
+			continue;
 		}
 
-		for (i = 0; i < base->len && n > 0 && !ev_stop; ++i) {
-			if (base->pfds[i].fd == -1)
-				continue;
-			if (base->pfds[i].revents & (POLLIN|POLLOUT|POLLHUP)) {
-				n--;
-				base->cbs[i].cb(base->pfds[i].fd,
-				    base->pfds[i].revents,
-				    base->cbs[i].udata);
-			}
+		memcpy(&base->timers[i].tv, &sub, sizeof(sub));
+		i++;
+	}
+
+	for (i = 0; i < base->len && n > 0 && !ev_stop; ++i) {
+		if (base->pfds[i].fd == -1)
+			continue;
+		if (base->pfds[i].revents & (POLLIN|POLLOUT|POLLHUP)) {
+			n--;
+			base->cbs[i].cb(base->pfds[i].fd,
+			    poll2ev(base->pfds[i].revents),
+			    base->cbs[i].udata);
 		}
 	}
 
+	return (0);
+}
+
+int
+ev_loop(void)
+{
+	while (!ev_stop) {
+		if (ev_step() == -1)
+			return (-1);
+	}
+
 	return 0;
 }
 
blob - 7acacfe73721b2c98e67bc1df641f3c9510c1c61
blob + eced7ccf6ec9531156df977c26ab0244f27348fa
--- ev.h
+++ ev.h
@@ -25,11 +25,20 @@
 
 struct timeval;
 
-int	ev_init(void);
-int	ev_add(int, int, void(*)(int, int, void *), void *);
-int	ev_signal(int, void(*)(int, int, void * ), void *);
-int	ev_timer(const struct timeval *, void(*)(int, int, void *), void *);
-int	ev_timer_pending(void);
-int	ev_del(int);
-int	ev_loop(void);
-void	ev_break(void);
+#define EV_READ		0x1
+#define EV_WRITE	0x2
+#define EV_SIGNAL	0x4
+#define EV_TIMEOUT	0x8
+
+int		ev_init(void);
+int		ev_add(int, int, void(*)(int, int, void *), void *);
+int		ev_signal(int, void(*)(int, int, void * ), void *);
+unsigned int	ev_timer(const struct timeval *, void(*)(int, int, void *),
+		    void *);
+int		ev_timer_pending(unsigned int);
+int		ev_timer_cancel(unsigned int);
+int		ev_del(int);
+int		ev_step(void);
+int		ev_step(void);
+int		ev_loop(void);
+void		ev_break(void);
blob - b6666792ae040e1ee339dc20a68db0cf74208e6f
blob + 7ed26ffa838545a2a5e2e82b66f57f527e4aef7a
--- web/Makefile
+++ web/Makefile
@@ -44,7 +44,7 @@ uninstall:
 	rm ${DESTDIR}${MANDIR}/man1/${PROG}.1
 
 .c.o:
-	${CC} -I../ ${CFLAGS} -c $< -o $@
+	${CC} -I../ ${CFLAGS} -DBUFIO_WITHOUT_TLS -c $< -o $@
 
 # --- maintainer targets ---
 
blob - da7bde52d49d841a387fa5a9526fe9bb66410503
blob + 424516bc75c38f21feafbd73cdcc6c5b22799fd1
--- web/bufio.c
+++ web/bufio.c
@@ -27,7 +27,6 @@
 
 #include <assert.h>
 #include <errno.h>
-#include <poll.h>
 #include <stdarg.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -35,10 +34,14 @@
 #include <string.h>
 #include <unistd.h>
 
+#ifndef BUFIO_WITHOUT_TLS
+#include <tls.h>
+#endif
+
 #include "bufio.h"
 
 int
-buf_init(struct buffer *buf)
+buf_init(struct buf *buf)
 {
 	const size_t	 cap = BIO_CHUNK;
 
@@ -50,7 +53,7 @@ buf_init(struct buffer *buf)
 }
 
 static int
-buf_grow(struct buffer *buf)
+buf_grow(struct buf *buf)
 {
 	size_t		 newcap;
 	void		*t;
@@ -65,7 +68,7 @@ buf_grow(struct buffer *buf)
 }
 
 int
-buf_write(struct buffer *buf, const void *d, size_t len)
+buf_write(struct buf *buf, const void *d, size_t len)
 {
 	while (buf->len + len > buf->cap) {
 		if (buf_grow(buf) == -1)
@@ -77,14 +80,32 @@ buf_write(struct buffer *buf, const void *d, size_t le
 }
 
 int
-buf_has_line(struct buffer *buf, const char *nl)
+buf_has_line(struct buf *buf, const char *nl)
 {
 	return (memmem(buf->buf, buf->len, nl, strlen(nl)) != NULL);
 }
 
+char *
+buf_getdelim(struct buf *buf, const char *nl, size_t *len)
+{
+	uint8_t	*endl;
+	size_t	 nlen;
+
+	*len = 0;
+
+	nlen = strlen(nl);
+	if ((endl = memmem(buf->buf, buf->len, nl, nlen)) == NULL)
+		return (NULL);
+	*len = endl + nlen - buf->buf;
+	*endl = '\0';
+	return (buf->buf);
+}
+
 void
-buf_drain(struct buffer *buf, size_t l)
+buf_drain(struct buf *buf, size_t l)
 {
+	buf->cur = 0;
+
 	if (l >= buf->len) {
 		buf->len = 0;
 		return;
@@ -95,7 +116,7 @@ buf_drain(struct buffer *buf, size_t l)
 }
 
 void
-buf_drain_line(struct buffer *buf, const char *nl)
+buf_drain_line(struct buf *buf, const char *nl)
 {
 	uint8_t		*endln;
 	size_t		 nlen;
@@ -107,7 +128,7 @@ buf_drain_line(struct buffer *buf, const char *nl)
 }
 
 void
-buf_free(struct buffer *buf)
+buf_free(struct buf *buf)
 {
 	free(buf->buf);
 	memset(buf, 0, sizeof(*buf));
@@ -131,14 +152,47 @@ bufio_init(struct bufio *bio)
 void
 bufio_free(struct bufio *bio)
 {
+#ifndef BUFIO_WITHOUT_TLS
+	if (bio->ctx)
+		tls_free(bio->ctx);
+	bio->ctx = NULL;
+#endif
+
 	if (bio->fd != -1)
 		close(bio->fd);
+	bio->fd = -1;
 
 	buf_free(&bio->rbuf);
 	buf_free(&bio->wbuf);
 }
 
 int
+bufio_close(struct bufio *bio)
+{
+#ifndef BUFIO_WITHOUT_TLS
+	if (bio->ctx == NULL)
+		return (0);
+
+	switch (tls_close(bio->ctx)) {
+	case 0:
+		return 0;
+	case TLS_WANT_POLLIN:
+		errno = EAGAIN;
+		bio->wantev = BUFIO_WANT_READ;
+		return (-1);
+	case TLS_WANT_POLLOUT:
+		errno = EAGAIN;
+		bio->wantev = BUFIO_WANT_WRITE;
+		return (-1);
+	default:
+		return (-1);
+	}
+#else
+	return (0);
+#endif
+}
+
+int
 bufio_reset(struct bufio *bio)
 {
 	bufio_free(bio);
@@ -157,22 +211,98 @@ bufio_set_chunked(struct bufio *bio, int chunked)
 	bio->chunked = chunked;
 }
 
-short
-bufio_pollev(struct bufio *bio)
+int
+bufio_starttls(struct bufio *bio, const char *host, int insecure,
+    const uint8_t *cert, size_t certlen, const uint8_t *key, size_t keylen)
 {
-	short		 ev;
+#ifndef BUFIO_WITHOUT_TLS
+	struct tls_config	*conf;
 
-	ev = POLLIN;
-	if (bio->wbuf.len != 0)
-		ev |= POLLOUT;
+	if ((conf = tls_config_new()) == NULL)
+		return (-1);
+
+	if (insecure) {
+		tls_config_insecure_noverifycert(conf);
+		tls_config_insecure_noverifyname(conf);
+		tls_config_insecure_noverifytime(conf);
+	}
+
+	if (cert && tls_config_set_keypair_mem(conf, cert, certlen,
+	    key, keylen) == -1) {
+		tls_config_free(conf);
+		return (-1);
+	}
+
+	if ((bio->ctx = tls_client()) == NULL) {
+		tls_config_free(conf);
+		return (-1);
+	}
+
+	if (tls_configure(bio->ctx, conf) == -1) {
+		tls_config_free(conf);
+		return (-1);
+	}
+
+	tls_config_free(conf);
+
+	if (tls_connect_socket(bio->ctx, bio->fd, host) == -1)
+		return (-1);
+
+	return (0);
+#else
+	errno = EINVAL;
+	return (-1);
+#endif
+}
+
+int
+bufio_ev(struct bufio *bio)
+{
+	short		 ev;
 
+	if (bio->wantev)
+		return (bio->wantev);
+
+	ev = BUFIO_WANT_READ;
+	if (bio->wbuf.len != 0)
+		ev |= BUFIO_WANT_WRITE;
+
 	return (ev);
 }
 
+int
+bufio_handshake(struct bufio *bio)
+{
+#ifndef BUFIO_WITHOUT_TLS
+	if (bio->ctx == NULL) {
+		errno = EINVAL;
+		return (-1);
+	}
+
+	switch (tls_handshake(bio->ctx)) {
+	case 0:
+		return (0);
+	case TLS_WANT_POLLIN:
+		errno = EAGAIN;
+		bio->wantev = BUFIO_WANT_READ;
+		return (-1);
+	case TLS_WANT_POLLOUT:
+		errno = EAGAIN;
+		bio->wantev = BUFIO_WANT_WRITE;
+		return (-1);
+	default:
+		return (-1);
+	}
+#else
+	errno = EINVAL;
+	return (-1);
+#endif
+}
+
 ssize_t
 bufio_read(struct bufio *bio)
 {
-	struct buffer	*rbuf = &bio->rbuf;
+	struct buf	*rbuf = &bio->rbuf;
 	ssize_t		 r;
 
 	assert(rbuf->cap >= rbuf->len);
@@ -181,6 +311,29 @@ bufio_read(struct bufio *bio)
 			return (-1);
 	}
 
+#ifndef BUFIO_WITHOUT_TLS
+	if (bio->ctx) {
+		r = tls_read(bio->ctx, rbuf->buf + rbuf->len,
+		    rbuf->cap - rbuf->len);
+		switch (r) {
+		case TLS_WANT_POLLIN:
+			errno = EAGAIN;
+			bio->wantev = BUFIO_WANT_READ;
+			return (-1);
+		case TLS_WANT_POLLOUT:
+			errno = EAGAIN;
+			bio->wantev = BUFIO_WANT_WRITE;
+			return (-1);
+		case -1:
+			return (-1);
+		default:
+			bio->wantev = 0;
+			rbuf->len += r;
+			return (r);
+		}
+	}
+#endif
+
 	r = read(bio->fd, rbuf->buf + rbuf->len, rbuf->cap - rbuf->len);
 	if (r == -1)
 		return (-1);
@@ -191,7 +344,7 @@ bufio_read(struct bufio *bio)
 size_t
 bufio_drain(struct bufio *bio, void *d, size_t len)
 {
-	struct buffer	*rbuf = &bio->rbuf;
+	struct buf	*rbuf = &bio->rbuf;
 
 	if (len > rbuf->len)
 		len = rbuf->len;
@@ -203,9 +356,30 @@ bufio_drain(struct bufio *bio, void *d, size_t len)
 ssize_t
 bufio_write(struct bufio *bio)
 {
-	struct buffer	*wbuf = &bio->wbuf;
+	struct buf	*wbuf = &bio->wbuf;
 	ssize_t		 w;
 
+#ifndef BUFIO_WITHOUT_TLS
+	if (bio->ctx) {
+		switch (w = tls_write(bio->ctx, wbuf->buf, wbuf->len)) {
+		case TLS_WANT_POLLIN:
+			errno = EAGAIN;
+			bio->wantev = BUFIO_WANT_READ;
+			return (-1);
+		case TLS_WANT_POLLOUT:
+			errno = EAGAIN;
+			bio->wantev = BUFIO_WANT_WRITE;
+			return (-1);
+		case -1:
+			return (-1);
+		default:
+			bio->wantev = 0;
+			buf_drain(wbuf, w);
+			return (w);
+		}
+	}
+#endif
+
 	w = write(bio->fd, wbuf->buf, wbuf->len);
 	if (w == -1)
 		return (-1);
@@ -216,7 +390,7 @@ bufio_write(struct bufio *bio)
 static int
 bufio_append(struct bufio *bio, const void *d, size_t len)
 {
-	struct buffer	*wbuf = &bio->wbuf;
+	struct buf	*wbuf = &bio->wbuf;
 
 	if (len == 0)
 		return (0);
@@ -277,3 +451,31 @@ bufio_compose_fmt(struct bufio *bio, const char *fmt, 
 	free(str);
 	return (r);
 }
+
+void
+bufio_rewind_cursor(struct bufio *bio)
+{
+	bio->rbuf.cur = 0;
+}
+
+int
+bufio_get_cb(void *d)
+{
+	struct bufio	*bio = d;
+	struct buf	*rbuf = &bio->rbuf;
+
+	if (rbuf->cur >= rbuf->len)
+		return (EOF);
+	return (rbuf->buf[rbuf->cur++]);
+}
+
+int
+bufio_peek_cb(void *d)
+{
+	struct bufio	*bio = d;
+	struct buf	*rbuf = &bio->rbuf;
+
+	if (rbuf->cur >= rbuf->len)
+		return (EOF);
+	return (rbuf->buf[rbuf->cur]);
+}
blob - 98fdeb1d1412eae123638b65b2c840c85352e4ea
blob + 257a931f662a9d92b602a0b41305198f2ced784c
--- web/bufio.h
+++ web/bufio.h
@@ -23,33 +23,50 @@
  * OTHER DEALINGS IN THE SOFTWARE.
  */
 
-#define BIO_CHUNK	1024
-struct buffer {
+#ifndef BUFIO_WITHOUT_TLS
+struct tls;
+#endif
+
+#define BIO_CHUNK	128
+struct buf {
 	uint8_t		*buf;
 	size_t		 len;
 	size_t		 cap;
+	size_t		 cur;
 };
 
 struct bufio {
 	int		 fd;
 	int		 chunked;
-	struct buffer	 wbuf;
-	struct buffer	 rbuf;
+#ifndef BUFIO_WITHOUT_TLS
+	struct tls	*ctx;
+#endif
+	int		 wantev;
+	struct buf	 wbuf;
+	struct buf	 rbuf;
 };
 
-int	buf_init(struct buffer *);
-int	buf_write(struct buffer *, const void *, size_t);
-int	buf_has_line(struct buffer *, const char *);
-void	buf_drain(struct buffer *, size_t);
-void	buf_drain_line(struct buffer *, const char *);
-void	buf_free(struct buffer *);
+#define	BUFIO_WANT_READ		0x1
+#define	BUFIO_WANT_WRITE	0x2
 
+int	buf_init(struct buf *);
+int	buf_write(struct buf *, const void *, size_t);
+int	buf_has_line(struct buf *, const char *);
+char	*buf_getdelim(struct buf *, const char *, size_t *);
+void	buf_drain(struct buf *, size_t);
+void	buf_drain_line(struct buf *, const char *);
+void	buf_free(struct buf *);
+
 int	bufio_init(struct bufio *);
 void	bufio_free(struct bufio *);
+int	bufio_close(struct bufio *);
 int	bufio_reset(struct bufio *);
 void	bufio_set_fd(struct bufio *, int);
 void	bufio_set_chunked(struct bufio *, int);
-short	bufio_pollev(struct bufio *);
+int	bufio_starttls(struct bufio *, const char *, int,
+	    const uint8_t *, size_t, const uint8_t *, size_t);
+int	bufio_ev(struct bufio *);
+int	bufio_handshake(struct bufio *);
 ssize_t	bufio_read(struct bufio *);
 size_t	bufio_drain(struct bufio *, void *, size_t);
 ssize_t	bufio_write(struct bufio *);
@@ -57,3 +74,8 @@ int	bufio_compose(struct bufio *, const void *, size_t
 int	bufio_compose_str(struct bufio *, const char *);
 int	bufio_compose_fmt(struct bufio *, const char *, ...)
 	    __attribute__((__format__ (printf, 2, 3)));
+void	bufio_rewind_cursor(struct bufio *);
+
+/* callbacks for pdjson */
+int	bufio_get_cb(void *);
+int	bufio_peek_cb(void *);
blob - 75b8cd43bfd889cd1f33d56beb1869ad32225af7
blob + b24bd2f26157718c7e62a92aea5c2ae4fbfe148a
--- web/http.c
+++ web/http.c
@@ -61,7 +61,7 @@ http_init(struct client *clt, int fd)
 int
 http_parse(struct client *clt)
 {
-	struct buffer	*rbuf = &clt->bio.rbuf;
+	struct buf	*rbuf = &clt->bio.rbuf;
 	struct request	*req = &clt->req;
 	size_t		 len;
 	uint8_t		*endln;
@@ -188,7 +188,7 @@ int
 http_read(struct client *clt)
 {
 	struct request	*req = &clt->req;
-	struct buffer	*rbuf = &clt->bio.rbuf;
+	struct buf	*rbuf = &clt->bio.rbuf;
 	size_t		 left;
 
 	/* clients may have sent more data than advertised */
blob - b6888f4d5f07b6e4310164980fc32c0d3a82f9be
blob + 16ad7fe7ebf1a6a063c5bd5e0c857c199237825b
--- web/web.c
+++ web/web.c
@@ -27,7 +27,6 @@
 #include <limits.h>
 #include <locale.h>
 #include <netdb.h>
-#include <poll.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -272,6 +271,20 @@ const char *js =
 
 const char *foot = "<script src='/app.js?v=0'></script></body></html>";
 
+static inline int
+bio_ev(struct bufio *bio)
+{
+	int	 ret, ev;
+
+	ret = 0;
+	ev = bufio_ev(bio);
+	if (ev & BUFIO_WANT_READ)
+		ret |= EV_READ;
+	if (ev & BUFIO_WANT_WRITE)
+		ret |= EV_WRITE;
+	return ret;
+}
+
 static int
 dial(const char *sock)
 {
@@ -355,7 +368,7 @@ dispatch_event(const char *msg)
 		if (ws_compose(clt, WST_TEXT, msg, len) == -1)
 			ret = -1;
 
-		ev_add(clt->bio.fd, POLLIN|POLLOUT, client_ev, clt);
+		ev_add(clt->bio.fd, EV_READ|EV_WRITE, client_ev, clt);
 	}
 
 	return (ret);
@@ -445,13 +458,13 @@ imsg_dispatch(int fd, int ev, void *d)
 	size_t			 datalen;
 	int			 r;
 
-	if (ev & (POLLIN|POLLHUP)) {
+	if (ev & EV_READ) {
 		if ((n = imsg_read(&imsgbuf)) == -1 && errno != EAGAIN)
 			fatal("imsg_read");
 		if (n == 0)
 			fatalx("pipe closed");
 	}
-	if (ev & POLLOUT) {
+	if (ev & EV_WRITE) {
 		if ((n = msgbuf_write(&imsgbuf.w)) == -1 && errno != EAGAIN)
 			fatal("msgbuf_write");
 		if (n == 0)
@@ -563,9 +576,9 @@ imsg_dispatch(int fd, int ev, void *d)
 		}
 	}
 
-	ev = POLLIN;
+	ev = EV_READ;
 	if (imsgbuf.w.queued)
-		ev |= POLLOUT;
+		ev |= EV_WRITE;
 	ev_add(fd, ev, imsg_dispatch, NULL);
 }
 
@@ -691,7 +704,7 @@ route_jump(struct client *clt)
 
 		imsg_compose(&imsgbuf, IMSG_CTL_JUMP, 0, 0, -1,
 		    path, sizeof(path));
-		ev_add(imsgbuf.w.fd, POLLIN|POLLOUT, imsg_dispatch, NULL);
+		ev_add(imsgbuf.w.fd, EV_READ|EV_WRITE, imsg_dispatch, NULL);
 		break;
 	}
 
@@ -783,7 +796,7 @@ route_mode(struct client *clt)
 
 		imsg_compose(&imsgbuf, IMSG_CTL_MODE, 0, 0, -1,
 		    &pm, sizeof(pm));
-		ev_add(imsgbuf.w.fd, POLLIN|POLLOUT, imsg_dispatch, NULL);
+		ev_add(imsgbuf.w.fd, EV_READ|EV_WRITE, imsg_dispatch, NULL);
 		break;
 	}
 
@@ -804,7 +817,7 @@ route_mode(struct client *clt)
 static void
 route_handle_ws(struct client *clt)
 {
-	struct buffer	*rbuf = &clt->bio.rbuf;
+	struct buf	*rbuf = &clt->bio.rbuf;
 	int		 type;
 	size_t		 len;
 
@@ -923,14 +936,14 @@ client_ev(int fd, int ev, void *d)
 {
 	struct client	*clt = d;
 
-	if (ev & (POLLIN|POLLHUP)) {
+	if (ev & EV_READ) {
 		if (bufio_read(&clt->bio) == -1 && errno != EAGAIN) {
 			log_warn("bufio_read");
 			goto err;
 		}
 	}
 
-	if (ev & POLLOUT) {
+	if (ev & EV_WRITE) {
 		if (bufio_write(&clt->bio) == -1 && errno != EAGAIN) {
 			log_warn("bufio_write");
 			goto err;
@@ -959,8 +972,8 @@ client_ev(int fd, int ev, void *d)
 		clt->route(clt);
 
  again:
-	ev = bufio_pollev(&clt->bio);
-	if (ev == POLLIN && (clt->done || clt->err)) {
+	ev = bio_ev(&clt->bio);
+	if (ev == EV_READ && (clt->done || clt->err)) {
 		goto err; /* done with this client */
 	}
 
@@ -993,7 +1006,7 @@ web_accept(int psock, int ev, void *d)
 
 	TAILQ_INSERT_TAIL(&clients, clt, clients);
 
-	client_ev(sock, POLLIN, clt);
+	client_ev(sock, EV_READ, clt);
 	return;
 }
 
@@ -1070,7 +1083,7 @@ main(int argc, char **argv)
 	imsg_compose(&imsgbuf, IMSG_CTL_SHOW, 0, 0, -1, NULL, 0);
 	imsg_compose(&imsgbuf, IMSG_CTL_STATUS, 0, 0, -1, NULL, 0);
 	imsg_compose(&imsgbuf, IMSG_CTL_MONITOR, 0, 0, -1, NULL, 0);
-	ev_add(amused_sock, POLLIN|POLLOUT, imsg_dispatch, NULL);
+	ev_add(amused_sock, EV_READ|EV_WRITE, imsg_dispatch, NULL);
 
 	memset(&hints, 0, sizeof(hints));
 	hints.ai_family = AF_UNSPEC;
@@ -1105,7 +1118,7 @@ main(int argc, char **argv)
 		if (listen(fd, 5) == -1)
 			err(1, "listen");
 
-		if (ev_add(fd, POLLIN, web_accept, NULL) == -1)
+		if (ev_add(fd, EV_READ, web_accept, NULL) == -1)
 			fatal("ev_add");
 		nsock++;
 	}
blob - 16fab184ad65d5ba6cbe6e2c5fa25f9b404af1f1
blob + 0e7302da3d95b628068ffacd9a5d67dd102a5cc1
--- web/ws.c
+++ web/ws.c
@@ -129,7 +129,7 @@ ws_accept_hdr(const char *secret, char *out, size_t ol
 int
 ws_read(struct client *clt, int *type, size_t *len)
 {
-	struct buffer	*rbuf = &clt->bio.rbuf;
+	struct buf	*rbuf = &clt->bio.rbuf;
 	size_t		 i;
 	uint32_t	 mask;
 	uint8_t		 first, second, op, plen;