From: Fredrik Tolf Date: Wed, 18 May 2016 03:58:50 +0000 (+0200) Subject: Merge branch 'master' into duplex X-Git-Url: http://www.dolda2000.com/gitweb/?p=ashd.git;a=commitdiff_plain;h=6d1caa155319702b5a36af8e5e2b94bf9f3b39f8;hp=bdbb6d12f5f73262685f871685cfd32bba9bdeb4 Merge branch 'master' into duplex --- diff --git a/lib/Makefile.am b/lib/Makefile.am index 30de42a..0f13d2b 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,6 +1,7 @@ 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 @@ -12,4 +13,5 @@ libht_a_SOURCES += mtio-select.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 diff --git a/lib/bufio.c b/lib/bufio.c new file mode 100644 index 0000000..c08e9dd --- /dev/null +++ b/lib/bufio.c @@ -0,0 +1,329 @@ +/* + ashd - A Sane HTTP Daemon + Copyright (C) 2008 Fredrik Tolf + + 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 . +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include + +#include +#include + +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); +} diff --git a/lib/bufio.h b/lib/bufio.h new file mode 100644 index 0000000..5874577 --- /dev/null +++ b/lib/bufio.h @@ -0,0 +1,41 @@ +#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 diff --git a/lib/mtio-epoll.c b/lib/mtio-epoll.c index 72468e0..af7cd27 100644 --- a/lib/mtio-epoll.c +++ b/lib/mtio-epoll.c @@ -37,7 +37,7 @@ static struct blocker *blockers; struct blocker { struct blocker *n, *p, *n2, *p2; int fd, reg; - int ev; + int ev, rev, id; time_t to; struct muth *th; }; @@ -129,34 +129,72 @@ static void remfd(struct blocker *bl) 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); } @@ -211,15 +249,27 @@ int ioloop(void) 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) diff --git a/lib/mtio-kqueue.c b/lib/mtio-kqueue.c index 1235973..e317153 100644 --- a/lib/mtio-kqueue.c +++ b/lib/mtio-kqueue.c @@ -37,7 +37,7 @@ static struct blocker *blockers; struct blocker { struct blocker *n, *p, *n2, *p2; int fd, reg; - int ev; + int ev, rev, id; time_t to; struct muth *th; }; @@ -137,26 +137,19 @@ static void remfd(struct blocker *bl) 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) @@ -164,7 +157,51 @@ int block(int fd, int ev, time_t to) 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); } @@ -214,15 +251,27 @@ int ioloop(void) 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) diff --git a/lib/mtio-select.c b/lib/mtio-select.c index 26e6785..6e9a767 100644 --- a/lib/mtio-select.c +++ b/lib/mtio-select.c @@ -37,39 +37,73 @@ static int exitstatus; 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); } @@ -125,10 +159,21 @@ int ioloop(void) 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); + } + } } } } diff --git a/lib/mtio.c b/lib/mtio.c index d7f36ac..b911f72 100644 --- a/lib/mtio.c +++ b/lib/mtio.c @@ -31,6 +31,41 @@ #include #include #include +#include + +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) { @@ -39,7 +74,10 @@ 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) { @@ -57,6 +95,39 @@ static ssize_t mtread(void *cookie, void *buf, size_t len) } } +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; @@ -65,7 +136,7 @@ static ssize_t mtwrite(void *cookie, const void *buf, size_t len) 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)) { @@ -88,6 +159,10 @@ static int mtclose(void *cookie) 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); } @@ -111,6 +186,7 @@ FILE *mtstdopen(int fd, int issock, int timeout, char *mode, struct stdiofd **in 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); @@ -121,6 +197,35 @@ FILE *mtstdopen(int fd, int issock, int timeout, char *mode, struct stdiofd **in 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; diff --git a/lib/mtio.h b/lib/mtio.h index 75928eb..ffb57da 100644 --- a/lib/mtio.h +++ b/lib/mtio.h @@ -10,12 +10,19 @@ struct stdiofd { 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 diff --git a/lib/req.c b/lib/req.c index ed5aeba..074e7df 100644 --- a/lib/req.c +++ b/lib/req.c @@ -32,6 +32,7 @@ #include #include #include +#include struct hthead *mkreq(char *method, char *url, char *ver) { @@ -164,6 +165,68 @@ fail: 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; @@ -230,6 +293,72 @@ out: 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; @@ -290,6 +419,19 @@ int writeresp(FILE *out, struct hthead *resp) 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; diff --git a/lib/req.h b/lib/req.h index 054d3c1..9ae60ab 100644 --- a/lib/req.h +++ b/lib/req.h @@ -3,6 +3,8 @@ #include +struct bufio; + struct hthead { char *method, *url, *ver, *msg; int code; @@ -23,8 +25,11 @@ int sendreq(int sock, struct hthead *req, int fd); 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 diff --git a/python3/ashd-wsgi3 b/python3/ashd-wsgi3 index a63362d..fa0b8b5 100755 --- a/python3/ashd-wsgi3 +++ b/python3/ashd-wsgi3 @@ -1,7 +1,7 @@ #!/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: @@ -157,6 +157,7 @@ class request(ashd.serve.wsgirequest): def __init__(self, *, bkreq, **kw): super().__init__(**kw) self.bkreq = bkreq.dup() + self.sendrights = None def mkenv(self): return mkenv(self.bkreq) @@ -168,6 +169,13 @@ class request(ashd.serve.wsgirequest): 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: @@ -176,13 +184,22 @@ class request(ashd.serve.wsgirequest): 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)) diff --git a/src/accesslog.c b/src/accesslog.c index a3894bf..af43373 100644 --- a/src/accesslog.c +++ b/src/accesslog.c @@ -39,6 +39,7 @@ #include #include #include +#include #define DEFFORMAT "%{%Y-%m-%d %H:%M:%S}t %m %u %A \"%G\"" @@ -215,20 +216,21 @@ static void serve(struct hthead *req, int fd) 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; @@ -241,7 +243,8 @@ static void filterreq(struct muth *mt, va_list args) 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; @@ -249,10 +252,10 @@ static void filterreq(struct muth *mt, va_list args) 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; @@ -261,14 +264,16 @@ static void filterreq(struct muth *mt, va_list args) 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); @@ -279,9 +284,9 @@ out: freehthead(req); if(resp != NULL) freehthead(resp); - fclose(cl); + bioclose(cl); if(hd != NULL) - fclose(hd); + bioclose(hd); } static void sighandler(int sig) diff --git a/src/htparser.c b/src/htparser.c index 92445d7..a738e25 100644 --- a/src/htparser.c +++ b/src/htparser.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "htparser.h" @@ -60,7 +61,7 @@ static void trimx(struct hthead *req) } } -static struct hthead *parsereq(FILE *in) +static struct hthead *parsereq(struct bufio *in) { struct hthead *req; struct charbuf method, url, ver; @@ -71,7 +72,7 @@ static struct hthead *parsereq(FILE *in) bufinit(url); bufinit(ver); while(1) { - c = getc(in); + c = biogetc(in); if(c == ' ') { break; } else if((c == EOF) || (c < 32) || (c >= 128)) { @@ -83,7 +84,7 @@ static struct hthead *parsereq(FILE *in) } } while(1) { - c = getc(in); + c = biogetc(in); if(c == ' ') { break; } else if((c == EOF) || (c < 32)) { @@ -95,7 +96,7 @@ static struct hthead *parsereq(FILE *in) } } while(1) { - c = getc(in); + c = biogetc(in); if(c == 10) { break; } else if(c == 13) { @@ -111,7 +112,7 @@ static struct hthead *parsereq(FILE *in) 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; @@ -128,38 +129,37 @@ 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); @@ -185,37 +185,43 @@ static int recvchunks(FILE *in, FILE *out) 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); } @@ -299,19 +305,63 @@ done: 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)) @@ -328,7 +378,7 @@ void serve(FILE *in, struct conn *conn) 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) { @@ -345,34 +395,46 @@ void serve(FILE *in, struct conn *conn) 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; } @@ -380,23 +442,23 @@ void serve(FILE *in, struct conn *conn) 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; } @@ -406,7 +468,7 @@ void serve(FILE *in, struct conn *conn) break; } - fclose(out); + bioclose(out); out = NULL; freehthead(req); freehthead(resp); @@ -414,12 +476,12 @@ void serve(FILE *in, struct conn *conn) } if(out != NULL) - fclose(out); + bioclose(out); if(req != NULL) freehthead(req); if(resp != NULL) freehthead(resp); - fclose(in); + bioclose(in); free(id); } diff --git a/src/htparser.h b/src/htparser.h index 7bd60a0..d9f014d 100644 --- a/src/htparser.h +++ b/src/htparser.h @@ -11,7 +11,7 @@ struct mtbuf { 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); diff --git a/src/plaintcp.c b/src/plaintcp.c index 1d3e2e7..56fc81c 100644 --- a/src/plaintcp.c +++ b/src/plaintcp.c @@ -150,19 +150,19 @@ void servetcp(struct muth *muth, va_list args) 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) diff --git a/src/ssl-gnutls.c b/src/ssl-gnutls.c index a181add..7aa1df0 100644 --- a/src/ssl-gnutls.c +++ b/src/ssl-gnutls.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "htparser.h" @@ -188,7 +189,7 @@ static int tlsblock(int fd, gnutls_session_t sess, time_t to) 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; @@ -217,7 +218,7 @@ static ssize_t sslread(void *cookie, char *buf, size_t len) 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; @@ -255,7 +256,7 @@ static int sslclose(void *cookie) return(0); } -static cookie_io_functions_t iofuns = { +static struct bufioops iofuns = { .read = sslread, .write = sslwrite, .close = sslclose, @@ -289,7 +290,6 @@ static void servessl(struct muth *muth, va_list args) struct sslconn ssl; gnutls_session_t sess; int ret; - FILE *in; int setcreds(gnutls_session_t sess) { @@ -344,8 +344,7 @@ static void servessl(struct muth *muth, va_list args) 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);