diff --git a/bashttpd b/bashttpd index 3f6090f..110b33e 100755 --- a/bashttpd +++ b/bashttpd @@ -7,7 +7,9 @@ # Original author: Avleen Vig, 2012 # Reworked by: Josh Cartwright, 2012 -warn() { echo "WARNING: $@" >&2; } +warn() { + echo "WARNING: $@" >&2 +} [ -r bashttpd.conf ] || { cat >bashttpd.conf <<'EOF' @@ -71,6 +73,10 @@ warn() { echo "WARNING: $@" >&2; } # When a client's requested URI has the word 'root' in it, serve up # a directory listing of / # +# on_uri_match '^/downloads+/*(.*)' serve_dir_or_file_from "/path/to/Downloads" +# +# When requesting /downloads show tree view of dir, when selecting file delivering it +# # DOCROOT=/var/www/html # on_uri_match '/(.*)' serve_dir_or_file_from "$DOCROOT" # When any URI request is made, attempt to serve a directory listing @@ -100,140 +106,179 @@ unconditionally serve_static_string 'Hello, world! You can configure bashttpd b # is invoked with the arguments '/say_hello_to/Josh' 'Josh', # (${BASH_REMATCH[0]} is always the full match) EOF - warn "Created bashttpd.conf using defaults. Please review it/configure before running bashttpd again." - exit 1 + + warn "Created bashttpd.conf using defaults. Please review it/configure before running bashttpd again." + exit 1 } -recv() { echo "< $@" >&2; } -send() { echo "> $@" >&2; - printf '%s\r\n' "$*"; } +recv() { + echo "< $@" >&2 +} + +send() { + echo "> $@" >&2 + printf '%s\r\n' "$*" +} [[ $UID = 0 ]] && warn "It is not recommended to run bashttpd as root." DATE=$(date +"%a, %d %b %Y %H:%M:%S %Z") + declare -a RESPONSE_HEADERS=( - "Date: $DATE" - "Expires: $DATE" - "Server: Slash Bin Slash Bash" + "Date: $DATE" + "Expires: $DATE" + "Server: Slash Bin Slash Bash" ) add_response_header() { - RESPONSE_HEADERS+=("$1: $2") + RESPONSE_HEADERS+=("$1: $2") } declare -a HTTP_RESPONSE=( - [200]="OK" - [400]="Bad Request" - [403]="Forbidden" - [404]="Not Found" - [405]="Method Not Allowed" - [500]="Internal Server Error" + [200]="OK" + [400]="Bad Request" + [403]="Forbidden" + [404]="Not Found" + [405]="Method Not Allowed" + [500]="Internal Server Error" ) send_response() { - local code=$1 - send "HTTP/1.0 $1 ${HTTP_RESPONSE[$1]}" - for i in "${RESPONSE_HEADERS[@]}"; do - send "$i" - done - send - while read -r line; do - send "$line" - done + local code=$1 + send "HTTP/1.1 $1 ${HTTP_RESPONSE[$1]}" + for i in "${RESPONSE_HEADERS[@]}" + do + send "$i" + done + send + + while read -r line + do + send "$line" + done +} + +send_response_ok_exit() { + send_response 200 + exit 0 } -send_response_ok_exit() { send_response 200; exit 0; } +send_fileresponse() { + local code=$1 + send "HTTP/1.1 $1 ${HTTP_RESPONSE[$1]}" + for i in "${RESPONSE_HEADERS[@]}" + do + send "$i" + done + send +} + +send_fileresponse_ok_exit() { + send_fileresponse 200 + cat "$1" + exit 0 +} fail_with() { - send_response "$1" <<< "$1 ${HTTP_RESPONSE[$1]}" - exit 1 + send_response "$1" <<< "$1 ${HTTP_RESPONSE[$1]}" + exit 1 } serve_file() { - local file=$1 + local file=$1 - read -r CONTENT_TYPE < <(file -b --mime-type "$file") && \ - add_response_header "Content-Type" "$CONTENT_TYPE" - read -r CONTENT_LENGTH < <(stat -c'%s' "$file") && \ - add_response_header "Content-Length" "$CONTENT_LENGTH" + read -r CONTENT_TYPE < <(file -b --mime-type "$file") && \ + add_response_header "Content-Type" "$CONTENT_TYPE" + read -r CONTENT_LENGTH < <(stat -c'%s' "$file") && \ + add_response_header "Content-Length" "$CONTENT_LENGTH" - send_response_ok_exit < "$file" + send_fileresponse_ok_exit "$file" } -serve_dir_with_tree() -{ - local dir="$1" tree_vers tree_opts basehref x +serve_dir_with_tree() { + local dir="${1%/}" + local tree_vers="" + local tree_opts="" + local basehref="${2%/}" + local x="" + + add_response_header "Content-Type" "text/html; charset=UTF-8" - add_response_header "Content-Type" "text/html" + # The --du option was added in 1.6.0. + read x tree_vers x < <(tree --version) + if [[ $tree_vers == v1.6* ]] + then + send_response_ok_exit < \ + <(tree -C -H "$basehref" --du -L 1 -D "$dir") - # The --du option was added in 1.6.0. - read x tree_vers x < <(tree --version) - [[ $tree_vers == v1.6* ]] && tree_opts="--du" + else + send_response_ok_exit < \ + <(tree -C -H "$basehref" -L 1 -D "$dir") - send_response_ok_exit < \ - <(tree -H "$2" -L 1 "$tree_opts" -D "$dir") + fi } -serve_dir_with_ls() -{ - local dir=$1 +serve_dir_with_ls() { + local dir=$1 - add_response_header "Content-Type" "text/plain" + add_response_header "Content-Type" "text/plain; charset=UTF-8" - send_response_ok_exit < \ - <(ls -la "$dir") + send_response_ok_exit < \ + <(ls -la "$dir") } serve_dir() { - local dir=$1 + local dir=$1 - # If `tree` is installed, use that for pretty output. - which tree &>/dev/null && \ - serve_dir_with_tree "$@" + # If `tree` is installed, use that for pretty output. + which tree &>/dev/null && \ + serve_dir_with_tree "$@" - serve_dir_with_ls "$@" + serve_dir_with_ls "$@" - fail_with 500 + fail_with 500 } serve_dir_or_file_from() { - local URL_PATH=$1/$3 - shift - - # sanitize URL_PATH - URL_PATH=${URL_PATH//[^a-zA-Z0-9_~\-\.\/]/} - [[ $URL_PATH == *..* ]] && fail_with 400 - - # Serve index file if exists in requested directory - [[ -d $URL_PATH && -f $URL_PATH/index.html && -r $URL_PATH/index.html ]] && \ - URL_PATH="$URL_PATH/index.html" - - if [[ -f $URL_PATH ]]; then - [[ -r $URL_PATH ]] && \ - serve_file "$URL_PATH" "$@" || fail_with 403 - elif [[ -d $URL_PATH ]]; then - [[ -x $URL_PATH ]] && \ - serve_dir "$URL_PATH" "$@" || fail_with 403 - fi - - fail_with 404 + local URL_PATH=$1/$3 + shift + + # sanitize URL_PATH + URL_PATH=${URL_PATH//[^a-zA-Z0-9_~\-\.\/]/} + [[ $URL_PATH == *..* ]] && fail_with 400 + + # Serve index file if exists in requested directory + [[ -d $URL_PATH && -f $URL_PATH/index.html && -r $URL_PATH/index.html ]] && \ + URL_PATH="$URL_PATH/index.html" + + if [[ -f $URL_PATH ]] + then + [[ -r $URL_PATH ]] && \ + serve_file "$URL_PATH" "$@" || fail_with 403 + elif [[ -d $URL_PATH ]] + then + [[ -x $URL_PATH ]] && \ + serve_dir "$URL_PATH" "$@" || fail_with 403 + fi + + fail_with 404 } serve_static_string() { - add_response_header "Content-Type" "text/plain" - send_response_ok_exit <<< "$1" + add_response_header "Content-Type" "text/plain; charset=UTF-8" + send_response_ok_exit <<< "$1" } on_uri_match() { - local regex=$1 - shift + local regex=$1 + shift - [[ $REQUEST_URI =~ $regex ]] && \ - "$@" "${BASH_REMATCH[@]}" + [[ $REQUEST_URI =~ $regex ]] && \ + "$@" "${BASH_REMATCH[@]}" } unconditionally() { - "$@" "$REQUEST_URI" + "$@" "$REQUEST_URI" } # Request-Line HTTP RFC 2616 $5.1 @@ -248,21 +293,22 @@ read -r REQUEST_METHOD REQUEST_URI REQUEST_HTTP_VERSION <<<"$line" [ -n "$REQUEST_METHOD" ] && \ [ -n "$REQUEST_URI" ] && \ [ -n "$REQUEST_HTTP_VERSION" ] \ - || fail_with 400 + || fail_with 400 # Only GET is supported at this time [ "$REQUEST_METHOD" = "GET" ] || fail_with 405 declare -a REQUEST_HEADERS -while read -r line; do - line=${line%%$'\r'} - recv "$line" +while read -r line +do + line=${line%%$'\r'} + recv "$line" - # If we've reached the end of the headers, break. - [ -z "$line" ] && break + # If we've reached the end of the headers, break. + [ -z "$line" ] && break - REQUEST_HEADERS+=("$line") + REQUEST_HEADERS+=("$line") done source bashttpd.conf