diff options
Diffstat (limited to 'src')
22 files changed, 372 insertions, 100 deletions
diff --git a/src/config/headers.c b/src/config/headers.c index 0afdfb3..c3aa94c 100644 --- a/src/config/headers.c +++ b/src/config/headers.c @@ -21,7 +21,7 @@ struct builtinheaders_s static struct builtinheaders_s const builtinheaders[] = { - { .key = "Accept-Ranges", .value = "none", .overridable = 0 }, + { .key = "Accept-Ranges", .value = "bytes", .overridable = 0 }, { .key = "Allow", .value = 0, .overridable = 0 }, { .key = "Cache-Control", .value = "private", .overridable = 1 }, { .key = "Connection", .value = 0, .overridable = 0 }, diff --git a/src/include/tipidee/response.h b/src/include/tipidee/response.h index 31b8df4..f912cd3 100644 --- a/src/include/tipidee/response.h +++ b/src/include/tipidee/response.h @@ -10,6 +10,7 @@ #include <skalibs/gccattributes.h> #include <skalibs/stat.h> +#include <skalibs/uint64.h> #include <skalibs/buffer.h> #include <skalibs/strerr.h> #include <skalibs/tai.h> @@ -35,21 +36,24 @@ extern size_t tipidee_response_header_date_G (char *, size_t) ; extern size_t tipidee_response_header_lastmodified (char *, size_t, struct stat const *) ; +extern size_t tipidee_response_header_write (buffer *, tipidee_response_header const *, uint32_t) ; extern size_t tipidee_response_header_writeall (buffer *, tipidee_response_header const *, uint32_t, uint32_t, tain const *) ; extern size_t tipidee_response_header_writeall_G (buffer *, tipidee_response_header const *, uint32_t, uint32_t) ; -#define tipidee_response_header_writeall_g(b, rhdr, rhdrn, options) tipidee_response_header_writeall(b, rhdr, rhdrn, (options), &STAMP) + +extern size_t tipidee_response_header_end (buffer *) ; + extern size_t tipidee_response_header_writemerge (buffer *, tipidee_response_header const *, uint32_t, tipidee_headers const *, uint32_t, tain const *) ; extern size_t tipidee_response_header_writemerge_G (buffer *, tipidee_response_header const *, uint32_t, tipidee_headers const *, uint32_t) ; -#define tipidee_response_header_writemerge_g(b, rhdr, rhdrn, hdr, options) tipidee_response_header_writemerge(b, rhdr, rhdrn, hdr, (options), &STAMP) -size_t tipidee_response_file (buffer *, tipidee_rql const *, unsigned int, char const *, struct stat const *, char const *, tipidee_response_header const *, uint32_t, uint32_t, tain const *) ; -size_t tipidee_response_file_G (buffer *, tipidee_rql const *, unsigned int, char const *, struct stat const *, char const *, tipidee_response_header const *, uint32_t, uint32_t) ; -#define tipidee_response_file_g(b, rql, status, reason, st, ct, rhdr, rhdrn, options) tipidee_response_file(b, rql, status, reason, st, ct, rhdr, rhdrn, (options), &STAMP) +size_t tipidee_response_file (buffer *, tipidee_rql const *, unsigned int, char const *, struct stat const *, uint64_t, char const *, tipidee_response_header const *, uint32_t, uint32_t, tain const *) ; +size_t tipidee_response_file_G (buffer *, tipidee_rql const *, unsigned int, char const *, struct stat const *, uint64_t, char const *, tipidee_response_header const *, uint32_t, uint32_t) ; + +size_t tipidee_response_partial (buffer *, tipidee_rql const *, struct stat const *, uint64_t, uint64_t, char const *, tipidee_response_header const *, uint32_t, uint32_t, tain const *) ; +size_t tipidee_response_partial_G (buffer *, tipidee_rql const *, struct stat const *, uint64_t, uint64_t, char const *, tipidee_response_header const *, uint32_t, uint32_t) ; -extern size_t tipidee_response_error_nofile (buffer *, tipidee_rql const *, unsigned int, char const *, char const *, tipidee_response_header const *, uint32_t, uint32_t, tain const *) ; -extern size_t tipidee_response_error_nofile_G (buffer *, tipidee_rql const *, unsigned int, char const *, char const *, tipidee_response_header const *, uint32_t, uint32_t) ; -#define tipidee_response_error_nofile_g(b, rql, status, reason, text, rhdr, rhdrn, options) tipidee_response_error_nofile(b, rql, status, reason, text, rhdr, rhdrn, (options), &STAMP) +extern size_t tipidee_response_error_nofile (buffer *, tipidee_rql const *, unsigned int, char const *, char const *, tipidee_response_header const *, uint32_t, tipidee_response_header const *, uint32_t, uint32_t, tain const *) ; +extern size_t tipidee_response_error_nofile_G (buffer *, tipidee_rql const *, unsigned int, char const *, char const *, tipidee_response_header const *, uint32_t, tipidee_response_header const *, uint32_t, uint32_t) ; extern int tipidee_response_header_preparebuiltin (tipidee_response_header *, uint32_t, char const *, size_t) ; diff --git a/src/include/tipidee/util.h b/src/include/tipidee/util.h index 7870a23..18eb2ce 100644 --- a/src/include/tipidee/util.h +++ b/src/include/tipidee/util.h @@ -3,9 +3,10 @@ #ifndef TIPIDEE_UTIL_H #define TIPIDEE_UTIL_H -#include <stddef.h> +#include <sys/types.h> #include <stdint.h> +#include <skalibs/uint64.h> #include <skalibs/buffer.h> #include <skalibs/tai.h> #include <skalibs/stralloc.h> @@ -31,4 +32,6 @@ extern int tipidee_util_chunked_read (buffer *, stralloc *, size_t, tain const * extern int tipidee_util_httpdate (char const *, tain *) ; extern int tipidee_util_defaulttext (unsigned int, tipidee_defaulttext *) ; +extern int tipidee_util_parse_range (char const *, off_t, uint64_t *, uint64_t *) ; + #endif diff --git a/src/libtipidee/deps-lib/tipidee b/src/libtipidee/deps-lib/tipidee index 985be7e..33f9f2f 100644 --- a/src/libtipidee/deps-lib/tipidee +++ b/src/libtipidee/deps-lib/tipidee @@ -16,8 +16,8 @@ tipidee_headers_parse.o tipidee_headers_search.o tipidee_log_answer.o tipidee_log_exit.o -tipidee_log_resource.o tipidee_log_request.o +tipidee_log_resource.o tipidee_log_start.o tipidee_method.o tipidee_response_error_nofile.o @@ -27,16 +27,21 @@ tipidee_response_file_G.o tipidee_response_header_date.o tipidee_response_header_date_G.o tipidee_response_header_date_fmt.o +tipidee_response_header_end.o tipidee_response_header_lastmodified.o tipidee_response_header_preparebuiltin.o +tipidee_response_header_write.o tipidee_response_header_writeall.o tipidee_response_header_writeall_G.o tipidee_response_header_writemerge.o tipidee_response_header_writemerge_G.o +tipidee_response_partial.o +tipidee_response_partial_G.o tipidee_response_status.o tipidee_rql_read.o tipidee_uri_parse.o tipidee_util_chunked_read.o tipidee_util_defaulttext.o tipidee_util_httpdate.o +tipidee_util_parse_range.o -lskarnet diff --git a/src/libtipidee/tipidee_response_error_nofile.c b/src/libtipidee/tipidee_response_error_nofile.c index 8a7cd53..10da032 100644 --- a/src/libtipidee/tipidee_response_error_nofile.c +++ b/src/libtipidee/tipidee_response_error_nofile.c @@ -8,19 +8,21 @@ #include <tipidee/method.h> #include <tipidee/response.h> -size_t tipidee_response_error_nofile (buffer *b, tipidee_rql const *rql, unsigned int status, char const *reason, char const *text, tipidee_response_header const *rhdr, uint32_t rhdrn, uint32_t options, tain const *stamp) +size_t tipidee_response_error_nofile (buffer *b, tipidee_rql const *rql, unsigned int status, char const *reason, char const *text, tipidee_response_header const *rhdr, uint32_t rhdrn, tipidee_response_header const *plus, uint32_t plusn, uint32_t options, tain const *stamp) { static char const txt1[] = "<html>\n<head><title>" ; static char const txt2[] = "</title></head>\n<body>\n<h1> " ; static char const txt3[] = " </h1>\n<p>\n" ; static char const txt4[] = "\n</p>\n</body>\n</html>\n" ; char fmt[SIZE_FMT] ; + tipidee_response_header v[2] = { { .key = "Content-Type", .value = "text/html; charset=UTF-8", .options = 0 }, { .key = "Content-Length", .value = fmt, .options = 0 } } ; size_t n = tipidee_response_status(b, rql, status, reason) ; n += tipidee_response_header_writeall(b, rhdr, rhdrn, options, stamp) ; - n += buffer_putsnoflush(b, "Content-Type: text/html; charset=UTF-8\r\n") ; - n += buffer_putsnoflush(b, "Content-Length: ") ; - n += buffer_putnoflush(b, fmt, size_fmt(fmt, text ? sizeof(txt1) + sizeof(txt2) + sizeof(txt3) + sizeof(txt4) - 4 + 2 * strlen(reason) + strlen(text) : 0)) ; - n += buffer_putnoflush(b, "\r\n\r\n", 4) ; + if (plusn) n += tipidee_response_header_write(b, plus, plusn) ; + fmt[size_fmt(fmt, text ? sizeof(txt1) + sizeof(txt2) + sizeof(txt3) + sizeof(txt4) - 4 + 2 * strlen(reason) + strlen(text) : 0)] = 0 ; + n += tipidee_response_header_write(b, v, 2) ; + + n += buffer_putnoflush(b, "\r\n", 2) ; if (text && rql->m != TIPIDEE_METHOD_HEAD) { n += buffer_putsnoflush(b, txt1) ; diff --git a/src/libtipidee/tipidee_response_error_nofile_G.c b/src/libtipidee/tipidee_response_error_nofile_G.c index d7239cc..e6bdce5 100644 --- a/src/libtipidee/tipidee_response_error_nofile_G.c +++ b/src/libtipidee/tipidee_response_error_nofile_G.c @@ -4,9 +4,9 @@ #include <tipidee/response.h> -size_t tipidee_response_error_nofile_G (buffer *b, tipidee_rql const *rql, unsigned int status, char const *reason, char const *text, tipidee_response_header const *rhdr, uint32_t rhdrn, uint32_t options) +size_t tipidee_response_error_nofile_G (buffer *b, tipidee_rql const *rql, unsigned int status, char const *reason, char const *text, tipidee_response_header const *rhdr, uint32_t rhdrn, tipidee_response_header const *plus, uint32_t plusn, uint32_t options) { tain wstamp ; tain_wallclock_read(&wstamp) ; - return tipidee_response_error_nofile(b, rql, status, reason, text, rhdr, rhdrn, options, &wstamp) ; + return tipidee_response_error_nofile(b, rql, status, reason, text, rhdr, rhdrn, plus, plusn, options, &wstamp) ; } diff --git a/src/libtipidee/tipidee_response_file.c b/src/libtipidee/tipidee_response_file.c index f2021ee..b64d086 100644 --- a/src/libtipidee/tipidee_response_file.c +++ b/src/libtipidee/tipidee_response_file.c @@ -2,18 +2,15 @@ #include <stddef.h> -#include <skalibs/types.h> +#include <skalibs/uint64.h> #include <skalibs/buffer.h> -#include <tipidee/conf.h> -#include <tipidee/method.h> -#include <tipidee/rql.h> #include <tipidee/response.h> -#include <tipidee/util.h> -size_t tipidee_response_file (buffer *b, tipidee_rql const *rql, unsigned int status, char const *reason, struct stat const *st, char const *ct, tipidee_response_header const *rhdr, uint32_t rhdrn, uint32_t options, tain const *stamp) +size_t tipidee_response_file (buffer *b, tipidee_rql const *rql, unsigned int status, char const *reason, struct stat const *st, uint64_t cl, char const *ct, tipidee_response_header const *rhdr, uint32_t rhdrn, uint32_t options, tain const *stamp) { char fmt[128] ; + tipidee_response_header v[2] = { { .key = "Content-Type", .value = ct, .options = 0 }, { .key = "Content-Length", .value = fmt, .options = 0 } } ; size_t n = tipidee_response_status(b, rql, status, reason) ; n += tipidee_response_header_writeall(b, rhdr, rhdrn, options & 1, stamp) ; if (options & 2) @@ -21,11 +18,7 @@ size_t tipidee_response_file (buffer *b, tipidee_rql const *rql, unsigned int st size_t l = tipidee_response_header_lastmodified(fmt, 128, st) ; if (l) n += buffer_putnoflush(b, fmt, l) ; } - n += buffer_putsnoflush(b, "Content-Type: ") ; - n += buffer_putsnoflush(b, ct) ; - n += buffer_putsnoflush(b, "\r\nContent-Length: ") ; - fmt[uint64_fmt(fmt, st->st_size)] = 0 ; - n += buffer_putsnoflush(b, fmt) ; - n += buffer_putnoflush(b, "\r\n\r\n", 4) ; + fmt[uint64_fmt(fmt, cl)] = 0 ; + n += tipidee_response_header_write(b, v, 2) ; return n ; } diff --git a/src/libtipidee/tipidee_response_file_G.c b/src/libtipidee/tipidee_response_file_G.c index 8c08c98..06af667 100644 --- a/src/libtipidee/tipidee_response_file_G.c +++ b/src/libtipidee/tipidee_response_file_G.c @@ -4,9 +4,9 @@ #include <tipidee/response.h> -size_t tipidee_response_file_G (buffer *b, tipidee_rql const *rql, unsigned int status, char const *reason, struct stat const *st, char const *ct, tipidee_response_header const *rhdr, uint32_t rhdrn, uint32_t options) +size_t tipidee_response_file_G (buffer *b, tipidee_rql const *rql, unsigned int status, char const *reason, struct stat const *st, uint64_t cl, char const *ct, tipidee_response_header const *rhdr, uint32_t rhdrn, uint32_t options) { tain wstamp ; tain_wallclock_read(&wstamp) ; - return tipidee_response_file(b, rql, status, reason, st, ct, rhdr, rhdrn, options, &wstamp) ; + return tipidee_response_file(b, rql, status, reason, st, cl, ct, rhdr, rhdrn, options, &wstamp) ; } diff --git a/src/libtipidee/tipidee_response_header_end.c b/src/libtipidee/tipidee_response_header_end.c new file mode 100644 index 0000000..ddd8575 --- /dev/null +++ b/src/libtipidee/tipidee_response_header_end.c @@ -0,0 +1,10 @@ +/* ISC license. */ + +#include <skalibs/buffer.h> + +#include <tipidee/response.h> + +size_t tipidee_response_header_end (buffer *b) +{ + return buffer_put(b, "\r\n", 2) ; +} diff --git a/src/libtipidee/tipidee_response_header_write.c b/src/libtipidee/tipidee_response_header_write.c new file mode 100644 index 0000000..6c8edd9 --- /dev/null +++ b/src/libtipidee/tipidee_response_header_write.c @@ -0,0 +1,21 @@ +/* ISC license. */ + +#include <stddef.h> + +#include <skalibs/buffer.h> + +#include <tipidee/response.h> + +size_t tipidee_response_header_write (buffer *b, tipidee_response_header const *hdr, uint32_t n) +{ + size_t m = 0 ; + for (uint32_t i = 0 ; i < n ; i++) + { + if (!hdr[i].value) continue ; + m += buffer_putsnoflush(b, hdr[i].key) ; + m += buffer_putnoflush(b, ": ", 2) ; + m += buffer_putsnoflush(b, hdr[i].value) ; + m += buffer_putnoflush(b, "\r\n", 2) ; + } + return m ; +} diff --git a/src/libtipidee/tipidee_response_header_writeall.c b/src/libtipidee/tipidee_response_header_writeall.c index b94578d..36bc9bf 100644 --- a/src/libtipidee/tipidee_response_header_writeall.c +++ b/src/libtipidee/tipidee_response_header_writeall.c @@ -1,5 +1,7 @@ /* ISC license. */ +#include <stddef.h> + #include <skalibs/buffer.h> #include <tipidee/response.h> @@ -9,13 +11,6 @@ size_t tipidee_response_header_writeall (buffer *b, tipidee_response_header cons char fmt[128] ; size_t m = buffer_putnoflush(b, fmt, tipidee_response_header_date(fmt, 128, stamp)) ; if (options & 1) m += buffer_putsnoflush(b, "Connection: close\r\n") ; - for (uint32_t i = 0 ; i < rhdrn ; i++) - { - if (!rhdr[i].value) continue ; - m += buffer_putsnoflush(b, rhdr[i].key) ; - m += buffer_putnoflush(b, ": ", 2) ; - m += buffer_putsnoflush(b, rhdr[i].value) ; - m += buffer_putnoflush(b, "\r\n", 2) ; - } + m += tipidee_response_header_write(b, rhdr, rhdrn) ; return m ; } diff --git a/src/libtipidee/tipidee_response_partial.c b/src/libtipidee/tipidee_response_partial.c new file mode 100644 index 0000000..4411896 --- /dev/null +++ b/src/libtipidee/tipidee_response_partial.c @@ -0,0 +1,29 @@ +/* ISC license. */ + +#include <stddef.h> + +#include <skalibs/uint64.h> +#include <skalibs/buffer.h> + +#include <tipidee/util.h> +#include <tipidee/response.h> + +size_t tipidee_response_partial (buffer *b, tipidee_rql const *rql, struct stat const *st, uint64_t start, uint64_t len, char const *ct, tipidee_response_header const *rhdr, uint32_t rhdrn, uint32_t options, tain const *stamp) +{ + tipidee_defaulttext dt ; + size_t n ; + char fmt[UINT64_FMT] ; + if (!tipidee_util_defaulttext(206, &dt)) return 0 ; + n = tipidee_response_file(b, rql, 206, dt.reason, st, len, ct, rhdr, rhdrn, options, stamp) ; + if (len) + { + n += buffer_putsnoflush(b, "Content-Range: bytes ") ; + n += buffer_putnoflush(b, fmt, uint64_fmt(fmt, start)) ; + n += buffer_putnoflush(b, "-", 1) ; + n += buffer_putnoflush(b, fmt, uint64_fmt(fmt, start + len - 1)) ; + n += buffer_putnoflush(b, "/", 1) ; + n += buffer_putnoflush(b, fmt, uint64_fmt(fmt, st->st_size)) ; + n += buffer_putnoflush(b, "\r\n", 2) ; + } + return n ; +} diff --git a/src/libtipidee/tipidee_response_partial_G.c b/src/libtipidee/tipidee_response_partial_G.c new file mode 100644 index 0000000..d9c5d78 --- /dev/null +++ b/src/libtipidee/tipidee_response_partial_G.c @@ -0,0 +1,12 @@ +/* ISC license. */ + +#include <skalibs/tai.h> + +#include <tipidee/response.h> + +size_t tipidee_response_partial_G (buffer *b, tipidee_rql const *rql, struct stat const *st, uint64_t start, uint64_t len, char const *ct, tipidee_response_header const *rhdr, uint32_t rhdrn, uint32_t options) +{ + tain wstamp ; + tain_wallclock_read(&wstamp) ; + return tipidee_response_partial(b, rql, st, start, len, ct, rhdr, rhdrn, options, &wstamp) ; +} diff --git a/src/libtipidee/tipidee_util_parse_range.c b/src/libtipidee/tipidee_util_parse_range.c new file mode 100644 index 0000000..e096fda --- /dev/null +++ b/src/libtipidee/tipidee_util_parse_range.c @@ -0,0 +1,49 @@ +/* ISC license. */ + +#include <string.h> + +#include <skalibs/uint64.h> + +#include <tipidee/util.h> + +int tipidee_util_parse_range (char const *s, off_t max, uint64_t *start, uint64_t *len) +{ + if (strncmp(s, "bytes=", 6)) return -1 ; + s += 6 ; + if (*s == '-') + { + uint64_t n ; + size_t m = uint64_scan(++s, &n) ; + if (!m) return -1 ; + s += m ; + if (*s && *s != ',') return -1 ; + if (n > max) return -1 ; + *start = max - n ; + *len = n ; + return 1 ; + } + else + { + uint64_t beg ; + uint64_t n ; + size_t m = uint64_scan(s, &beg) ; + if (!m) return -1 ; + s += m ; + if (*s++ != '-') return -1 ; + if (beg >= max) return -1 ; + if (!*s || *s == ',') + { + *start = beg ; + *len = max - beg ; + return 1 ; + } + m = uint64_scan(s, &n) ; + if (!m) return -1 ; + s += m ; + if (*s && *s != ',') return -1 ; + if (n >= max || n < beg) return -1 ; + *start = beg ; + *len = n + 1 - beg ; + return 1 ; + } +} diff --git a/src/tipideed/cgi.c b/src/tipideed/cgi.c index 343f7af..14da139 100644 --- a/src/tipideed/cgi.c +++ b/src/tipideed/cgi.c @@ -25,13 +25,14 @@ #include <tipidee/tipidee.h> #include "tipideed-internal.h" +#define dienomem(rql, docroot) die500sys(rql, 111, (docroot), "stralloc_catb") + static void addenv_ (tipidee_rql const *rql, char const *docroot, char const *k, char const *v, int slash) { if (!stralloc_cats(&g.sa, k) || !stralloc_catb(&g.sa, "=/", 1 + !!slash) || !stralloc_cats(&g.sa, v) - || !stralloc_0(&g.sa)) - die500sys(rql, 111, docroot, "stralloc_catb") ; + || !stralloc_0(&g.sa)) dienomem(rql, docroot) ; } #define addenv(rql, d, k, v) addenv_(rql, d, k, (v), 0) @@ -40,8 +41,24 @@ static void addenv_ (tipidee_rql const *rql, char const *docroot, char const *k, static void delenv (tipidee_rql const *rql, char const *docroot, char const *k) { if (!stralloc_cats(&g.sa, k) - || !stralloc_0(&g.sa)) - die500sys(rql, 111, docroot, "stralloc_catb") ; + || !stralloc_0(&g.sa)) dienomem(rql, docroot) ; +} + +static void addrequesturi (tipidee_rql const *rql, char const *docroot, char const *script, char const *infopath) +{ + if (!stralloc_cats(&g.sa, "REQUEST_URI=") + || !stralloc_cats(&g.sa, script)) dienomem(rql, docroot) ; + if (infopath) + { + if (!stralloc_catb(&g.sa, "/", 1) + || !stralloc_cats(&g.sa, infopath)) dienomem(rql, docroot) ; + } + if (rql->uri.query) + { + if (!stralloc_catb(&g.sa, "?", 1) + || !stralloc_cats(&g.sa, rql->uri.query)) dienomem(rql, docroot) ; + } + if (!stralloc_0(&g.sa)) dienomem(rql, docroot) ; } static inline void modify_env (tipidee_rql const *rql, char const *docroot, tipidee_headers const *hdr, size_t cl, char const *script, char const *infopath) @@ -61,13 +78,14 @@ static inline void modify_env (tipidee_rql const *rql, char const *docroot, tipi if (rql->uri.query) addenv(rql, docroot, "QUERY_STRING", rql->uri.query) ; else delenv(rql, docroot, "QUERY_STRING") ; addenv(rql, docroot, "SCRIPT_NAME", script) ; + addrequesturi(rql, docroot, script, infopath) ; addenv(rql, docroot, "SERVER_NAME", rql->uri.host) ; { char proto[9] = "HTTP/1.1" ; if (!rql->http_minor) proto[7] = '0' ; addenv(rql, docroot, "SERVER_PROTOCOL", proto) ; } - + for (size_t i = 0 ; i < hdr->n ; i++) { char const *key = hdr->buf + hdr->list[i].left ; diff --git a/src/tipideed/deps-exe/tipideed b/src/tipideed/deps-exe/tipideed index 8e5263e..ac5d4a3 100644 --- a/src/tipideed/deps-exe/tipideed +++ b/src/tipideed/deps-exe/tipideed @@ -1,8 +1,9 @@ cgi.o +errors.o harden.o options.o regular.o -responses.o +redirection.o send_file.o trace.o util.o diff --git a/src/tipideed/responses.c b/src/tipideed/errors.c index 6e32ea9..9b00f3b 100644 --- a/src/tipideed/responses.c +++ b/src/tipideed/errors.c @@ -21,22 +21,28 @@ #include "tipideed-internal.h" -void response_error_early (tipidee_rql const *rql, unsigned int status, char const *reason, char const *text, uint32_t options) +void response_error_early_plus (tipidee_rql const *rql, unsigned int status, char const *reason, char const *text, tipidee_response_header const *v, uint32_t n, uint32_t options) { tain deadline ; - tipidee_response_error_nofile_G(buffer_1, rql, status, reason, text, g.rhdr, g.rhdrn, options & 1 || !g.cont) ; + tipidee_response_error_nofile_G(buffer_1, rql, status, reason, text, g.rhdr, g.rhdrn, v, n, options & 1 || !g.cont) ; tain_add_g(&deadline, &g.writetto) ; if (!buffer_timed_flush_g(buffer_1, &deadline)) strerr_diefu1sys(111, "write to stdout") ; } -void response_error_early_and_exit (tipidee_rql const *rql, unsigned int status, char const *reason, char const *text) +void response_error_early_plus_and_exit (tipidee_rql const *rql, unsigned int status, char const *reason, char const *text, tipidee_response_header const *v, uint32_t n) { - response_error_early(rql, status, reason, text, 1) ; + response_error_early_plus(rql, status, reason, text, v, n, 1) ; log_and_exit(1) ; } -void response_error (tipidee_rql const *rql, char const *docroot, unsigned int status, uint32_t options) +void eexit_405 (tipidee_rql const *rql) +{ + static tipidee_response_header const allowpost = { .key = "Allow", .value = "GET, HEAD, POST", .options = 0 } ; + response_error_early_plus_and_exit(rql, 405, "Method Not Allowed", "Invalid method for the resource.", &allowpost, 1) ; +} + +void response_error_plus (tipidee_rql const *rql, char const *docroot, unsigned int status, tipidee_response_header const *plus, uint32_t plusn, uint32_t options) { tain deadline ; tipidee_defaulttext dt ; @@ -85,7 +91,9 @@ void response_error (tipidee_rql const *rql, char const *docroot, unsigned int s } else { - tipidee_response_file_G(buffer_1, rql, status, dt.reason, &st, tipidee_conf_get_content_type(&g.conf, g.sa.s + pos), g.rhdr, g.rhdrn, options) ; + tipidee_response_file_G(buffer_1, rql, status, dt.reason, &st, st.st_size, tipidee_conf_get_content_type(&g.conf, g.sa.s + pos), g.rhdr, g.rhdrn, options) ; + tipidee_response_header_write(buffer_1, plus, plusn) ; + tipidee_response_header_end(buffer_1) ; tipidee_log_answer(g.logv, rql, status, st.st_size) ; send_file(fd, st.st_size, g.sa.s + pos) ; fd_close(fd) ; @@ -95,56 +103,38 @@ void response_error (tipidee_rql const *rql, char const *docroot, unsigned int s } } - tipidee_response_error_nofile_G(buffer_1, rql, status, dt.reason, dt.text, g.rhdr, g.rhdrn, options & 1 || !g.cont) ; + tipidee_response_error_nofile_G(buffer_1, rql, status, dt.reason, dt.text, g.rhdr, g.rhdrn, plus, plusn, options & 1 || !g.cont) ; tipidee_log_answer(g.logv, rql, status, 0) ; tain_add_g(&deadline, &g.writetto) ; if (!buffer_timed_flush_g(buffer_1, &deadline)) strerr_diefu1sys(111, "write to stdout") ; } -void response_error_and_exit (tipidee_rql const *rql, char const *docroot, unsigned int status) +void response_error_plus_and_exit (tipidee_rql const *rql, char const *docroot, unsigned int status, tipidee_response_header const *plus, uint32_t plusn) { - response_error(rql, docroot, status, 1) ; + response_error_plus(rql, docroot, status, plus, plusn, 1) ; log_and_exit(0) ; } -void response_error_and_die (tipidee_rql const *rql, int e, char const *docroot, unsigned int status, char const *const *v, unsigned int n, uint32_t options) +void response_error_plus_and_die (tipidee_rql const *rql, int e, char const *docroot, unsigned int status, tipidee_response_header const *plus, uint32_t plusn, char const *const *v, unsigned int n, uint32_t options) { int serr = errno ; - response_error(rql, docroot, status, options | 1) ; + response_error_plus(rql, docroot, status, plus, plusn, options | 1) ; errno = serr ; if (options & 1) strerr_dievsys(e, v, n) ; else strerr_diev(e, v, n) ; } -void exit_405_ (tipidee_rql const *rql, uint32_t options) +void exit_405 (tipidee_rql const *rql, char const *docroot, uint32_t options) { - tain deadline ; - tipidee_response_status(buffer_1, rql, 405, "Method Not Allowed") ; - tipidee_response_header_writeall_G(buffer_1, g.rhdr, g.rhdrn, 1) ; - buffer_putsnoflush(buffer_1, "Allow: GET, HEAD") ; - if (options & 1) buffer_putsnoflush(buffer_1, ", POST") ; - buffer_putnoflush(buffer_1, "\r\n\r\n", 4) ; - if (!(options & 2)) tipidee_log_answer(g.logv, rql, 405, 0) ; - tain_add_g(&deadline, &g.writetto) ; - if (!buffer_timed_flush_g(buffer_1, &deadline)) - strerr_diefu1sys(111, "write to stdout") ; - log_and_exit(0) ; + tipidee_response_header hd = { .key = "Allow", .value = options & 1 ? "GET, HEAD, POST" : "GET, HEAD", .options = 0 } ; + response_error_plus_and_exit(rql, docroot, 405, &hd, 1) ; } -void respond_30x (tipidee_rql const *rql, tipidee_redirection const *rd) +void respond_416 (tipidee_rql const *rql, char const *docroot, uint64_t size) { - static unsigned int const status[4] = { 307, 308, 302, 301 } ; - static char const *const reason[4] = { "Temporary Redirect", "Permanent Redirect", "Found", "Moved Permanently" } ; - tain deadline ; - tipidee_response_status(buffer_1, rql, status[rd->type], reason[rd->type]) ; - tipidee_response_header_writeall_G(buffer_1, g.rhdr, g.rhdrn, 0) ; - buffer_putsnoflush(buffer_1, "Content-Length: 0\r\nLocation: ") ; - buffer_putsnoflush(buffer_1, rd->location) ; - if (rd->sub) buffer_putsnoflush(buffer_1, rd->sub) ; - buffer_putnoflush(buffer_1, "\r\n\r\n", 4) ; - tipidee_log_answer(g.logv, rql, status[rd->type], 0) ; - tain_add_g(&deadline, &g.writetto) ; - if (!buffer_timed_flush_g(buffer_1, &deadline)) - strerr_diefu1sys(111, "write to stdout") ; + char buf[8 + UINT64_FMT] = "bytes */" ; + tipidee_response_header cr = { .key = "Content-Range", .value = buf, .options = 0 } ; + buf[8 + uint64_fmt(buf + 8, size)] = 0 ; + response_error_plus(rql, docroot, 416, &cr, 1, 0) ; } diff --git a/src/tipideed/redirection.c b/src/tipideed/redirection.c new file mode 100644 index 0000000..e525e66 --- /dev/null +++ b/src/tipideed/redirection.c @@ -0,0 +1,29 @@ +/* ISC license. */ + +#include <skalibs/buffer.h> +#include <skalibs/strerr.h> +#include <skalibs/tai.h> +#include <skalibs/unix-timed.h> + +#include <tipidee/log.h> +#include <tipidee/util.h> +#include <tipidee/response.h> +#include "tipideed-internal.h" + +void respond_30x (tipidee_rql const *rql, tipidee_redirection const *rd) +{ + static unsigned int const status[4] = { 307, 308, 302, 301 } ; + tipidee_defaulttext dt ; + tain deadline ; + tipidee_util_defaulttext(status[rd->type], &dt) ; + tipidee_response_status(buffer_1, rql, status[rd->type], dt.reason) ; + tipidee_response_header_writeall_G(buffer_1, g.rhdr, g.rhdrn, 0) ; + buffer_putsnoflush(buffer_1, "Content-Length: 0\r\nLocation: ") ; + buffer_putsnoflush(buffer_1, rd->location) ; + if (rd->sub) buffer_putsnoflush(buffer_1, rd->sub) ; + buffer_putnoflush(buffer_1, "\r\n\r\n", 4) ; + tipidee_log_answer(g.logv, rql, status[rd->type], 0) ; + tain_add_g(&deadline, &g.writetto) ; + if (!buffer_timed_flush_g(buffer_1, &deadline)) + strerr_diefu1sys(111, "write to stdout") ; +} diff --git a/src/tipideed/regular.c b/src/tipideed/regular.c index 67fba35..3d82750 100644 --- a/src/tipideed/regular.c +++ b/src/tipideed/regular.c @@ -22,7 +22,8 @@ int respond_regular (tipidee_rql const *rql, char const *docroot, char const *fn if (rql->m == TIPIDEE_METHOD_HEAD) { tain deadline ; - tipidee_response_file_G(buffer_1, rql, 200, "OK", st, ra->content_type, g.rhdr, g.rhdrn, 2 | !g.cont) ; + tipidee_response_file_G(buffer_1, rql, 200, "OK", st, st->st_size, ra->content_type, g.rhdr, g.rhdrn, 2 | !g.cont) ; + tipidee_response_header_end(buffer_1) ; tipidee_log_answer(g.logv, rql, 200, st->st_size) ; tain_add_g(&deadline, &g.writetto) ; if (!buffer_timed_flush_g(buffer_1, &deadline)) @@ -40,7 +41,8 @@ int respond_regular (tipidee_rql const *rql, char const *docroot, char const *fn } else die500sys(rql, 111, docroot, "open ", fn) ; } - tipidee_response_file_G(buffer_1, rql, 200, "OK", st, ra->content_type, g.rhdr, g.rhdrn, 2 | !g.cont) ; + tipidee_response_file_G(buffer_1, rql, 200, "OK", st, st->st_size, ra->content_type, g.rhdr, g.rhdrn, 2 | !g.cont) ; + tipidee_response_header_end(buffer_1) ; tipidee_log_answer(g.logv, rql, 200, st->st_size) ; send_file(fd, st->st_size, fn) ; fd_close(fd) ; @@ -58,10 +60,30 @@ int respond_304 (tipidee_rql const *rql, char const *fn, struct stat const *st) size_t l = tipidee_response_header_lastmodified(fmt, 128, st) ; if (l) buffer_putnoflush(buffer_1, fmt, l) ; } - buffer_putnoflush(buffer_1, "\r\n", 2) ; + tipidee_response_header_end(buffer_1) ; tipidee_log_answer(g.logv, rql, 304, 0) ; tain_add_g(&deadline, &g.writetto) ; if (!buffer_timed_flush_g(buffer_1, &deadline)) strerr_diefu1sys(111, "write to stdout") ; return 0 ; } + +int respond_partial (tipidee_rql const *rql, char const *docroot, char const *fn, struct stat const *st, uint64_t start, uint64_t len, tipidee_resattr const *ra) +{ + int fd = open_read(fn) ; + if (fd == -1) + { + if (errno == EACCES) + { + respond_403(rql, docroot) ; + return 0 ; + } + else die500sys(rql, 111, docroot, "open ", fn) ; + } + tipidee_response_partial_G(buffer_1, rql, st, start, len, ra->content_type, g.rhdr, g.rhdrn, 2 | !g.cont) ; + tipidee_response_header_end(buffer_1) ; + tipidee_log_answer(g.logv, rql, 206, len) ; + send_file_range(fd, start, len, fn) ; + fd_close(fd) ; + return 0 ; +} diff --git a/src/tipideed/send_file.c b/src/tipideed/send_file.c index 6d97167..bcbd1c7 100644 --- a/src/tipideed/send_file.c +++ b/src/tipideed/send_file.c @@ -2,6 +2,66 @@ #include <skalibs/sysdeps.h> +#ifdef SKALIBS_HASSENDFILE + +#include <sys/types.h> +#include <sys/sendfile.h> +#include <limits.h> + +#include <skalibs/uint64.h> +#include <skalibs/strerr.h> +#include <skalibs/tai.h> +#include <skalibs/unix-timed.h> + +#include "tipideed-internal.h" + +void init_splice_pipe (void) +{ +} + +struct sendfile_s +{ + int fd ; + off_t pos ; + uint64_t n ; +} ; + +static int s_getfd (void *p) +{ + (void)p ; + return 1 ; +} + +static int s_isnonempty (void *p) +{ + struct sendfile_s *sf = p ; + return !!sf->n ; +} + +static int s_flush (void *p) +{ + struct sendfile_s *sf = p ; + while (sf->n) + { + ssize_t r = sendfile(1, sf->fd, &sf->pos, sf->n > SSIZE_MAX ? SSIZE_MAX : sf->n) ; + if (r == -1) return 0 ; + sf->n -= r ; + } + return 1 ; +} + +void send_file_range (int fd, uint64_t offset, uint64_t n, char const *fn) +{ + struct sendfile_s sf = { .pos = offset, .n = n, .fd = fd } ; + tain deadline ; + tain_add_g(&deadline, &g.writetto) ; + if (!buffer_timed_flush_g(buffer_1, &deadline)) + strerr_diefu2sys(111, "write", " to stdout") ; + if (!timed_flush_g(&sf, &s_getfd, &s_isnonempty, &s_flush, &deadline)) + strerr_diefu3sys(111, "sendfile ", fn, " to stdout") ; +} + +#else #ifdef SKALIBS_HASSPLICE #include <skalibs/nonposix.h> @@ -10,7 +70,9 @@ #include <stdint.h> #include <unistd.h> +#include <skalibs/uint64.h> #include <skalibs/strerr.h> +#include <skalibs/tai.h> #include <skalibs/djbunix.h> #include <skalibs/unix-timed.h> @@ -53,7 +115,7 @@ static int flush (void *b) return 1 ; } -void send_file (int fd, uint64_t n, char const *fn) +void send_file_range (int fd, uint64_t offset, uint64_t n, char const *fn) { tain deadline ; struct spliceinfo_s si = { .last = 0 } ; @@ -62,7 +124,7 @@ void send_file (int fd, uint64_t n, char const *fn) strerr_diefu2sys(111, "write", " to stdout") ; while (n) { - si.n = splice(fd, 0, g.p[1], 0, n, 0) ; + si.n = splice(fd, &offset, g.p[1], 0, n, 0) ; if (si.n == -1) strerr_diefu2sys(111, "read from ", fn) ; else if (!si.n) strerr_diefu3x(111, "serve ", fn, ": file was truncated") ; else if (si.n > n) @@ -81,7 +143,10 @@ void send_file (int fd, uint64_t n, char const *fn) #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> @@ -94,12 +159,14 @@ void init_splice_pipe (void) { } -void send_file (int fd, uint64_t n, char const *fn) +void send_file_range (int fd, uint64_t offset, uint64_t n, char const *fn) { tain deadline ; struct iovec v[2] ; ssize_t r ; if (!n) goto flushit ; /* I know, I know, but do-while SUCKS */ + if (offset && lseek(fd, offset, SEEK_SET) == -1) + strerr_diefu2sys(111, "lseek on ", fn) ; fillit: buffer_wpeek(buffer_1, v) ; r = allreadv(fd, v, 2) ; @@ -120,3 +187,4 @@ void send_file (int fd, uint64_t n, char const *fn) } #endif +#endif diff --git a/src/tipideed/tipideed-internal.h b/src/tipideed/tipideed-internal.h index 8631861..4b35efa 100644 --- a/src/tipideed/tipideed-internal.h +++ b/src/tipideed/tipideed-internal.h @@ -79,28 +79,33 @@ extern void tipideed_harden (unsigned int) ; /* Responses */ -extern void response_error_early (tipidee_rql const *, unsigned int, char const *, char const *, uint32_t) ; /* set bit 0 for Connection: close */ -extern void response_error_early_and_exit (tipidee_rql const *, unsigned int, char const *, char const *) gccattr_noreturn ; -extern void exit_405_ (tipidee_rql const *, uint32_t) gccattr_noreturn ; /* set bit 0 for Allow: POST, bit 1 for preexit */ +#define response_error_early(rql, status, reason, text, options) response_error_early_plus(rql, status, reason, text, 0, 0, options) +extern void response_error_early_plus (tipidee_rql const *, unsigned int, char const *, char const *, tipidee_response_header const *, uint32_t, uint32_t) ; +#define response_error_early_and_exit(rql, status, reason, text) response_error_early_plus_and_exit(rql, status, reason, (text), 0, 0) +extern void response_error_early_plus_and_exit (tipidee_rql const *, unsigned int, char const *, char const *, tipidee_response_header const *, uint32_t) gccattr_noreturn ; +extern void eexit_405 (tipidee_rql const *) gccattr_noreturn ; #define eexit_400(r, s) response_error_early_and_exit(r, 400, "Bad Request", s) -#define eexit_405(r) exit_405_((r), 3) #define eexit_408(r) response_error_early_and_exit((r), 408, "Request Timeout", 0) #define eexit_413(r, s) response_error_early_and_exit(r, 413, "Request Entity Too Large", s) #define eexit_501(r, s) response_error_early_and_exit(r, 501, "Not Implemented", s) +#define response_error(rql, docroot, status, options) response_error_plus(rql, docroot, status, 0, 0, options) +extern void response_error_plus (tipidee_rql const *, char const *, unsigned int, tipidee_response_header const *, uint32_t, uint32_t) ; /* set bit 0 for Connection: close */ +#define response_error_and_exit(rql, docroot, status) response_error_plus_and_exit(rql, docroot, (status), 0, 0) +extern void response_error_plus_and_exit (tipidee_rql const *, char const *, unsigned int, tipidee_response_header const *, uint32_t) gccattr_noreturn ; +#define response_error_and_die(rql, e, docroot, status, v, n, options) response_error_plus_and_die(rql, e, docroot, status, 0, 0, v, n, options) +extern void response_error_plus_and_die (tipidee_rql const *, int, char const *, unsigned int, tipidee_response_header const *, uint32_t, char const *const *, unsigned int, uint32_t) gccattr_noreturn ; /* set bit 0 for diesys */ -extern void response_error (tipidee_rql const *, char const *, unsigned int, uint32_t) ; /* set bit 0 for Connection: close */ -extern void response_error_and_exit (tipidee_rql const *, char const *, unsigned int) gccattr_noreturn ; -extern void response_error_and_die (tipidee_rql const *, int, char const *, unsigned int, char const *const *, unsigned int, uint32_t) gccattr_noreturn ; /* set bit 0 for diesys */ - +extern void exit_405 (tipidee_rql const *, char const *, uint32_t) gccattr_noreturn ; #define exit_400(r, d) response_error_and_exit(r, (d), 400) -#define exit_405(r) exit_405_((r), 0) #define exit_408(r, d) response_error_and_exit(r, (d), 408) #define exit_413(r, d) response_error_and_exit(r, (d), 413) #define exit_501(r, d) response_error_and_exit(r, (d), 501) extern void respond_30x (tipidee_rql const *, tipidee_redirection const *) ; +extern void respond_416 (tipidee_rql const *, char const *, uint64_t) ; + #define respond_403(r, d) response_error(r, (d), 403, 0) #define respond_404(r, d) response_error(r, (d), 404, 0) #define respond_414(r, d) response_error(r, (d), 414, 0) @@ -126,12 +131,14 @@ extern int respond_options (tipidee_rql const *, uint32_t) ; /* send_file */ extern void init_splice_pipe (void) ; -extern void send_file (int, uint64_t, char const *) ; +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) /* regular */ extern int respond_regular (tipidee_rql const *, char const *, char const *, struct stat const *, tipidee_resattr const *) ; +extern int respond_partial (tipidee_rql const *, char const *, char const *, struct stat const *, uint64_t, uint64_t, tipidee_resattr const *) ; extern int respond_304 (tipidee_rql const *, char const *, struct stat const *) ; diff --git a/src/tipideed/tipideed.c b/src/tipideed/tipideed.c index 8323db1..69280f6 100644 --- a/src/tipideed/tipideed.c +++ b/src/tipideed/tipideed.c @@ -11,6 +11,7 @@ #include <skalibs/posixplz.h> #include <skalibs/env.h> #include <skalibs/uint16.h> +#include <skalibs/uint64.h> #include <skalibs/types.h> #include <skalibs/bytestr.h> #include <skalibs/sgetopt.h> @@ -286,7 +287,7 @@ static inline int serve (tipidee_rql *rql, char const *docroot, char *uribuf, ti if (!ra.flags & TIPIDEE_RA_FLAG_CGI) { if (infopath) { respond_404(rql, docroot) ; return 0 ; } - if (rql->m == TIPIDEE_METHOD_POST) exit_405(rql) ; + if (rql->m == TIPIDEE_METHOD_POST) exit_405(rql, docroot, 0) ; } if (rql->m == TIPIDEE_METHOD_OPTIONS) @@ -306,6 +307,19 @@ static inline int serve (tipidee_rql *rql, char const *docroot, char *uribuf, ti && tain_less(&actual, &wanted)) return respond_304(rql, fn, &st) ; } + + if (rql->m == TIPIDEE_METHOD_GET) + { + infopath = tipidee_headers_search(hdr, "Range") ; + if (infopath) + { + uint64_t start, len ; + int r = tipidee_util_parse_range(infopath, st.st_size, &start, &len) ; + if (r > 0) return respond_partial(rql, docroot, fn, &st, start, len, &ra) ; + if (r < 0) { respond_416(rql, docroot, st.st_size) ; return 0 ; } + } + } + return respond_regular(rql, docroot, fn, &st, &ra) ; } |