X-Git-Url: https://git.plutz.net/?p=confetti;a=blobdiff_plain;f=cgilite%2Ffile.sh;h=04a8ef617c9f755a4dcb7c3cf3adeeca69683f27;hp=8c2e52ffc8910781575126547c067bbd609932bd;hb=5b8d1b752ede7a6dc4250620ca58971447570a76;hpb=597759dd09bc6e18e8b0a124c0a5dc55cc78e261 diff --git a/cgilite/file.sh b/cgilite/file.sh new file mode 100755 index 0000000..04a8ef6 --- /dev/null +++ b/cgilite/file.sh @@ -0,0 +1,126 @@ +#!/bin/sh + +# Copyright 2016 - 2019 Paul Hänsch +# +# This file is part of cgilite. +# +# cgilite is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cgilite 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with cgilite. If not, see . + +[ -n "$include_fileserve" ] && return 0 +include_fileserve="$0" + +file_type(){ + case ${1##*.} in + html|html) printf 'text/html';; + css) printf 'text/css';; + js) printf 'text/javascript';; + txt) printf 'text/plain';; + sh) printf 'text/shellscript';; + jpg|jpeg) printf 'image/jpeg';; + png) printf 'image/png';; + svg) printf 'image/svg+xml';; + gif) printf 'image/gif';; + webm) printf 'video/webm';; + mp4|m4v) printf 'video/mp4';; + m4a) printf 'audio/mp4';; + ogg) printf 'audio/ogg';; + xml) printf 'application/xml';; + m3u8) printf 'application/x-mpegURL';; + ts) printf 'video/MP2T';; + mpd) printf 'application/dash+xml';; + m4s) printf 'video/iso.segment';; + *) printf 'application/octet-stream';; + esac +} + +FILE(){ + local file file_size file_date http_date cachedate range mime + file="$1" mime="$2" + + if ! [ -f "$file" ]; then + printf 'Content-Length: 0\r\nStatus: 404 Not Found\r\n\r\n' + exit 0 + elif ! [ -r "$file" ]; then + printf 'Content-Length: 0\r\nStatus: 403 Forbidden\r\n\r\n' + exit 0 + fi + + file_size="$(stat -Lc %s "$file")" + file_date="$(stat -Lc %Y "$file")" + http_date="$(date -uRd @$file_date)" + http_date="${http_date%+0000}GMT" + cachedate="$( + # Parse the allowable date formats from Section 3.3.1 of + # https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html + HEADER If-Modified-Since \ + | sed -E 's;^[^ ]+, ([0-9]{2}) (...) ([0-9]{4}) (..:..:..) GMT$;\3-\2-\1 \4;; + s;^[^ ]+, ([0-9]{2})-(...)-([789][0-9]) (..:..:..) GMT$;19\3-\2-\1 \4;; + s;^[^ ]+, ([0-9]{2})-(...)-([0-6][0-9]) (..:..:..) GMT$;20\3-\2-\1 \4;; + s;^[^ ]+ (...) ([0-9]{2}) (..:..:..) ([0-9]{4})$;\4-\1-\2 \3;; + s;^[^ ]+ (...) ([0-9]) (..:..:..) ([0-9]{4})$;\4-\1-\2 \3;; + s;Jan;01;; s;Feb;02;; s;Mar;03;; s;Apr;04;; s;May;05;; s;Jun;06;; + s;Jul;07;; s;Aug;08;; s;Sep;09;; s;Oct;10;; s;Nov;11;; s;Dec;12;;' \ + | xargs -r0 date +%s -ud 2>&- + )" + + range="$(HEADER Range |sed -nE 's;^bytes=([0-9]+-[0-9]*|-[0-9]+)$;\1;p;q;')" + case "$range" in + *-) range="${range}$((file_size - 1))";; + -*) [ ${range#-} -le $file_size ] \ + && range="$((file_size - ${range#-}))-$((file_size - 1))" \ + || range="0-$((file_size - 1))";; + *-*) [ ${range#*-} -ge $file_size ] \ + && range="${range%-*}-$((file_size - 1))";; + esac + + if [ "$file_date" -lt "$cachedate" ] 2>&-; then + printf '%s: %s\r\n' \ + Status '304 Not Modified' \ + Content-Length 0 \ + Last-Modified "$http_date" + printf '\r\n' + + elif [ -z "$range" ]; then + printf '%s: %s\r\n' \ + Status "200 OK" \ + Accept-Ranges bytes \ + Last-Modified "$http_date" \ + Content-Type "${mime:-$(file_type "$file")}" \ + Content-Length $file_size + printf '\r\n' + + [ "$REQUEST_METHOD" != HEAD ] && cat "$file" + + elif [ "${range%-*}" -le "${range#*-}" ]; then + printf '%s: %s\r\n' \ + Status "206 Partial Content" \ + Accept-Ranges bytes \ + Last-Modified "$http_date" \ + Content-Type "${mime:-$(file_type "$file")}" \ + Content-Range "bytes ${range}/${file_size}" \ + Content-Length "$((${range#*-} - ${range%-*} + 1))" + printf '\r\n' + + [ "$REQUEST_METHOD" != HEAD ] \ + && tail -c+$((${range%-*} + 1)) "$file" \ + | head -c "$((${range#*-} - ${range%-*} + 1))" + + elif [ "${range%-*}" -gt "${range#*-}" ]; then + printf '%s: %s\r\n' \ + Status "216 Range Not Satisfiable" \ + Content-Length 0 \ + Content-Range \*/${file_size} + printf '\r\n' + fi +}