From cf9ee77a7cb6f94773d6c7bc85a8363c5ddf86be Mon Sep 17 00:00:00 2001 From: single-right-quote <34298117+single-right-quote@users.noreply.github.com> Date: Thu, 10 Sep 2020 22:03:12 +0000 Subject: [PATCH] reorganize configuration of resource- and domain-specific headers MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit in doing so, move so-called ”extra headers” code to a separate script furthermore, slightly adjust the logic of http-error-response.execline headers are now specified on a per-header basis, one file per header --- .../binaries/http-error-response.execline | 76 ++++++++++------ .../binaries/http-get-extra-headers.execline | 87 +++++++++++++++++++ visible-to-httpd/binaries/httpd.execline | 8 +- 3 files changed, 138 insertions(+), 33 deletions(-) create mode 100755 visible-to-httpd/binaries/http-get-extra-headers.execline diff --git a/visible-to-httpd/binaries/http-error-response.execline b/visible-to-httpd/binaries/http-error-response.execline index 0fae198..af58267 100755 --- a/visible-to-httpd/binaries/http-error-response.execline +++ b/visible-to-httpd/binaries/http-error-response.execline @@ -14,44 +14,68 @@ foreground { log.execline "fatal: ??"${1}"??: "${3} } -# (why does `hoc -e` not work?) -backtick -i -n Content-Length { - backtick -i -n message_length { - pipeline { s6-echo -n -- ${2} } - wc -c - } - importas -i -u message_length message_length - - pipeline { s6-echo -- ${message_length}"*2 + 288" } - hoc -} - backtick -i -n Date { date -u "+%a, %d %b %Y %T GMT" } - -backtick -i -n extra_headers { cat configuration/default_headers/default } +backtick -i -n extra_headers { http-get-extra-headers.execline ${1} } multisubstitute { -# TODO: importas -i -u Content-Length Content-Length importas -i -u Date Date importas -i -u extra_headers extra_headers - importas -D "invalid default hostname which should not match any hostname directory" hostname http_header_parse_Host + importas -D "?? no hostname ??" hostname http_header_parse_Host } if { - s6-echo -n -- "HTTP/1.1 "${1}" "${2}"\r -Content-Type: application/xhtml+xml; charset=utf-8\r + # if there is an error response page for this status code: + ifelse { + s6-test -d configuration/error_response_pages/${hostname}/${1} + -a -r configuration/error_response_pages/${hostname}/${1} + } + { + cd configuration/error_response_pages/${hostname}/${1} + + backtick -D "application/xhtml+xml; charset=utf-8" -n Content-Type { cat Content-Type } + backtick -i -n Content-Length { wc -c message_body } + + multisubstitute { + importas -i -u Content-Type Content-Type + importas -i -u Content-Length Content-Length + } + + if { + s6-echo -n -- "HTTP/1.1 "${1}" "${2}"\r +Content-Type: "${Content-Type}"\r +Content-Length: "${Content-Length}"\r Date: "${Date}"\r "${extra_headers}"\r \r " -} - -if { - ifelse { s6-test -r configuration/error_response_pages/${hostname}/${1} } - { - # TODO: support `Content-Types`?? and `Content-Length` - cat configuration/error_response_pages/${hostname}/${1} + } + + cat message_body } + # default: no error status page on file; use a hardcoded default + + # (why does `hoc -e` not work?) + backtick -i -n Content-Length { + backtick -i -n message_length { + pipeline { s6-echo -n -- ${2} } + wc -c + } + importas -i -u message_length message_length + + pipeline { s6-echo -- ${message_length}"*2 + 288" } + hoc + } + importas -i -u Content-Length Content-Length + + if { + s6-echo -n -- "HTTP/1.1 "${1}" "${2}"\r +Content-Type: application/xhtml+xml; charset=utf-8\r +Content-Length: "${Content-Length}"\r +Date: "${Date}"\r +"${extra_headers}"\r +\r +" + } s6-echo -n -- " @@ -70,6 +94,6 @@ if { " } + # hack: write(3p) is unsafe -# s6-sleep -m 512 diff --git a/visible-to-httpd/binaries/http-get-extra-headers.execline b/visible-to-httpd/binaries/http-get-extra-headers.execline new file mode 100755 index 0000000..39cd3c2 --- /dev/null +++ b/visible-to-httpd/binaries/http-get-extra-headers.execline @@ -0,0 +1,87 @@ +#!/binaries/execlineb -WS0 +# +# http-get-particular-headers.execline [status-code] +# +# sufficiently annoyingly complex resource-specific HTTP header logic that +# gets reused a lot +# +# headers are specified in a header directory, which has a file for each +# header you wish to send; this imposes structure with almost no parsing at all +# +# exploiting the filesystem as more or less a map (as opposed to the trivial +# implementation: using a single file containing the desired headers) means the +# user need not bother with editing files containing `\r\n`s +# (however, it is almost certainly considerably more annoying to use) +# +# TODO: ? status code overrides support?? +# TODO: write a GUI?? with like, `yad(1)` or something??? idk + +multisubstitute { + importas -i hostname http_header_parse_Host + importas -i resource resource +} + +foreground { log.execline "resource: "${resource} } + +backtick -n header-directory { + # for status code pages specifically + ifelse { + s6-test \${#} != 0 + -a -d configuration/error_response_pages/${1}/headers + -a -r configuration/error_response_pages/${1}/headers + } + { + s6-echo -n -- configuration/error_response_pages/${1}/headers + } + + # otherwise: prioritize header specifications in order of specificity: + # by resource, then by hostname, then the fallback (and finally nothing) + ifelse { + s6-test -d configuration/overrides/${resource}/headers + -a -r configuration/overrides/${resource}/headers + } + { + s6-echo -n -- configuration/overrides/${resource}/headers + } + ifelse { + s6-test -d configuration/default_headers/${hostname} + -a -r configuration/default_headers/${hostname} + } + { + s6-echo -n -- configuration/default_headers/${hostname} + } + # the preferred name for a DNS lookup should begin with an alphanumeric, + # so “-fallback”, since it begins with a hyphen, should never conflict + # with a hostname anybody would ever actually use + ifelse { + s6-test -d configuration/default_headers/-fallback + -a -r configuration/default_headers/-fallback + } + { + s6-echo -n -- configuration/default_headers/-fallback + } + exit 1 +} + +# (we output nothing if there is no applicable header directory, which is fine) +if -t { s6-test -v header-directory } + importas -i -u header-directory header-directory + + foreground { log.execline "header-directory: "${header-directory} } + + cd ${header-directory} + elglob -0 -s header_names * + + # we’ll strip out `\r`s and `\n`s from filenames and file contents, in + # case the configuration should ever be made in a mischevious way + forx header_name { ${header_names} } + importas -i -u header_name header_name + if { + pipeline { s6-echo -n -- ${header_name}": " } + tr -d "\r\n" + } + if { + pipeline { cat ${header_name} } + tr -d "\r\n" + } + # do make sure not to forget to supply the final newline diff --git a/visible-to-httpd/binaries/httpd.execline b/visible-to-httpd/binaries/httpd.execline index 823dc43..187f67b 100755 --- a/visible-to-httpd/binaries/httpd.execline +++ b/visible-to-httpd/binaries/httpd.execline @@ -216,13 +216,7 @@ if -X -n -t { # Security Policy; for the latter, consider HTTP 301 redirects # # be warned!! we do not validate these overrides! - backtick -i -n extra_headers { - ifelse { s6-test -r configuration/default_headers/override/${resource} } - { - cat configuration/default_headers/override/${resource} - } - cat configuration/default_headers/default - } + backtick -i -n extra_headers { http-get-extra-headers.execline } backtick -D "200 ok" -n status_code_and_message { if { s6-test -r configuration/overrides/${resource}/status_code } -- 2.47.3