commit 97e32564b3acd3954654d49e895e0bc203ca7887 Author: numzero Date: Sat Mar 25 00:58:38 2023 +0300 An HTTP server in BASH diff --git a/server.sh b/server.sh new file mode 100755 index 0000000..f6cceb9 --- /dev/null +++ b/server.sh @@ -0,0 +1,112 @@ +#!/bin/bash + +set -e + +ADDR=127.0.0.1 +PORT=1234 +ROOT="$(realpath "${1:-$PWD}")" + +LF=$'\n' + +tmpdir="$(mktemp -d)" +trap 'rm -r "$tmpdir"' EXIT +trap 'exit' INT + +stdhead() { + code="$1" + head="$2" + echo "HTTP/1.0 $code $head" + echo "Content-Type: text/plain" + echo "Connection: close" +} + +error() { + code="$1" + head="$2" + message="$3" + echo "HTTP/1.0 $code $head" + echo "Content-Type: text/plain" + echo "Connection: close" + echo + echo "$code $head" + echo + echo "$message" + exit +} + +handle() { + connection_id="$1" + response_pipe="$tmpdir/response-$connection_id.pipe" + event_pipe="$tmpdir/event-$connection_id.pipe" + mkfifo -m 0600 "$response_pipe" "$event_pipe" + gnetcat -c -l "$ADDR" -p "$PORT" < "$response_pipe" | sed -E -u 's/\r$//' | ( + read method url version || { + echo "Connection broken" > "$event_pipe" + exit + } + cat >& /dev/null & # read but ignore headers and whatever else + echo "$method $url" > "$event_pipe" + if ! [ "$version" = "HTTP/1.0" -o "$version" = "HTTP/1.1" ]; then + error 505 'HTTP Version Not Supported' "Version $version is not supported.${LF}Only 1.0 is really supported." + fi + if ! [ "$method" = "GET" ]; then + error 501 'Not Implemented' "Method $method is not implemented.${LF}Only GET is supported." + fi + if ! echo "$url" | grep -q '^/'; then + error 400 'Bad Request' "URL must be host-relative" + fi + if echo "$url" | grep -q '/\.'; then + error 403 'Forbidden' "Dot-files are forbidden" + fi + target="$ROOT$url" + name=$(basename "$url") + if [ -f "$target" ]; then + echo "HTTP/1.0 200 OK" + echo "Connection: close" + echo + cat "$target" + exit + fi + if [ -d "$target" ]; then + if ! echo "$url" | grep -q '/$'; then + echo "HTTP/1.0 307 Temporary Redirect" + echo "Connection: close" + echo "Location: $url/" + exit + fi + ls "$target" 2>/dev/null | { + echo "HTTP/1.0 200 OK" + echo "Content-Type: text/html; charset=utf-8" + echo "Connection: close" + echo + echo "" + echo "Directory listing for $url" + echo "

$name

" + echo "

up" + echo "

" + } || error 403 'Forbidden' "Permission denied for $url" + exit + fi + error 404 'Not Found' "Requested file $url is not found" + ) > "$response_pipe" & + rm "$response_pipe" + cat "$event_pipe" + rm "$event_pipe" +} + +echo "Serving $ROOT" + +for((i=0;;i++)); do + handle $i +done