lib_LIBRARIES = libht.a
-libht_a_SOURCES = utils.c mt.c log.c req.c proc.c mtio.c resp.c cf.c
+libht_a_SOURCES = utils.c mt.c log.c req.c proc.c mtio.c resp.c \
+ cf.c bufio.c
libht_a_CFLAGS = -fPIC
if USE_EPOLL
libht_a_SOURCES += mtio-epoll.c
endif
endif
-pkginclude_HEADERS = utils.h mt.h log.h req.h proc.h mtio.h resp.h cf.h
+pkginclude_HEADERS = utils.h mt.h log.h req.h proc.h mtio.h resp.h \
+ cf.h bufio.h
--- /dev/null
+/*
+ ashd - A Sane HTTP Daemon
+ Copyright (C) 2008 Fredrik Tolf <fredrik@dolda2000.com>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <utils.h>
+#include <bufio.h>
+
+struct bufio *bioopen(void *pdata, struct bufioops *ops)
+{
+ struct bufio *bio;
+
+ omalloc(bio);
+ bio->pdata = pdata;
+ bio->ops = ops;
+ bio->bufhint = 4096;
+ return(bio);
+}
+
+int bioclose(struct bufio *bio)
+{
+ int rv;
+
+ bioflush(bio);
+ if(bio->ops->close)
+ rv = bio->ops->close(bio->pdata);
+ else
+ rv = 0;
+ buffree(bio->rbuf);
+ buffree(bio->wbuf);
+ free(bio);
+ return(rv);
+}
+
+size_t biordata(struct bufio *bio)
+{
+ return(bio->rbuf.d - bio->rh);
+}
+
+size_t biorspace(struct bufio *bio)
+{
+ if((bio->rbuf.d - bio->rh) >= bio->bufhint)
+ return(0);
+ return(bio->bufhint - (bio->rbuf.d - bio->rh));
+}
+
+int bioeof(struct bufio *bio)
+{
+ return(bio->eof && (bio->rh >= bio->rbuf.d));
+}
+
+static ssize_t biofill(struct bufio *bio)
+{
+ size_t ns;
+ ssize_t ret;
+
+ if(!bio->ops->read) {
+ bio->eof = 1;
+ return(0);
+ }
+ if(bio->eof)
+ return(0);
+ if(bio->rh == bio->rbuf.d)
+ bio->rh = bio->rbuf.d = 0;
+ if(bio->rbuf.d == bio->rbuf.s) {
+ if(bio->rh > 0) {
+ memmove(bio->rbuf.b, bio->rbuf.b + bio->rh, bio->rbuf.d -= bio->rh);
+ bio->rh = 0;
+ } else {
+ if((ns = bio->rbuf.s * 2) < bio->bufhint)
+ ns = bio->bufhint;
+ sizebuf(bio->rbuf, ns);
+ }
+ }
+ if((bio->rbuf.s > bio->bufhint) && (bio->rbuf.d < bio->bufhint))
+ bio->rbuf.b = srealloc(bio->rbuf.b, bio->rbuf.s = bio->bufhint);
+ ret = bio->ops->read(bio->pdata, bio->rbuf.b + bio->rbuf.d, bio->rbuf.s - bio->rbuf.d);
+ if(ret < 0) {
+ bio->err = errno;
+ return(-1);
+ } else if(ret == 0) {
+ bio->eof = 1;
+ return(0);
+ }
+ bio->rbuf.d += ret;
+ return(bio->rbuf.d - bio->rh);
+}
+
+ssize_t biorensure(struct bufio *bio, size_t bytes)
+{
+ ssize_t ret;
+
+ while(bio->rbuf.d - bio->rh < bytes) {
+ if((ret = biofill(bio)) <= 0)
+ return(ret);
+ }
+ return(bio->rbuf.d - bio->rh);
+}
+
+ssize_t biofillsome(struct bufio *bio)
+{
+ return(biofill(bio));
+}
+
+int biogetc(struct bufio *bio)
+{
+ ssize_t ret;
+
+ while(bio->rbuf.d <= bio->rh) {
+ if((ret = biofill(bio)) <= 0)
+ return(EOF);
+ }
+ return((unsigned char)bio->rbuf.b[bio->rh++]);
+}
+
+ssize_t bioreadsome(struct bufio *bio, void *buf, size_t len)
+{
+ ssize_t ret;
+
+ if((bio->rh >= bio->rbuf.d) && ((ret = biofill(bio)) <= 0))
+ return(ret);
+ ret = min(len, bio->rbuf.d - bio->rh);
+ memcpy(buf, bio->rbuf.b + bio->rh, ret);
+ bio->rh += ret;
+ return(ret);
+}
+
+size_t biowdata(struct bufio *bio)
+{
+ return(bio->wbuf.d - bio->wh);
+}
+
+size_t biowspace(struct bufio *bio)
+{
+ if((bio->wbuf.d - bio->wh) >= bio->bufhint)
+ return(0);
+ return(bio->bufhint - (bio->wbuf.d - bio->wh));
+}
+
+int bioflush(struct bufio *bio)
+{
+ ssize_t ret;
+
+ while(bio->wh < bio->wbuf.d) {
+ ret = bio->ops->write(bio->pdata, bio->wbuf.b + bio->wh, bio->wbuf.d - bio->wh);
+ if(ret < 0) {
+ bio->err = errno;
+ return(-1);
+ }
+ bio->wh += ret;
+ }
+ return(0);
+}
+
+int bioflushsome(struct bufio *bio)
+{
+ ssize_t ret;
+
+ if(bio->wh < bio->wbuf.d) {
+ ret = bio->ops->write(bio->pdata, bio->wbuf.b + bio->wh, bio->wbuf.d - bio->wh);
+ if(ret < 0) {
+ bio->err = errno;
+ return(-1);
+ }
+ bio->wh += ret;
+ return(1);
+ } else {
+ return(0);
+ }
+}
+
+ssize_t biowensure(struct bufio *bio, size_t bytes)
+{
+ if(bio->wbuf.s - bio->wbuf.d < bytes) {
+ if(!bio->ops->write) {
+ errno = bio->err = EPIPE;
+ return(-1);
+ }
+ if(bioflush(bio) < 0)
+ return(-1);
+ bio->wh = bio->wbuf.d = 0;
+ if((bio->wbuf.s > bio->bufhint) && (bytes <= bio->bufhint))
+ bio->wbuf.b = srealloc(bio->wbuf.b, bio->wbuf.s = bio->bufhint);
+ else
+ sizebuf(bio->wbuf, (bytes < bio->bufhint)?bio->bufhint:bytes);
+ }
+ return(0);
+}
+
+int bioputc(struct bufio *bio, int c)
+{
+ if(biowensure(bio, 1) < 0)
+ return(-1);
+ bio->wbuf.b[bio->wbuf.d++] = c;
+ return(0);
+}
+
+ssize_t biowrite(struct bufio *bio, const void *data, size_t len)
+{
+ ssize_t wb, ret;
+
+ wb = 0;
+ while(len > 0) {
+ if(biowensure(bio, min(len, bio->bufhint)) < 0) {
+ if(wb > 0)
+ return(wb);
+ return(-1);
+ }
+ if(len < bio->wbuf.s - bio->wbuf.d) {
+ memcpy(bio->wbuf.b + bio->wbuf.d, data, len);
+ bio->wbuf.d += len;
+ wb += len;
+ len = 0;
+ } else {
+ if(bioflush(bio) < 0) {
+ if(wb > 0)
+ return(wb);
+ return(-1);
+ }
+ bio->wh = bio->wbuf.d = 0;
+ ret = bio->ops->write(bio->pdata, data, len);
+ if(ret < 0) {
+ if(wb > 0)
+ return(wb);
+ bio->err = errno;
+ return(-1);
+ }
+ data += ret; len -= ret; wb += ret;
+ }
+ }
+ return(wb);
+}
+
+ssize_t biowritesome(struct bufio *bio, const void *data, size_t len)
+{
+ ssize_t ret;
+
+ sizebuf(bio->wbuf, bio->bufhint);
+ if(bio->wh == bio->wbuf.d)
+ bio->wh = bio->wbuf.d = 0;
+ if(bio->wbuf.d == bio->wbuf.s) {
+ if(bio->wh > 0) {
+ memmove(bio->wbuf.b, bio->wbuf.b + bio->wh, bio->wbuf.d -= bio->wh);
+ bio->wh = 0;
+ }
+ }
+ ret = min(len, bio->wbuf.s - bio->wbuf.d);
+ memcpy(bio->wbuf.b + bio->wbuf.d, data, ret);
+ bio->wbuf.d += ret;
+ if(bioflushsome(bio) < 0) {
+ if(ret == 0)
+ return(-1);
+ if(ret < bio->wbuf.d - bio->wh) { /* Should never be false */
+ bio->wbuf.d -= ret;
+ return(-1);
+ }
+ }
+ return(ret);
+}
+
+int bioprintf(struct bufio *bio, const char *format, ...)
+{
+ va_list args;
+ int ret;
+
+ if(biowensure(bio, strlen(format)) < 0)
+ return(-1);
+ while(1) {
+ va_start(args, format);
+ ret = vsnprintf(bio->wbuf.b + bio->wbuf.d, bio->wbuf.s - bio->wbuf.d, format, args);
+ va_end(args);
+ if(ret <= bio->wbuf.s - bio->wbuf.d) {
+ bio->wbuf.d += ret;
+ return(0);
+ }
+ if(biowensure(bio, ret) < 0)
+ return(-1);
+ }
+}
+
+ssize_t biocopysome(struct bufio *dst, struct bufio *src)
+{
+ ssize_t ret;
+
+ if(src->rh >= src->rbuf.d)
+ return(0);
+ if((ret = biowritesome(dst, src->rbuf.b + src->rh, src->rbuf.d - src->rh)) < 0)
+ return(-1);
+ src->rh += ret;
+ return(ret);
+}
+
+ssize_t biocopybuf(struct bufio *dst, struct bufio *src)
+{
+ ssize_t ret;
+
+ sizebuf(dst->wbuf, dst->bufhint);
+ if(dst->wbuf.d == dst->wbuf.s) {
+ if(dst->wh > 0) {
+ memmove(dst->wbuf.b, dst->wbuf.b + dst->wh, dst->wbuf.d -= dst->wh);
+ dst->wh = 0;
+ }
+ }
+ ret = min(src->rbuf.d - src->rh, dst->wbuf.s - dst->wbuf.d);
+ memcpy(dst->wbuf.b + dst->wbuf.d, src->rbuf.b + src->rh, ret);
+ src->rh += ret;
+ dst->wbuf.d += ret;
+ return(ret);
+}
--- /dev/null
+#ifndef _LIB_BUFIO_H
+#define _LIB_BUFIO_H
+
+struct bufioops {
+ ssize_t (*read)(void *pdata, void *buf, size_t len);
+ ssize_t (*write)(void *pdata, const void *buf, size_t len);
+ int (*close)(void *pdata);
+};
+
+struct bufio {
+ struct charbuf rbuf, wbuf;
+ size_t rh, wh, bufhint;
+ int err, eof;
+ void *pdata;
+ struct bufioops *ops;
+};
+
+struct bufio *bioopen(void *pdata, struct bufioops *ops);
+int bioclose(struct bufio *bio);
+
+size_t biordata(struct bufio *bio);
+size_t biorspace(struct bufio *bio);
+int bioeof(struct bufio *bio);
+ssize_t biorensure(struct bufio *bio, size_t bytes);
+ssize_t biofillsome(struct bufio *bio);
+int biogetc(struct bufio *bio);
+ssize_t bioreadsome(struct bufio *bio, void *buf, size_t len);
+
+size_t biowdata(struct bufio *bio);
+size_t biowspace(struct bufio *bio);
+int bioflush(struct bufio *bio);
+int bioflushsome(struct bufio *bio);
+ssize_t biowensure(struct bufio *bio, size_t bytes);
+int bioputc(struct bufio *bio, int c);
+ssize_t biowrite(struct bufio *bio, const void *data, size_t len);
+ssize_t biowritesome(struct bufio *bio, const void *data, size_t len);
+int bioprintf(struct bufio *bio, const char *format, ...);
+ssize_t biocopysome(struct bufio *dst, struct bufio *src);
+ssize_t biocopybuf(struct bufio *dst, struct bufio *src);
+
+#endif
struct blocker {
struct blocker *n, *p, *n2, *p2;
int fd, reg;
- int ev;
+ int ev, rev, id;
time_t to;
struct muth *th;
};
bl->reg = 0;
}
-int block(int fd, int ev, time_t to)
+static int addblock(struct blocker *bl)
{
- struct blocker *bl;
- int rv;
-
- omalloc(bl);
- bl->fd = fd;
- bl->ev = ev;
- if(to > 0)
- bl->to = time(NULL) + to;
- bl->th = current;
- if((epfd >= 0) && regfd(bl)) {
- free(bl);
+ if((epfd >= 0) && regfd(bl))
return(-1);
- }
bl->n = blockers;
if(blockers)
blockers->p = bl;
blockers = bl;
- rv = yield();
+ return(0);
+}
+
+static void remblock(struct blocker *bl)
+{
if(bl->n)
bl->n->p = bl->p;
if(bl->p)
bl->p->n = bl->n;
- if(bl == blockers)
+ if(blockers == bl)
blockers = bl->n;
remfd(bl);
- free(bl);
+}
+
+struct selected mblock(time_t to, int n, struct selected *spec)
+{
+ int i, id;
+ struct blocker bls[n];
+
+ to = (to > 0)?(time(NULL) + to):0;
+ for(i = 0; i < n; i++) {
+ bls[i] = (struct blocker) {
+ .fd = spec[i].fd,
+ .ev = spec[i].ev,
+ .id = i,
+ .to = to,
+ .th = current,
+ };
+ if(addblock(&bls[i])) {
+ for(i--; i >= 0; i--)
+ remblock(&bls[i]);
+ return((struct selected){.fd = -1, .ev = -1});
+ }
+ }
+ id = yield();
+ for(i = 0; i < n; i++)
+ remblock(&bls[i]);
+ if(id < 0)
+ return((struct selected){.fd = -1, .ev = -1});
+ return((struct selected){.fd = bls[id].fd, .ev = bls[id].rev});
+}
+
+int block(int fd, int ev, time_t to)
+{
+ struct blocker bl;
+ int rv;
+
+ bl = (struct blocker) {
+ .fd = fd,
+ .ev = ev,
+ .id = -1,
+ .to = (to > 0)?(time(NULL) + to):0,
+ .th = current,
+ };
+ if(addblock(&bl))
+ return(-1);
+ rv = yield();
+ remblock(&bl);
return(rv);
}
ev = -1;
for(bl = fdlist[fd]; bl; bl = nbl) {
nbl = bl->n2;
- if((ev < 0) || (ev & bl->ev))
- resume(bl->th, ev);
+ if((ev < 0) || (ev & bl->ev)) {
+ if(bl->id < 0) {
+ resume(bl->th, ev);
+ } else {
+ bl->rev = ev;
+ resume(bl->th, bl->id);
+ }
+ }
}
}
now = time(NULL);
for(bl = blockers; bl; bl = nbl) {
nbl = bl->n;
- if((bl->to != 0) && (bl->to <= now))
- resume(bl->th, 0);
+ if((bl->to != 0) && (bl->to <= now)) {
+ if(bl->id < 0) {
+ resume(bl->th, 0);
+ } else {
+ bl->rev = 0;
+ resume(bl->th, bl->id);
+ }
+ }
}
}
for(bl = blockers; bl; bl = bl->n)
struct blocker {
struct blocker *n, *p, *n2, *p2;
int fd, reg;
- int ev;
+ int ev, rev, id;
time_t to;
struct muth *th;
};
bl->reg = 0;
}
-int block(int fd, int ev, time_t to)
+static int addblock(struct blocker *bl)
{
- struct blocker *bl;
- int rv;
-
- omalloc(bl);
- bl->fd = fd;
- bl->ev = ev;
- if(to > 0)
- bl->to = time(NULL) + to;
- bl->th = current;
- if((qfd >= 0) && regfd(bl)) {
- free(bl);
+ if((qfd >= 0) && regfd(bl))
return(-1);
- }
bl->n = blockers;
if(blockers)
blockers->p = bl;
blockers = bl;
- rv = yield();
+ return(0);
+}
+
+static void remblock(struct blocker *bl)
+{
if(bl->n)
bl->n->p = bl->p;
if(bl->p)
if(bl == blockers)
blockers = bl->n;
remfd(bl);
- free(bl);
+}
+
+struct selected mblock(time_t to, int n, struct selected *spec)
+{
+ int i, id;
+ struct blocker bls[n];
+
+ to = (to > 0)?(time(NULL) + to):0;
+ for(i = 0; i < n; i++) {
+ bls[i] = (struct blocker) {
+ .fd = spec[i].fd,
+ .ev = spec[i].ev,
+ .id = i,
+ .to = to,
+ .th = current,
+ };
+ if(addblock(&bls[i])) {
+ for(i--; i >= 0; i--)
+ remblock(&bls[i]);
+ return((struct selected){.fd = -1, .ev = -1});
+ }
+ }
+ id = yield();
+ for(i = 0; i < n; i++)
+ remblock(&bls[i]);
+ if(id < 0)
+ return((struct selected){.fd = -1, .ev = -1});
+ return((struct selected){.fd = bls[id].fd, .ev = bls[id].rev});
+}
+
+int block(int fd, int ev, time_t to)
+{
+ struct blocker bl;
+ int rv;
+
+ bl = (struct blocker) {
+ .fd = fd,
+ .ev = ev,
+ .id = -1,
+ .to = (to > 0)?(time(NULL) + to):0,
+ .th = current,
+ };
+ addblock(&bl);
+ rv = yield();
+ remblock(&bl);
return(rv);
}
ev = (evs[i].filter == EVFILT_READ)?EV_READ:EV_WRITE;
for(bl = fdlist[fd]; bl; bl = nbl) {
nbl = bl->n2;
- if(ev & bl->ev)
- resume(bl->th, ev);
+ if(ev & bl->ev) {
+ if(bl->id < 0) {
+ resume(bl->th, ev);
+ } else {
+ bl->rev = ev;
+ resume(bl->th, bl->id);
+ }
+ }
}
}
now = time(NULL);
for(bl = blockers; bl; bl = nbl) {
nbl = bl->n;
- if((bl->to != 0) && (bl->to <= now))
- resume(bl->th, 0);
+ if((bl->to != 0) && (bl->to <= now)) {
+ if(bl->id < 0) {
+ resume(bl->th, 0);
+ } else {
+ bl->rev = 0;
+ resume(bl->th, bl->id);
+ }
+ }
}
}
for(bl = blockers; bl; bl = bl->n)
struct blocker {
struct blocker *n, *p;
int fd;
- int ev;
+ int ev, rev, id;
time_t to;
struct muth *th;
};
-int block(int fd, int ev, time_t to)
+static void addblock(struct blocker *bl)
{
- struct blocker *bl;
- int rv;
-
- if(fd >= FD_SETSIZE) {
- flog(LOG_ERR, "tried to use more file descriptors than select() can handle: fd %i", fd);
- errno = EMFILE;
- return(-1);
- }
- omalloc(bl);
- bl->fd = fd;
- bl->ev = ev;
- if(to > 0)
- bl->to = time(NULL) + to;
- bl->th = current;
bl->n = blockers;
if(blockers)
blockers->p = bl;
blockers = bl;
- rv = yield();
+}
+
+static void remblock(struct blocker *bl)
+{
if(bl->n)
bl->n->p = bl->p;
if(bl->p)
bl->p->n = bl->n;
if(bl == blockers)
blockers = bl->n;
- free(bl);
+}
+
+struct selected mblock(time_t to, int n, struct selected *spec)
+{
+ int i, id;
+ struct blocker bls[n];
+
+ to = (to > 0)?(time(NULL) + to):0;
+ for(i = 0; i < n; i++) {
+ bls[i] = (struct blocker){
+ .fd = spec[i].fd,
+ .ev = spec[i].ev,
+ .id = i,
+ .to = to,
+ .th = current,
+ };
+ addblock(&bls[i]);
+ }
+ id = yield();
+ for(i = 0; i < n; i++)
+ remblock(&bls[i]);
+ if(id < 0)
+ return((struct selected){.fd = -1, .ev = -1});
+ return((struct selected){.fd = bls[id].fd, .ev = bls[id].rev});
+}
+
+int block(int fd, int ev, time_t to)
+{
+ struct blocker bl;
+ int rv;
+
+ if(fd >= FD_SETSIZE) {
+ flog(LOG_ERR, "tried to use more file descriptors than select() can handle: fd %i", fd);
+ errno = EMFILE;
+ return(-1);
+ }
+ bl = (struct blocker) {
+ .fd = fd,
+ .ev = ev,
+ .id = -1,
+ .to = (to > 0)?(time(NULL) + to):0,
+ .th = current,
+ };
+ addblock(&bl);
+ rv = yield();
+ remblock(&bl);
return(rv);
}
ev |= EV_WRITE;
if(FD_ISSET(bl->fd, &efds))
ev = -1;
- if((ev < 0) || (ev & bl->ev))
- resume(bl->th, ev);
- else if((bl->to != 0) && (bl->to <= now))
- resume(bl->th, 0);
+ if((ev < 0) || (ev & bl->ev)) {
+ if(bl->id < 0) {
+ resume(bl->th, ev);
+ } else {
+ bl->rev = ev;
+ resume(bl->th, bl->id);
+ }
+ } else if((bl->to != 0) && (bl->to <= now)) {
+ if(bl->id < 0) {
+ resume(bl->th, 0);
+ } else {
+ bl->rev = 0;
+ resume(bl->th, bl->id);
+ }
+ }
}
}
}
#include <utils.h>
#include <mt.h>
#include <mtio.h>
+#include <bufio.h>
+
+static ssize_t mtrecv(struct stdiofd *d, void *buf, size_t len)
+{
+ struct msghdr msg;
+ char cbuf[512];
+ struct cmsghdr *cmsg;
+ struct iovec bufvec;
+ socklen_t clen;
+ ssize_t ret;
+ int i, *fds;
+
+ msg = (struct msghdr){};
+ msg.msg_iov = &bufvec;
+ msg.msg_iovlen = 1;
+ bufvec.iov_base = buf;
+ bufvec.iov_len = len;
+ msg.msg_control = cbuf;
+ msg.msg_controllen = sizeof(cbuf);
+ if((ret = recvmsg(d->fd, &msg, MSG_DONTWAIT)) < 0)
+ return(ret);
+ for(cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SCM_RIGHTS)) {
+ fds = (int *)CMSG_DATA(cmsg);
+ clen = (cmsg->cmsg_len - ((char *)fds - (char *)cmsg)) / sizeof(*fds);
+ for(i = 0; i < clen; i++) {
+ if(d->rights < 0)
+ d->rights = fds[i];
+ else
+ close(fds[i]);
+ }
+ }
+ }
+ return(ret);
+}
static ssize_t mtread(void *cookie, void *buf, size_t len)
{
ssize_t ret;
while(1) {
- ret = read(d->fd, buf, len);
+ if(d->sock)
+ ret = mtrecv(d, buf, len);
+ else
+ ret = read(d->fd, buf, len);
if((ret < 0) && (errno == EAGAIN)) {
ev = block(d->fd, EV_READ, d->timeout);
if(ev < 0) {
}
}
+static ssize_t mtsend(struct stdiofd *d, const void *buf, size_t len)
+{
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ char cbuf[CMSG_SPACE(sizeof(int))];
+ struct iovec bufvec;
+ ssize_t ret;
+ int cr;
+
+ msg = (struct msghdr){};
+ msg.msg_iov = &bufvec;
+ msg.msg_iovlen = 1;
+ bufvec.iov_base = (void *)buf;
+ bufvec.iov_len = len;
+ cr = -1;
+ if(d->sendrights >= 0) {
+ msg.msg_control = cbuf;
+ msg.msg_controllen = sizeof(cbuf);
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ *((int *)CMSG_DATA(cmsg)) = d->sendrights;
+ cr = d->sendrights;
+ d->sendrights = -1;
+ msg.msg_controllen = cmsg->cmsg_len;
+ }
+ ret = sendmsg(d->fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL);
+ if(cr >= 0)
+ close(cr);
+ return(ret);
+}
+
static ssize_t mtwrite(void *cookie, const void *buf, size_t len)
{
struct stdiofd *d = cookie;
while(1) {
if(d->sock)
- ret = send(d->fd, buf, len, MSG_DONTWAIT | MSG_NOSIGNAL);
+ ret = mtsend(d, buf, len);
else
ret = write(d->fd, buf, len);
if((ret < 0) && (errno == EAGAIN)) {
struct stdiofd *d = cookie;
close(d->fd);
+ if(d->rights >= 0)
+ close(d->rights);
+ if(d->sendrights >= 0)
+ close(d->sendrights);
free(d);
return(0);
}
d->fd = fd;
d->sock = issock;
d->timeout = timeout;
+ d->rights = d->sendrights = -1;
if(!(ret = funstdio(d, r?mtread:NULL, w?mtwrite:NULL, NULL, mtclose))) {
free(d);
return(NULL);
return(ret);
}
+struct bufio *mtbioopen(int fd, int issock, int timeout, char *mode, struct stdiofd **infop)
+{
+ static struct bufioops ops = {
+ .read = mtread, .write = mtwrite, .close = mtclose,
+ };
+ struct stdiofd *d;
+ struct bufio *ret;
+
+ if(!strcmp(mode, "r")) {
+ } else if(!strcmp(mode, "w")) {
+ } else if(!strcmp(mode, "r+")) {
+ } else {
+ return(NULL);
+ }
+ omalloc(d);
+ d->fd = fd;
+ d->sock = issock;
+ d->timeout = timeout;
+ d->rights = d->sendrights = -1;
+ if(!(ret = bioopen(d, &ops))) {
+ free(d);
+ return(NULL);
+ }
+ fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
+ if(infop)
+ *infop = d;
+ return(ret);
+}
+
struct pipe {
struct charbuf data;
size_t bufmax;
int fd;
int sock;
int timeout;
+ int rights, sendrights;
};
+struct selected {
+ int fd, ev;
+};
+
+struct selected mblock(time_t to, int n, struct selected *spec);
int block(int fd, int ev, time_t to);
int ioloop(void);
void exitioloop(int status);
FILE *mtstdopen(int fd, int issock, int timeout, char *mode, struct stdiofd **infop);
+struct bufio *mtbioopen(int fd, int issock, int timeout, char *mode, struct stdiofd **infop);
void mtiopipe(FILE **read, FILE **write);
#endif
#include <log.h>
#include <req.h>
#include <proc.h>
+#include <bufio.h>
struct hthead *mkreq(char *method, char *url, char *ver)
{
return(-1);
}
+int parseheadersb(struct hthead *head, struct bufio *in)
+{
+ int c, state;
+ struct charbuf name, val;
+ size_t tsz;
+
+ bufinit(name);
+ bufinit(val);
+ state = 0;
+ tsz = 0;
+ while(1) {
+ c = biogetc(in);
+ if(++tsz >= 65536)
+ goto fail;
+ again:
+ if(state == 0) {
+ if(c == '\r') {
+ } else if(c == '\n') {
+ break;
+ } else if(c == EOF) {
+ goto fail;
+ } else {
+ state = 1;
+ goto again;
+ }
+ } else if(state == 1) {
+ if(c == ':') {
+ trim(&name);
+ bufadd(name, 0);
+ state = 2;
+ } else if(c == '\r') {
+ } else if(c == '\n') {
+ goto fail;
+ } else if(c == EOF) {
+ goto fail;
+ } else {
+ bufadd(name, c);
+ }
+ } else if(state == 2) {
+ if(c == '\r') {
+ } else if(c == '\n') {
+ trim(&val);
+ bufadd(val, 0);
+ headappheader(head, name.b, val.b);
+ buffree(name);
+ buffree(val);
+ state = 0;
+ } else if(c == EOF) {
+ goto fail;
+ } else {
+ bufadd(val, c);
+ }
+ }
+ }
+ return(0);
+
+fail:
+ buffree(name);
+ buffree(val);
+ return(-1);
+}
+
struct hthead *parseresponse(FILE *in)
{
struct hthead *req;
return(req);
}
+struct hthead *parseresponseb(struct bufio *in)
+{
+ struct hthead *req;
+ int code;
+ struct charbuf ver, msg;
+ int c;
+
+ req = NULL;
+ bufinit(ver);
+ bufinit(msg);
+ code = 0;
+ while(1) {
+ c = biogetc(in);
+ if(c == ' ') {
+ break;
+ } else if((c == EOF) || (c < 32) || (c >= 128)) {
+ goto fail;
+ } else {
+ bufadd(ver, c);
+ if(ver.d >= 128)
+ goto fail;
+ }
+ }
+ while(1) {
+ c = biogetc(in);
+ if(c == ' ') {
+ break;
+ } else if((c == EOF) || (c < '0') || (c > '9')) {
+ goto fail;
+ } else {
+ code = (code * 10) + (c - '0');
+ if(code >= 10000)
+ goto fail;
+ }
+ }
+ while(1) {
+ c = biogetc(in);
+ if(c == 10) {
+ break;
+ } else if(c == 13) {
+ } else if((c == EOF) || (c < 32)) {
+ goto fail;
+ } else {
+ bufadd(msg, c);
+ if(msg.d >= 512)
+ goto fail;
+ }
+ }
+ bufadd(msg, 0);
+ bufadd(ver, 0);
+ req = mkresp(code, msg.b, ver.b);
+ if(parseheadersb(req, in))
+ goto fail;
+ goto out;
+
+fail:
+ if(req != NULL) {
+ freehthead(req);
+ req = NULL;
+ }
+out:
+ buffree(msg);
+ buffree(ver);
+ return(req);
+}
+
void replrest(struct hthead *head, char *rest)
{
char *tmp;
return(0);
}
+int writerespb(struct bufio *out, struct hthead *resp)
+{
+ int i;
+
+ if(bioprintf(out, "%s %i %s\r\n", resp->ver, resp->code, resp->msg) < 0)
+ return(-1);
+ for(i = 0; i < resp->noheaders; i++) {
+ if(bioprintf(out, "%s: %s\r\n", resp->headers[i][0], resp->headers[i][1]) < 0)
+ return(-1);
+ }
+ return(0);
+}
+
int sendreq2(int sock, struct hthead *req, int fd, int flags)
{
int ret, i;
#include <stdio.h>
+struct bufio;
+
struct hthead {
char *method, *url, *ver, *msg;
int code;
int recvreq(int sock, struct hthead **reqp);
void replrest(struct hthead *head, char *rest);
int parseheaders(struct hthead *head, FILE *in);
+int parseheadersb(struct hthead *head, struct bufio *in);
struct hthead *parseresponse(FILE *in);
+struct hthead *parseresponseb(struct bufio *in);
int writeresp(FILE *out, struct hthead *resp);
+int writerespb(struct bufio *out, struct hthead *resp);
char *unquoteurl(char *in);
#endif
#!/usr/bin/python3
import sys, os, getopt, socket, logging, time, locale, collections, signal
-import ashd.util, ashd.serve
+import ashd.util, ashd.serve, ashd.htlib
try:
import pdm.srv
except:
def __init__(self, *, bkreq, **kw):
super().__init__(**kw)
self.bkreq = bkreq.dup()
+ self.sendrights = None
def mkenv(self):
return mkenv(self.bkreq)
return self.bkreq.bsk.fileno()
def writehead(self, status, headers):
+ headers = list(headers)
+ for header in headers:
+ nm, val = header
+ if nm.lower() == "x-ash-send-rights":
+ self.sendrights = val
+ headers.remove(header)
+ break
w = self.buffer.extend
w(b"HTTP/1.1 " + recode(status) + b"\n")
for nm, val in headers:
def flush(self):
try:
- ret = self.bkreq.bsk.send(self.buffer, socket.MSG_DONTWAIT)
+ if self.sendrights is not None:
+ ret = ashd.htlib.sendfd(self.bkreq.bsk.fileno(), self.sendrights.fileno(), self.buffer)
+ self.sendrights.close()
+ self.sendrights = None
+ else:
+ ret = self.bkreq.bsk.send(self.buffer, socket.MSG_DONTWAIT)
self.buffer[:ret] = b""
except IOError:
raise ashd.serve.closed()
def close(self):
- self.bkreq.close()
+ try:
+ self.bkreq.close()
+ finally:
+ if self.sendrights is not None:
+ self.sendrights.close()
def handle(req):
reqhandler.handle(request(bkreq=req, handler=reqhandler))
#include <proc.h>
#include <mt.h>
#include <mtio.h>
+#include <bufio.h>
#define DEFFORMAT "%{%Y-%m-%d %H:%M:%S}t %m %u %A \"%G\""
logreq(&data);
}
-static int passdata(FILE *in, FILE *out, off_t *passed)
+static int passdata(struct bufio *in, struct bufio *out, off_t *passed)
{
- size_t read;
+ ssize_t read;
off_t total;
- char buf[8192];
total = 0;
- while(!feof(in)) {
- read = fread(buf, 1, sizeof(buf), in);
- if(ferror(in))
- return(-1);
- if(fwrite(buf, 1, read, out) != read)
+ while(!bioeof(in)) {
+ if((read = biordata(in)) > 0) {
+ if((read = biowritesome(out, in->rbuf.b + in->rh, read)) < 0)
+ return(-1);
+ in->rh += read;
+ total += read;
+ }
+ if(biorspace(in) && (biofillsome(in) < 0))
return(-1);
- total += read;
}
if(passed)
*passed = total;
vavar(int, fd);
int pfds[2];
struct hthead *resp;
- FILE *cl, *hd;
+ struct bufio *cl, *hd;
+ struct stdiofd *cli, *hdi;
struct logdata data;
hd = NULL;
data = defdata;
data.req = req;
gettimeofday(&data.start, NULL);
- cl = mtstdopen(fd, 1, 600, "r+", NULL);
+ cl = mtbioopen(fd, 1, 600, "r+", &cli);
if(socketpair(PF_UNIX, SOCK_STREAM, 0, pfds))
goto out;
- hd = mtstdopen(pfds[1], 1, 600, "r+", NULL);
+ hd = mtbioopen(pfds[1], 1, 600, "r+", &hdi);
if(sendreq(ch, req, pfds[0])) {
close(pfds[0]);
goto out;
if(passdata(cl, hd, &data.bytesin))
goto out;
- if(fflush(hd))
+ if(bioflush(hd))
goto out;
shutdown(pfds[1], SHUT_WR);
- if((resp = parseresponse(hd)) == NULL)
+ if((resp = parseresponseb(hd)) == NULL)
goto out;
+ cli->sendrights = hdi->rights;
+ hdi->rights = -1;
data.resp = resp;
- writeresp(cl, resp);
- fprintf(cl, "\r\n");
+ writerespb(cl, resp);
+ bioprintf(cl, "\r\n");
if(passdata(hd, cl, &data.bytesout))
goto out;
gettimeofday(&data.end, NULL);
freehthead(req);
if(resp != NULL)
freehthead(resp);
- fclose(cl);
+ bioclose(cl);
if(hd != NULL)
- fclose(hd);
+ bioclose(hd);
}
static void sighandler(int sig)
#include <log.h>
#include <req.h>
#include <proc.h>
+#include <bufio.h>
#include "htparser.h"
}
}
-static struct hthead *parsereq(FILE *in)
+static struct hthead *parsereq(struct bufio *in)
{
struct hthead *req;
struct charbuf method, url, ver;
bufinit(url);
bufinit(ver);
while(1) {
- c = getc(in);
+ c = biogetc(in);
if(c == ' ') {
break;
} else if((c == EOF) || (c < 32) || (c >= 128)) {
}
}
while(1) {
- c = getc(in);
+ c = biogetc(in);
if(c == ' ') {
break;
} else if((c == EOF) || (c < 32)) {
}
}
while(1) {
- c = getc(in);
+ c = biogetc(in);
if(c == 10) {
break;
} else if(c == 13) {
bufadd(url, 0);
bufadd(ver, 0);
req = mkreq(method.b, url.b, ver.b);
- if(parseheaders(req, in))
+ if(parseheadersb(req, in))
goto fail;
trimx(req);
goto out;
return(req);
}
-static off_t passdata(FILE *in, FILE *out, off_t max)
+static off_t passdata(struct bufio *in, struct bufio *out, off_t max)
{
- size_t read;
+ ssize_t read;
off_t total;
- char buf[8192];
total = 0;
- while(!feof(in) && ((max < 0) || (total < max))) {
- read = sizeof(buf);
- if(max >= 0)
- read = min(max - total, read);
- read = fread(buf, 1, read, in);
- if(ferror(in))
- return(-1);
- if(fwrite(buf, 1, read, out) != read)
+ while(!bioeof(in) && ((max < 0) || (total < max))) {
+ if((read = biordata(in)) > 0) {
+ if(max >= 0)
+ read = min(max - total, read);
+ if((read = biowritesome(out, in->rbuf.b + in->rh, read)) < 0)
+ return(-1);
+ in->rh += read;
+ total += read;
+ }
+ if(biorspace(in) && ((max < 0) || (biordata(in) < max - total)) && (biofillsome(in) < 0))
return(-1);
- total += read;
}
return(total);
}
-static int recvchunks(FILE *in, FILE *out)
+static int recvchunks(struct bufio *in, struct bufio *out)
{
- char buf[8192];
- size_t read, chlen;
+ ssize_t read, chlen;
int c, r;
while(1) {
chlen = 0;
r = 0;
while(1) {
- c = getc(in);
+ c = biogetc(in);
if(c == 10) {
if(!r)
return(-1);
if(chlen == 0)
break;
while(chlen > 0) {
- read = fread(buf, 1, min(sizeof(buf), chlen), in);
- if(feof(in) || ferror(in))
- return(-1);
- if(fwrite(buf, 1, read, out) != read)
+ if((read = biordata(in)) > 0) {
+ if((read = biowritesome(out, in->rbuf.b + in->rh, min(read, chlen))) < 0)
+ return(-1);
+ in->rh += read;
+ chlen -= read;
+ }
+ if(biorspace(in) && (biordata(in) < chlen) && (biofillsome(in) <= 0))
return(-1);
- chlen -= read;
}
- if((getc(in) != 13) || (getc(in) != 10))
+ if((biogetc(in) != 13) || (biogetc(in) != 10))
return(-1);
}
/* XXX: Technically, there may be trailers to be read, but that's
* just about as likely as chunk extensions. */
- if((getc(in) != 13) || (getc(in) != 10))
+ if((biogetc(in) != 13) || (biogetc(in) != 10))
return(-1);
return(0);
}
-static int passchunks(FILE *in, FILE *out)
+static int passchunks(struct bufio *in, struct bufio *out)
{
- char buf[8192];
size_t read;
- do {
- read = fread(buf, 1, sizeof(buf), in);
- if(ferror(in))
- return(-1);
- fprintf(out, "%zx\r\n", read);
- if(fwrite(buf, 1, read, out) != read)
+ while(!bioeof(in)) {
+ if((read = biordata(in)) > 0) {
+ bioprintf(out, "%zx\r\n", read);
+ if(biowrite(out, in->rbuf.b + in->rh, read) != read)
+ return(-1);
+ in->rh += read;
+ bioprintf(out, "\r\n");
+ if(bioflush(out) < 0)
+ return(-1);
+ }
+ if(biorspace(in) && (biofillsome(in) < 0))
return(-1);
- fprintf(out, "\r\n");
- } while(read > 0);
+ }
+ bioprintf(out, "0\r\n\r\n");
return(0);
}
return(ret);
}
-void serve(FILE *in, struct conn *conn)
+static void passduplex(struct bufio *a, int afd, struct bufio *b, int bfd)
+{
+ struct selected pfd[4], sel;
+ struct bufio *sio;
+ int n, ev;
+
+ while(!bioeof(a) && !bioeof(b)) {
+ biocopybuf(b, a);
+ biocopybuf(a, b);
+ n = 0;
+ if(!a->eof) {
+ ev = 0;
+ if(biorspace(a))
+ ev |= EV_READ;
+ if(biowdata(a))
+ ev |= EV_WRITE;
+ if(ev)
+ pfd[n++] = (struct selected){.fd = afd, .ev = ev};
+ }
+ if(!b->eof) {
+ ev = 0;
+ if(!b->eof && biorspace(b))
+ ev |= EV_READ;
+ if(biowdata(b))
+ ev |= EV_WRITE;
+ if(ev)
+ pfd[n++] = (struct selected){.fd = bfd, .ev = ev};
+ }
+ sel = mblock(600, n, pfd);
+ if(sel.fd == afd)
+ sio = a;
+ else if(sel.fd == bfd)
+ sio = b;
+ else
+ break;
+ if((sel.ev & EV_READ) && (biofillsome(sio) < 0))
+ break;
+ if((sel.ev & EV_WRITE) && (bioflushsome(sio) < 0))
+ break;
+ }
+}
+
+void serve(struct bufio *in, int infd, struct conn *conn)
{
int pfds[2];
- FILE *out;
+ struct bufio *out, *dout;
+ struct stdiofd *outi;
struct hthead *req, *resp;
char *hd, *id;
off_t dlen;
- int keep;
+ int keep, duplex;
id = connid();
out = NULL;
req = resp = NULL;
while(plex >= 0) {
+ bioflush(in);
if((req = parsereq(in)) == NULL)
break;
if(!canonreq(req))
if(sendreq(plex, req, pfds[0]))
break;
close(pfds[0]);
- out = mtstdopen(pfds[1], 1, 600, "r+", NULL);
+ out = mtbioopen(pfds[1], 1, 600, "r+", &outi);
if(getheader(req, "content-type") != NULL) {
if((hd = getheader(req, "content-length")) != NULL) {
headrmheader(req, "content-type");
}
}
- if(fflush(out))
+ if(bioflush(out))
break;
/* Make sure to send EOF */
shutdown(pfds[1], SHUT_WR);
- if((resp = parseresponse(out)) == NULL)
+ if((resp = parseresponseb(out)) == NULL)
break;
replstr(&resp->ver, req->ver);
if(!getheader(resp, "server"))
headappheader(resp, "Server", sprintf3("ashd/%s", VERSION));
+ duplex = hasheader(resp, "x-ash-switch", "duplex");
+ trimx(resp);
- if(!strcasecmp(req->ver, "HTTP/1.0")) {
+ if(duplex) {
+ if(outi->rights < 0)
+ break;
+ writerespb(in, resp);
+ bioprintf(in, "\r\n");
+ dout = mtbioopen(outi->rights, 1, 600, "r+", NULL);
+ passduplex(in, infd, dout, outi->rights);
+ outi->rights = -1;
+ bioclose(dout);
+ break;
+ } else if(!strcasecmp(req->ver, "HTTP/1.0")) {
if(!strcasecmp(req->method, "head")) {
keep = http10keep(req, resp);
- writeresp(in, resp);
- fprintf(in, "\r\n");
+ writerespb(in, resp);
+ bioprintf(in, "\r\n");
} else if((hd = getheader(resp, "content-length")) != NULL) {
keep = http10keep(req, resp);
dlen = atoo(hd);
- writeresp(in, resp);
- fprintf(in, "\r\n");
+ writerespb(in, resp);
+ bioprintf(in, "\r\n");
if(passdata(out, in, dlen) != dlen)
break;
} else {
headrmheader(resp, "connection");
- writeresp(in, resp);
- fprintf(in, "\r\n");
+ writerespb(in, resp);
+ bioprintf(in, "\r\n");
passdata(out, in, -1);
break;
}
break;
} else if(!strcasecmp(req->ver, "HTTP/1.1")) {
if(!strcasecmp(req->method, "head")) {
- writeresp(in, resp);
- fprintf(in, "\r\n");
+ writerespb(in, resp);
+ bioprintf(in, "\r\n");
} else if((hd = getheader(resp, "content-length")) != NULL) {
- writeresp(in, resp);
- fprintf(in, "\r\n");
+ writerespb(in, resp);
+ bioprintf(in, "\r\n");
dlen = atoo(hd);
if(passdata(out, in, dlen) != dlen)
break;
} else if(!getheader(resp, "transfer-encoding")) {
headappheader(resp, "Transfer-Encoding", "chunked");
- writeresp(in, resp);
- fprintf(in, "\r\n");
+ writerespb(in, resp);
+ bioprintf(in, "\r\n");
if(passchunks(out, in))
break;
} else {
- writeresp(in, resp);
- fprintf(in, "\r\n");
+ writerespb(in, resp);
+ bioprintf(in, "\r\n");
passdata(out, in, -1);
break;
}
break;
}
- fclose(out);
+ bioclose(out);
out = NULL;
freehthead(req);
freehthead(resp);
}
if(out != NULL)
- fclose(out);
+ bioclose(out);
if(req != NULL)
freehthead(req);
if(resp != NULL)
freehthead(resp);
- fclose(in);
+ bioclose(in);
free(id);
}
size_t s, d;
};
-void serve(FILE *in, struct conn *conn);
+void serve(struct bufio *in, int infd, struct conn *conn);
int listensock4(int port);
int listensock6(int port);
vavar(int, fd);
vavar(struct sockaddr_storage, name);
vavar(struct tcpport *, stcp);
- FILE *in;
+ struct bufio *in;
struct conn conn;
struct tcpconn tcp;
memset(&conn, 0, sizeof(conn));
memset(&tcp, 0, sizeof(tcp));
- in = mtstdopen(fd, 1, 60, "r+", NULL);
+ in = mtbioopen(fd, 1, 60, "r+", NULL);
conn.pdata = &tcp;
conn.initreq = initreq;
tcp.fd = fd;
tcp.name = name;
tcp.port = stcp;
- serve(in, &conn);
+ serve(in, fd, &conn);
}
static void listenloop(struct muth *muth, va_list args)
#include <mtio.h>
#include <req.h>
#include <log.h>
+#include <bufio.h>
#include "htparser.h"
return(block(fd, EV_READ, to));
}
-static ssize_t sslread(void *cookie, char *buf, size_t len)
+static ssize_t sslread(void *cookie, void *buf, size_t len)
{
struct sslconn *ssl = cookie;
ssize_t xf;
return(xf);
}
-static ssize_t sslwrite(void *cookie, const char *buf, size_t len)
+static ssize_t sslwrite(void *cookie, const void *buf, size_t len)
{
struct sslconn *ssl = cookie;
int ret;
return(0);
}
-static cookie_io_functions_t iofuns = {
+static struct bufioops iofuns = {
.read = sslread,
.write = sslwrite,
.close = sslclose,
struct sslconn ssl;
gnutls_session_t sess;
int ret;
- FILE *in;
int setcreds(gnutls_session_t sess)
{
ssl.name = name;
ssl.sess = sess;
bufinit(ssl.in);
- in = fopencookie(&ssl, "r+", iofuns);
- serve(in, &conn);
+ serve(bioopen(&ssl, &iofuns), fd, &conn);
out:
gnutls_deinit(sess);