aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2025-03-22 05:53:08 +0000
committerLaurent Bercot <ska@appnovation.com>2025-03-22 05:53:08 +0000
commit9cd4e0d1902ebb196ebd53ed7ee88e6689d801b7 (patch)
treeab44d569dce124541085629f6dbe0cc27b1da0fa /src
parent53fdbac9da06a0dfb3f2821f7c7384001eea4f68 (diff)
downloadtipidee-9cd4e0d1902ebb196ebd53ed7ee88e6689d801b7.tar.gz
Add cgi streaming
WILDLY UNTESTED, don't use this commit, probably. Also, autochunk hasn't been implemented yet. Signed-off-by: Laurent Bercot <ska@appnovation.com>
Diffstat (limited to 'src')
-rw-r--r--src/config/headers.c1
-rw-r--r--src/config/lexparse.c10
-rw-r--r--src/include/tipidee/resattr.h5
-rw-r--r--src/libtipidee/tipidee_conf_get_resattr.c2
-rw-r--r--src/tipideed/cgi.c266
-rw-r--r--src/tipideed/deps-exe/tipideed1
-rw-r--r--src/tipideed/stream.c133
-rw-r--r--src/tipideed/tipideed-internal.h7
8 files changed, 297 insertions, 128 deletions
diff --git a/src/config/headers.c b/src/config/headers.c
index efeb712..2a34bba 100644
--- a/src/config/headers.c
+++ b/src/config/headers.c
@@ -30,6 +30,7 @@ static struct builtinheaders_s const builtinheaders[] =
{ .key = "Date", .value = 0, .overridable = 0 },
{ .key = "Server", .value = "tipidee/" TIPIDEE_VERSION, .overridable = 1 },
{ .key = "Status", .value = 0, .overridable = 0 },
+ { .key = "Transfer-Encoding", .value = 0, .overridable = 0 },
{ .key = "Vary", .value = "Accept-Encoding", .overridable = 0 },
{ .key = "X-Content-Type-Options", .value = "nosniff", .overridable = 1 },
{ .key = "X-Frame-Options", .value = "DENY", .overridable = 1 }
diff --git a/src/config/lexparse.c b/src/config/lexparse.c
index a799a6b..160aa97 100644
--- a/src/config/lexparse.c
+++ b/src/config/lexparse.c
@@ -50,6 +50,8 @@ enum directivevalue_e
T_NONNPH,
T_BASICAUTH,
T_NOAUTH,
+ T_AUTOCHUNK,
+ T_NOAUTOCHUNK,
T_FILETYPE,
T_CUSTOMRESPONSE
} ;
@@ -408,6 +410,7 @@ static inline void process_line (char const *s, size_t const *word, size_t n, st
static struct namevalue_s const directives[] =
{
{ .name = "!", .value = T_BANG },
+ { .name = "autochunk", .value = T_AUTOCHUNK },
{ .name = "basic-auth", .value = T_BASICAUTH },
{ .name = "cgi", .value = T_CGI },
{ .name = "content-type", .value = T_CONTENTTYPE },
@@ -419,6 +422,7 @@ static inline void process_line (char const *s, size_t const *word, size_t n, st
{ .name = "index-file", .value = T_INDEXFILE },
{ .name = "log", .value = T_LOG },
{ .name = "no-auth", .value = T_NOAUTH },
+ { .name = "noautochunk", .value = T_NOAUTOCHUNK },
{ .name = "noncgi", .value = T_NONCGI },
{ .name = "nonnph", .value = T_NONNPH },
{ .name = "nph", .value = T_NPH },
@@ -508,6 +512,12 @@ static inline void process_line (char const *s, size_t const *word, size_t n, st
strerr_warnw5x("file ", g.storage.s + md->filepos, " line ", md->linefmt, ": directive basic-auth not implemented in tipidee-" TIPIDEE_VERSION) ;
parse_bitattr(s, word, n, domain->s, domain->len, md, 2, 0) ;
break ;
+ case T_AUTOCHUNK :
+ parse_bitattr(s, word, n, domain->s, domain->len, md, 3, 1) ;
+ break ;
+ case T_NOAUTOCHUNK :
+ parse_bitattr(s, word, n, domain->s, domain->len, md, 3, 0) ;
+ break ;
case T_FILETYPE :
parse_filetype(s, word, n, domain->s, domain->len, md) ;
break ;
diff --git a/src/include/tipidee/resattr.h b/src/include/tipidee/resattr.h
index edd0a41..da4fbc7 100644
--- a/src/include/tipidee/resattr.h
+++ b/src/include/tipidee/resattr.h
@@ -5,11 +5,14 @@
#include <stdint.h>
+ /* These values must match the bitattrs in lexparse.c */
+
#define TIPIDEE_RA_FLAG_CGI 0x0001
#define TIPIDEE_RA_FLAG_NPH 0x0002
#define TIPIDEE_RA_FLAG_BA 0x0004
+#define TIPIDEE_RA_FLAG_AUTOCHUNK 0x0008
-#define TIPIDEE_RA_BITS 3
+#define TIPIDEE_RA_BITS 4
typedef struct tipidee_resattr_s tipidee_resattr, *tipidee_resattr_ref ;
struct tipidee_resattr_s
diff --git a/src/libtipidee/tipidee_conf_get_resattr.c b/src/libtipidee/tipidee_conf_get_resattr.c
index c5fc91a..b5f18b7 100644
--- a/src/libtipidee/tipidee_conf_get_resattr.c
+++ b/src/libtipidee/tipidee_conf_get_resattr.c
@@ -28,7 +28,7 @@ int tipidee_conf_get_resattr (tipidee_conf const *conf, char const *res, tipidee
if (r == -1) return 0 ;
if (r)
{
- rra.flags = (~rra.mask & atom.mask & atom.flags) | ((rra.mask | ~atom.mask) & rra.flags) ; /* yup */
+ rra.flags = (~rra.mask & atom.mask & atom.flags) | ((rra.mask | ~atom.mask) & rra.flags) ; /* it's obvious, right */
rra.mask |= atom.mask ;
if (!rra.content_type) rra.content_type = atom.content_type ;
}
diff --git a/src/tipideed/cgi.c b/src/tipideed/cgi.c
index 648f257..fbf08c7 100644
--- a/src/tipideed/cgi.c
+++ b/src/tipideed/cgi.c
@@ -20,6 +20,7 @@
#include <skalibs/iopause.h>
#include <skalibs/env.h>
#include <skalibs/exec.h>
+#include <skalibs/siovec.h>
#include <skalibs/unix-timed.h>
#include <tipidee/tipidee.h>
@@ -131,8 +132,6 @@ static inline int do_nph (tipidee_rql const *rql, char const *docroot, char cons
{
#define NAME "tipideed (nph helper for pid "
tain deadline ;
- char buf[4096] ;
- buffer b = BUFFER_INIT(&buffer_write, p[1], buf, 4096) ;
size_t m = sizeof(NAME) - 1 ;
char progstr[sizeof(NAME) + PID_FMT] ;
memcpy(progstr, NAME, m) ;
@@ -142,8 +141,7 @@ static inline int do_nph (tipidee_rql const *rql, char const *docroot, char cons
tain_add_g(&deadline, &g.cgitto) ;
close(p[0]) ;
if (ndelay_on(p[1]) == -1) die500sys(rql, 111, docroot, "set fd nonblocking") ;
- if (buffer_timed_put_g(&b, body, bodylen, &deadline) < bodylen
- || !buffer_timed_flush_g(&b, &deadline))
+ if (timed_write_g(p[1], body, bodylen, &deadline) < bodylen)
die500sys(rql, 111, docroot, "write request body to nph ", argv[0]) ;
_exit(0) ;
}
@@ -156,17 +154,46 @@ static inline int do_nph (tipidee_rql const *rql, char const *docroot, char cons
die500sys(rql, errno == ENOENT ? 127 : 126, docroot, "exec nph ", argv[0]) ;
}
-static inline int run_cgi (tipidee_rql const *rql, char const *docroot, char const *const *argv, char const *const *envp, char const *body, size_t bodylen, tipidee_headers *hdr, stralloc *sa)
+static inline int local_redirect (tipidee_rql *rql, char const *docroot, char const *loc, char *uribuf, char const *cginame)
+{
+ size_t n ;
+ size_t hostlen = strlen(rql->uri.host) ;
+ uint16_t port = rql->uri.port ;
+ uint8_t ishttps = rql->uri.https ;
+ char hosttmp[hostlen + 1] ;
+ memcpy(hosttmp, rql->uri.host, hostlen + 1) ;
+ n = tipidee_uri_parse(uribuf, URI_BUFSIZE, loc, &rql->uri) ;
+ if (!n || n + hostlen + 1 > URI_BUFSIZE)
+ die502x(rql, 2, docroot, "cgi ", cginame, " returned an invalid ", "Location", " value", " for local redirection") ;
+ memcpy(uribuf + n, hosttmp, hostlen + 1) ;
+ rql->uri.host = uribuf + n ;
+ rql->uri.port = port ;
+ rql->uri.https = ishttps ;
+ tipidee_log_debug(g.logv, "cgi ", cginame, ": local redirect to ", rql->uri.path) ;
+ return 1 ;
+}
+
+static inline int do_cgi (tipidee_rql *rql, char const *docroot, char const *const *argv, char const *const *envp, char const *body, size_t bodylen, char *uribuf, int autochunk)
{
+ char const *reason = "OK" ;
+ char const *location ;
+ char const *contentlength ;
+ char const *statusfield ;
+ unsigned int status = 0 ;
+ size_t rbodylen = 0 ;
+ int r ;
+ tipidee_headers hdr ;
+ char hdrbuf[4096] ;
+ char buf[4096] ;
iopause_fd x[2] = { { .events = IOPAUSE_READ }, { .events = IOPAUSE_WRITE } } ;
size_t bodyw = 0 ;
- unsigned int rstate = 0 ;
tain deadline ;
pid_t pid ;
disize curheader = DISIZE_ZERO ;
uint32_t parserstate = 0 ;
buffer b ;
- char buf[4096] ;
+
+ tipidee_headers_init(&hdr, hdrbuf, 4096) ;
{
int fd[2] = { 0, 1 } ;
pid = child_spawn2(argv[0], argv, envp, fd) ;
@@ -180,18 +207,13 @@ static inline int run_cgi (tipidee_rql const *rql, char const *docroot, char con
}
buffer_init(&b, &buffer_read, x[0].fd, buf, 4096) ;
tain_add_g(&deadline, &g.cgitto) ;
- while (x[0].fd >= 0)
+
+ for (;;)
{
- int r = iopause_g(x, 1 + (x[1].fd >= 0), &deadline) ;
+ r = iopause_g(x, 1 + (x[1].fd >= 0), &deadline) ;
if (r == -1) die500sys(rql, 111, docroot, "iopause") ;
- if (!r)
- {
- kill(pid, SIGTERM) ;
- strerr_warnw3x("cgi ", argv[0], " timed out") ;
- respond_504(rql, docroot) ;
- break ;
- }
- if (x[1].fd >= 0 && x[1].revents & (IOPAUSE_WRITE | IOPAUSE_EXCEPT))
+ if (!r) goto cgitimeout ;
+ if (x[1].fd >= 0 && x[1].revents & IOPAUSE_WRITE)
{
size_t len = allwrite(x[1].fd, body + bodyw, bodylen - bodyw) ;
if (!len)
@@ -206,115 +228,90 @@ static inline int run_cgi (tipidee_rql const *rql, char const *docroot, char con
x[1].fd = -1 ;
}
}
- if (x[0].fd >= 0 && x[0].revents & (IOPAUSE_READ | IOPAUSE_EXCEPT))
+ if (x[0].revents & IOPAUSE_READ)
{
- switch (rstate)
+ switch (tipidee_headers_parse_nb(&b, &hdr, &curheader, &parserstate))
{
- case 0 :
- {
- r = tipidee_headers_parse_nb(&b, hdr, &curheader, &parserstate) ;
- switch (r)
- {
- case -2 : break ;
- case -1 : die500sys(rql, 111, docroot, "read from cgi ", argv[0]) ;
- case 0 :
- {
- size_t n = buffer_len(&b) ;
- if (!stralloc_readyplus(sa, n)) die500sys(rql, 111, docroot, "stralloc_readyplus") ;
- buffer_getnofill(&b, sa->s + sa->len, n) ;
- sa->len += n ;
- rstate = 1 ;
- break ;
- }
- case 400 : die502x(rql, 2, docroot, "invalid output", " from cgi ", argv[0]) ;
- case 413 : die502x(rql, 2, docroot, hdr->n >= TIPIDEE_HEADERS_MAX ? "Too many headers" : "Too much header data", " from cgi ", argv[0]) ;
- case 500 : die500x(rql, 101, docroot, "can't happen: ", "avltreen_insert failed", " in do_cgi") ;
- default : die500x(rql, 101, docroot, "can't happen: ", "unknown tipidee_headers_parse return code", " in do_cgi") ;
- }
- if (!rstate) break ;
- }
- case 1 :
- {
- if (!slurpn(x[0].fd, sa, g.maxcgibody))
- {
- if (error_isagain(errno)) break ;
- else if (errno == ENOBUFS) die502x(rql, 2, docroot, "Too fat body", " from cgi ", argv[0]) ;
- else die500sys(rql, 111, docroot, "read body", " from cgi ", argv[0]) ;
- }
- close(x[0].fd) ;
- x[0].fd = -1 ;
- rstate = 2 ;
- }
+ case -2 : break ;
+ case -1 : die500sys(rql, 111, docroot, "read from cgi ", argv[0]) ;
+ case 0 : goto normalcase ;
+ case 400 : die502x(rql, 2, docroot, "invalid output", " from cgi ", argv[0]) ;
+ case 413 : die502x(rql, 2, docroot, hdr.n >= TIPIDEE_HEADERS_MAX ? "Too many headers" : "Too much header data", " from cgi ", argv[0]) ;
+ case 500 : die500x(rql, 101, docroot, "can't happen: ", "avltreen_insert failed", " in do_cgi") ;
+ default : die500x(rql, 101, docroot, "can't happen: ", "unknown tipidee_headers_parse return code", " in do_cgi") ;
}
}
}
+
+ cgitimeout:
+ kill(pid, SIGTERM) ;
+ strerr_warnw3x("cgi ", argv[0], " timed out") ;
+ respond_504(rql, docroot) ;
if (x[1].fd >= 0) close(x[1].fd) ;
- if (x[0].fd >= 0) close(x[0].fd) ;
- return rstate == 2 ;
-}
+ close(x[0].fd) ;
+ return 0 ;
-static inline int local_redirect (tipidee_rql *rql, char const *docroot, char const *loc, char *uribuf, char const *cginame)
-{
- size_t n ;
- size_t hostlen = strlen(rql->uri.host) ;
- uint16_t port = rql->uri.port ;
- uint8_t ishttps = rql->uri.https ;
- char hosttmp[hostlen + 1] ;
- memcpy(hosttmp, rql->uri.host, hostlen + 1) ;
- n = tipidee_uri_parse(uribuf, URI_BUFSIZE, loc, &rql->uri) ;
- if (!n || n + hostlen + 1 > URI_BUFSIZE)
- die502x(rql, 2, docroot, "cgi ", cginame, " returned an invalid ", "Location", " value", " for local redirection") ;
- memcpy(uribuf + n, hosttmp, hostlen + 1) ;
- rql->uri.host = uribuf + n ;
- rql->uri.port = port ;
- rql->uri.https = ishttps ;
- tipidee_log_debug(g.logv, "cgi ", cginame, ": local redirect to ", rql->uri.path) ;
- return 1 ;
-}
+ normalcase:
-static inline int process_cgi_output (tipidee_rql *rql, char const *docroot, tipidee_headers const *hdr, char const *rbody, size_t rbodylen, char *uribuf, char const *cginame)
-{
- char const *location = tipidee_headers_search(hdr, "Location") ;
- char const *x = tipidee_headers_search(hdr, "Status") ;
- char const *reason = "OK" ;
- unsigned int status = 0 ;
- tain deadline ;
+ if (x[1].fd >= 0)
+ {
+ if (timed_write_g(x[1].fd, body + bodyw, bodylen - bodyw, &deadline) < bodylen - bodyw)
+ {
+ if (errno == ETIMEDOUT) goto cgitimeout ;
+ else strerr_warnwu2sys("write request body to cgi ", argv[0]) ;
+ }
+ close(x[1].fd) ;
+ x[1].fd = -1 ;
+ }
tain_add_g(&deadline, &g.writetto) ;
- if (x)
+
+ statusfield = tipidee_headers_search(&hdr, "Status") ;
+ location = tipidee_headers_search(&hdr, "Location") ;
+ contentlength = tipidee_headers_search(&hdr, "Content-Length") ;
+ if (contentlength)
+ {
+ if (!size0_scan(contentlength, &rbodylen))
+ die502x(rql, 2, docroot, "cgi ", argv[0], " returned an invalid ", "Content-Length", " header") ;
+ }
+ if (statusfield)
{
- size_t m = uint_scan(x, &status) ;
- if (!m || (x[m] && x[m] != ' '))
- die502x(rql, 2, docroot, "cgi ", cginame, " returned an invalid ", "Status", " header") ;
- if (x[m]) reason = x + m + 1 ;
+ size_t m = uint_scan(statusfield, &status) ;
+ if (!m || (statusfield[m] && statusfield[m] != ' '))
+ die502x(rql, 2, docroot, "cgi ", argv[0], " returned an invalid ", "Status", " header") ;
+ if (statusfield[m]) reason = statusfield + m + 1 ;
else
{
tipidee_defaulttext dt ;
reason = tipidee_util_defaulttext(status, &dt) ? dt.reason : "" ;
}
if (!location && (status == 301 || status == 302 || status == 307 || status == 308))
- die502x(rql, 2, docroot, "cgi ", cginame, " returned a redirection status code without a ", "Location", " header") ;
+ die502x(rql, 2, docroot, "cgi ", argv[0], " returned a redirection status code without a ", "Location", " header") ;
if (status < 100 || status > 999)
- die502x(rql, 2, docroot, "cgi ", cginame, " returned an invalid ", "Status", " value") ;
+ die502x(rql, 2, docroot, "cgi ", argv[0], " returned an invalid ", "Status", " value") ;
}
if (location)
{
- if (!location[0]) die502x(rql, 2, docroot, "cgi ", cginame, " returned an invalid ", "Location", " header") ;
- if (location[0] == '/' && location[1] != '/') return local_redirect(rql, docroot, location, uribuf, cginame) ;
+ if (!location[0]) die502x(rql, 2, docroot, "cgi ", argv[0], " returned an invalid ", "Location", " header") ;
+ if (location[0] == '/' && location[1] != '/')
+ {
+ close(x[0].fd) ;
+ return local_redirect(rql, docroot, location, uribuf, argv[0]) ;
+ }
if (rbodylen)
{
if (!status)
- die502x(rql, 2, docroot, "cgi ", cginame, " didn't output a ", "Status", " header", " for a client redirect response with document") ;
+ die502x(rql, 2, docroot, "cgi ", argv[0], " didn't output a ", "Status", " header", " for a client redirect response with document") ;
if (status < 300 || status > 399)
- die502x(rql, 2, docroot, "cgi ", cginame, " returned an invalid ", "Status", " value", " for a client redirect response with document") ;
+ die502x(rql, 2, docroot, "cgi ", argv[0], " returned an invalid ", "Status", " value", " for a client redirect response with document") ;
}
else
{
- for (size_t i = 0 ; i < hdr->n ; i++)
+ for (size_t i = 0 ; i < hdr.n ; i++)
{
- char const *key = hdr->buf + hdr->list[i].left ;
+ char const *key = hdr.buf + hdr.list[i].left ;
if (!strcasecmp(key, "Location") || !strcasecmp(key, "Status")) continue ;
if (str_start(key, "X-CGI-")) continue ;
- die502x(rql, 2, docroot, "cgi ", cginame, " returned extra headers", " for a client redirect response without document") ;
+ die502x(rql, 2, docroot, "cgi ", argv[0], " returned extra headers", " for a client redirect response without document") ;
}
if (!status)
{
@@ -326,21 +323,14 @@ static inline int process_cgi_output (tipidee_rql *rql, char const *docroot, tip
else
{
if (!status) status = 200 ;
- if (status != 304 && !tipidee_headers_search(hdr, "Content-Type"))
- die502x(rql, 2, docroot, "cgi ", cginame, " didn't output a ", "Content-Type", " header") ;
- }
- x = tipidee_headers_search(hdr, "Content-Length") ;
- if (x)
- {
- size_t cln ;
- if (!size0_scan(x, &cln))
- die502x(rql, 2, docroot, "cgi ", cginame, " returned an invalid ", "Content-Length", " header") ;
- if (cln != rbodylen)
- die502x(rql, 2, docroot, "cgi ", cginame, " returned a mismatching ", "Content-Length", " header") ;
+ if (status != 304 && !tipidee_headers_search(&hdr, "Content-Type"))
+ die502x(rql, 2, docroot, "cgi ", argv[0], " didn't output a ", "Content-Type", " header") ;
}
tipidee_response_status(buffer_1, rql, status, reason) ;
- tipidee_response_header_writemerge_G(buffer_1, g.rhdr, g.rhdrn, hdr, !g.cont) ;
+ tipidee_response_header_writemerge_G(buffer_1, g.rhdr, g.rhdrn, &hdr, !g.cont) ;
+
+ if (contentlength)
{
char fmt[SIZE_FMT] ;
fmt[size_fmt(fmt, rbodylen)] = 0 ;
@@ -348,28 +338,52 @@ static inline int process_cgi_output (tipidee_rql *rql, char const *docroot, tip
buffer_putsnoflush(buffer_1, fmt) ;
buffer_putnoflush(buffer_1, "\r\n", 2) ;
}
- if (buffer_timed_put_g(buffer_1, "\r\n", 2, &deadline) < 2)
- strerr_diefu1sys(111, "write to stdout") ;
+ else if (autochunk)
+ buffer_putsnoflush(buffer_1, "Transfer-Encoding: chunked\r\n") ;
+
+ buffer_putnoflush(buffer_1, "\r\n", 2) ;
tipidee_log_answer(g.logv, rql, status, rbodylen) ;
- if (rbodylen)
+
+ if (contentlength)
+ {
+ size_t len ;
+ struct iovec v[2] ;
+ if (rbodylen)
+ {
+ buffer_wpeek(&b, v) ;
+ len = siovec_len(v, 2) ;
+ if (len > rbodylen)
+ {
+ siovec_trunc(v, 2, rbodylen) ;
+ len = rbodylen ;
+ strerr_warnw3x("cgi ", argv[0], " returned more data than advertised in its Content-Length") ;
+ }
+ if (buffer_timed_putv_g(buffer_1, v, 2, &deadline) < len)
+ strerr_diefu1sys(111, "write to stdout") ;
+ rbodylen -= len ;
+ }
+ if (!rbodylen)
+ {
+ close(x[0].fd) ;
+ return 0 ;
+ }
+ stream_fixed(x[0].fd, rbodylen, argv[0]) ;
+ }
+ else if (autochunk)
+ stream_autochunk(&b, argv[0]) ;
+ else
{
- if (buffer_timed_put_g(buffer_1, rbody, rbodylen, &deadline) < rbodylen)
+ size_t len ;
+ struct iovec v[2] ;
+ buffer_wpeek(&b, v) ;
+ len = siovec_len(v, 2) ;
+ if (buffer_timed_putv_g(buffer_1, v, 2, &deadline) < len)
strerr_diefu1sys(111, "write to stdout") ;
+ stream_infinite(x[0].fd, argv[0]) ;
}
- if (!buffer_timed_flush_g(buffer_1, &deadline))
- strerr_diefu1sys(111, "write to stdout") ;
- return 0 ;
-}
-static inline int do_cgi (tipidee_rql *rql, char const *docroot, char const *const *argv, char const *const *envp, char const *body, size_t bodylen, char *uribuf)
-{
- static stralloc sa = STRALLOC_ZERO ;
- tipidee_headers hdr ;
- char hdrbuf[4096] ;
- sa.len = 0 ;
- tipidee_headers_init(&hdr, hdrbuf, 4096) ;
- if (!run_cgi(rql, docroot, argv, envp, body, bodylen, &hdr, &sa)) return 0 ;
- return process_cgi_output(rql, docroot, &hdr, sa.s, sa.len, uribuf, argv[0]) ;
+ close(x[0].fd) ;
+ return 0 ;
}
int respond_cgi (tipidee_rql *rql, char const *docroot, char const *fn, size_t docrootlen, char const *infopath, char *uribuf, tipidee_headers const *hdr, tipidee_resattr const *ra, char const *body, size_t bodylen)
@@ -384,5 +398,5 @@ int respond_cgi (tipidee_rql *rql, char const *docroot, char const *fn, size_t d
g.sa.len = sabase ;
return ra->flags & TIPIDEE_RA_FLAG_NPH ?
do_nph(rql, docroot, argv, envp, body, bodylen) :
- do_cgi(rql, docroot, argv, envp, body, bodylen, uribuf) ;
+ do_cgi(rql, docroot, argv, envp, body, bodylen, uribuf, rql->http_minor && ra->flags & TIPIDEE_RA_FLAG_AUTOCHUNK) ;
}
diff --git a/src/tipideed/deps-exe/tipideed b/src/tipideed/deps-exe/tipideed
index ac5d4a3..a47d715 100644
--- a/src/tipideed/deps-exe/tipideed
+++ b/src/tipideed/deps-exe/tipideed
@@ -5,6 +5,7 @@ options.o
regular.o
redirection.o
send_file.o
+stream.o
trace.o
util.o
libtipidee.a.xyzzy
diff --git a/src/tipideed/stream.c b/src/tipideed/stream.c
new file mode 100644
index 0000000..7323c93
--- /dev/null
+++ b/src/tipideed/stream.c
@@ -0,0 +1,133 @@
+/* ISC license. */
+
+#include <skalibs/sysdeps.h>
+
+#ifdef SKALIBS_HASSPLICE
+
+#include <skalibs/nonposix.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include <skalibs/allreadwrite.h>
+#include <skalibs/strerr.h>
+#include <skalibs/tai.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/unix-timed.h>
+
+#include "tipideed-internal.h"
+
+struct fixed_info_s
+{
+ int fd ;
+ size_t n ;
+} ;
+
+static int fixed_getfd (void *b)
+{
+ struct fixed_info_s *si = b ;
+ return si->fd ;
+}
+
+static ssize_t fixed_get (void *b)
+{
+ struct fixed_info_s *si = b ;
+ while (si->n)
+ {
+ ssize_t r = sanitize_read(splice(si->fd, 0, 1, 0, si->n, SPLICE_F_NONBLOCK)) ;
+ if (r == -1) break ;
+ if (!r) return 0 ;
+ si->n -= r ;
+ }
+ return si->n ? -1 : 1 ;
+}
+
+void stream_fixed (int fd, size_t n, char const *fn)
+{
+ tain deadline ;
+ struct fixed_info_s si = { .fd = fd, .n = n } ;
+ tain_add_g(&deadline, tain_less(&g.writetto, &g.cgitto) ? &g.cgitto : &g.writetto) ;
+ if (timed_get_g(&si, &fixed_getfd, &fixed_get, &deadline) < 0)
+ strerr_diefu3sys(111, "splice from ", fn, " to stdout") ;
+}
+
+void stream_infinite (int fd, char const *fn)
+{
+ ssize_t r ;
+
+ /* only works when fd is a pipe, which is the case for cgi; fcgi/scgi will need refactoring */
+ /* XXX: ignores timeouts, but this is just TOO GOOD to pass up */
+ /* no really, it is just optimal in 99.9% of cases, it is WORTH IT */
+
+ while ((r = splice(fd, 0, 1, 0, 65536, SPLICE_F_MORE)) > 0) ;
+
+ /* You WISH you had written that line of code */
+
+ if (r == -1) strerr_diefu3sys(111, "splice from ", fn, " to stdout") ;
+}
+
+#else
+
+#include <sys/uio.h>
+#include <unistd.h>
+#include <stdint.h>
+
+#include <skalibs/uint64.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/buffer.h>
+#include <skalibs/strerr.h>
+#include <skalibs/tai.h>
+#include <skalibs/unix-timed.h>
+
+#include "tipideed-internal.h"
+
+void stream_fixed (int fd, size_t n, char const *fn)
+{
+ tain deadline ;
+ struct iovec v[2] ;
+ size_t r ;
+ if (!n) goto flushit ;
+ fillit:
+ buffer_wpeek(buffer_1, v) ;
+ siovec_trunc(v, 2, n) ;
+ tain_add_g(&deadline, &g.cgitto) ;
+ r = timed_readv_g(fd, v, 2, &deadline) ;
+ if (r == -1) strerr_diefu2sys(111, "read from resource ", fn) ;
+ if (!r) strerr_dief3x(111, "resource ", fn, " provided less content than advertised in Content-Length") ;
+ buffer_wseek(buffer_1, r) ;
+ n -= r ;
+ flushit:
+ tain_add_g(&deadline, &g.writetto) ;
+ if (!buffer_timed_flush_g(buffer_1, &deadline))
+ strerr_diefu1sys(111, "write to stdout") ;
+ if (n) goto fillit ;
+}
+
+void stream_infinite (int fd, char const *fn)
+{
+ tain deadline ;
+ struct iovec v[2] ;
+ size_t r ;
+ for (;;)
+ {
+ buffer_wpeek(buffer_1, v) ;
+ tain_add_g(&deadline, &g.cgitto) ;
+ r = timed_readv_g(fd, v, 2, &deadline) ;
+ if (r == -1) strerr_diefu2sys(111, "read from resource ", fn) ;
+ if (!r) break ;
+ tain_add_g(&deadline, &g.writetto) ;
+ if (!buffer_timed_flush_g(buffer_1, &deadline))
+ strerr_diefu1sys(111, "write to stdout") ;
+ }
+}
+
+#endif
+
+
+#include <skalibs/buffer.h>
+
+void stream_autochunk (buffer *b, char const *fn)
+{
+}
diff --git a/src/tipideed/tipideed-internal.h b/src/tipideed/tipideed-internal.h
index 1087b15..eaa8341 100644
--- a/src/tipideed/tipideed-internal.h
+++ b/src/tipideed/tipideed-internal.h
@@ -139,6 +139,13 @@ extern void send_file_range (int, uint64_t, uint64_t, char const *) ;
#define send_file(fd, n, fn) send_file_range(fd, 0, n, fn)
+ /* stream */
+
+extern void stream_fixed (int, uint64_t, char const *) ;
+extern void stream_autochunk (buffer *, char const *) ;
+extern void stream_infinite (int, char const *) ;
+
+
/* regular */
extern int respond_regular (tipidee_rql const *, char const *, char const *, struct stat const *, tipidee_resattr const *) ;