Author: Adam <git@apiote.xyz>
rename server
| 0
diff --git a/server2.sh b/server2.sh deleted file mode 100755 index ad646f5371ce9db4eac1b6f5fdafc1ea7de562ef..0000000000000000000000000000000000000000 --- a/server2.sh +++ /dev/null @@ -1,391 +0,0 @@ -#!/bin/sh -# -# Based on server by Upper Stream (c) 2017, MIT license -# (https://gist.github.com/upperstream/b9b77429bc024541b7605aea76086ad8) - -. start.sh -. get.sh -. password.sh - -set -e - -exec >>~/toymaker.log -exec 2>&1 - -# program name -program=${0##*/} - -usage() { - cat <<-EOF - Usage: - - $program [-p port] [docroot] - $program -h - - -p port : specify listening port number; defaults to 1313 - -h : print this help and quit - docroot : specify document root directory; defaults to the current directory -EOF -} - -# default listening port -port=1313 - -# Configure in accordance with your environment -nc=$(command -v nc) || true - -# todo mktmp -pid_file=/tmp/$program.pid - -slashes() { - printf "%s" "$1" | sed 's|[^/]||g' | wc -m -} - -field() { - echo "$1" | cut -d '/' -f "$2" -} -makefifo() { - name="$1" - mkfifo -m 0600 "/tmp/$name.fifo" && echo "/tmp/$name.fifo" -} - -templateMessage() { - message=$1 - templateName=$2 - contentType=$3 - case $contentType in - text/plain) - ext='txt' - ;; - text/html|\*|\*/\*|'') - ext='html' - ;; - image/\*|image/svg) - ext='svg' - ;; - *) - return 1 - ;; - esac - [ -f "templates/$templateName.$ext" ] || return 1 - #shellcheck disable=SC1090 - . "templates/$templateName.$ext" - template "$message" -} - -respond200() { - page="$1" - contentType="$2" - [ -z "$contentType" ] && contentType='text/html' - - # { cat "$fifo2" > /dev/null; printf ""; } > "$fifo1" & - - printf "HTTP/1.1 200 OK\r\nContent-Length: %s\r\nContent-Type: %s\r\n\r\n%s\r\n" "${#page}" "$contentType" "$page" > "$fifo1" - - # printf "%s\r\n" "$page" >> "$fifo1" - - # sleep 1; echo "end" > "$fifo2" - printf "200 %s" "${#page}" -} - -respond200file() { - filePath="$1" - contentType="$2" - - { cat "$fifo2" > /dev/null; printf ""; } > "$fifo1" & - - printf "HTTP/1.1 200 OK\r\nContent-Length: %s\r\nContent-Type: %s\r\n\r\n" "${#page}" "$contentType" >> "$fifo1" - - cat "$filePath" >> "$fifo1" - - sleep 1; echo "end" > "$fifo2" - printf "200 %s" "${#page}" -} - -respond202() { - toy=$1 - item=$2 - message=$3 - contentType="$4" - [ -z "$contentType" ] && contentType='text/html' - printf "HTTP/1.1 202 Accepted\r\nContent-Length: %s\r\nLocation: /toys/%s/%s\r\n\r\n%s\r\n" "${#message}" "$toy" "$item" "$message" > "$fifo1" - printf "202 -" -} - -respond401() { - message="" - printf "HTTP/1.1 401 Unauthorized\r\nContent-Length: %s\r\n\r\n%s\r\n" "${#message}" "$message" > "$fifo1" - printf "401 -" -} - -respond403() { - message="" - printf "HTTP/1.1 403 Forbidden\r\nContent-Length: %s\r\n\r\n%s\r\n" "${#message}" "$message" > "$fifo1" - printf "403 -" -} - -respond404() { - path=$1 - # todo html - message="$path not found" - printf "HTTP/1.1 404 Not Found\r\nContent-Length: %s\r\n\r\n%s\r\n" "${#message}" "$message" > "$fifo1" - printf "404 -" -} - -respond405() { - method=$1 - # todo html - message="$method unsupported" - printf "HTTP/1.1 405 Method Not Allowed\r\nContent-Length: %s\r\n\r\n%s\r\n" "${#message}" "$message" > "$fifo1" - printf "405 -" -} - -respond406() { - contentType=$1 - # todo html - message="$contentType unsupported" - printf "HTTP/1.1 406 Not Acceptable\r\nContent-Length: %s\r\n\r\n%s\r\n" "${#message}" "$message" > "$fifo1" - printf "406 -" -} - -respond500() { - # todo html - message="Server error: $1" - printf "HTTP/1.1 500 Internal Server Error\r\nContent-Length: %s\r\n\r\n%s\r\n" "${#message}" "$message" > "$fifo1" - printf "500 -" -} - -check_auth() { - authorization=$1 - stored=$(cat password) - if [ -z "$stored" ] || [ -z "$authorization" ] - then - respond401 - return 1 - fi - if ! printf '%s' "$authorization" | verify "$stored" - then - respond403 - return 1 - fi -} - -execute() { - method="$1" - path="$2" - authorization="$3" - accept="$4" - _toys=$(field "$path" 2) - if [ "$_toys" != 'toys' ] - then - respond404 "$path" - return 1 - fi - - case $method in - GET) - if [ "$(slashes "$path")" -eq 2 ] - then - toy=$(field "$path" 3) - if [ "$toy" = '' ] - then - if page=$(templateMessage "$(list_toys)" 'toys' 'text/html') - then - respond200 "$page" - else - respond406 "$accept" - fi - else - r=$(list_items "$toy") - status=$(echo "$r" | cut -d ';' -f 1) - if [ "$status" = 'n' ] - then - respond404 "$path" - elif page=$(templateMessage "$r" 'items' 'text/html') - then - respond200 "$page" - else - respond406 "$accept" - fi - fi - elif [ "$(slashes "$path")" -eq 3 ] - then - toy=$(field "$path" 3) - item=$(field "$path" 4) - if [ -z "$item" ] - then - respond404 "$path" - return 1 - fi - r=$(show_item "$toy" "$item") - status=$(echo "$r" | cut -d ';' -f 1) - if [ "$status" = 'n' ] - then - respond404 "$path" - elif page=$(templateMessage "$r" 'item' "$accept") - then - if [ "$status" = 'r' ] - then - respond202 "$toy" "$item" "$page" "$accept" - else - respond200 "$page" "$accept" - fi - else - respond406 "$accept" - fi - elif [ "$(slashes "$path")" -eq 4 ] - then - toy=$(field "$path" 3) - item=$(field "$path" 4) - artifactName=$(field "$path" 5) - r=$(get_artifact "$toy" "$item" "$artifactName") - status=$(echo "$r" | cut -d ';' -f 1) - if [ "$status" = 'n' ] - then - respond404 "$path" - else - contentType=$(echo "$r" | cut -d ';' -f 2) - filePath=$(echo "$r" | cut -d ';' -f 3) - respond200file "$filePath" "$contentType" - fi - else - respond404 "$path" - fi - ;; - POST) - check_auth "$authorization" || return - - toy=$(field "$path" 3) - if [ "$(slashes "$path")" -ne 3 ] || [ -n "$(field "$path" 4)" ] - then - respond404 "$path" - fi - - item=$(start_item "$toy") - if [ $! -gt 0 ] - then - respond500 "error while creating $toy" - else - respond202 "$toy" "$item" - fi - - # todo delete old items, leave 1 stable, 1 errored - ;; - DELETE) - check_auth "$authorization" || return - - if [ "$(slashes "$path")" -eq 3 ] - then - toy=$(field "$path" 3) - item=$(field "$path" 4) - if stop_item "$toy" "$item" - then - respond200 "" - else - respond500 "error while stopping $toy/$item" - fi - else - respond404 "$path" - fi - ;; - *) - respond405 "$method" - ;; - esac -} - -cleanup() { - rm "$fifo1" "$fifo2" "$pid_file" -} - -interrupt() { - cleanup - exit 0 -} - -parse() { - cr=$(printf "\r") - while read -r line; do - line="${line%"$cr"}" - case "$line" in - GET*|POST*|PUT*|DELETE*|HEAD*|OPTIONS*|TRACE*) - # shellcheck disable=SC2086 - set -- $line - method=$1 - path=$2 - ;; - Host:*) - host=${line#Host: } - ;; - Accept:*) - accept=${line#Accept: } - ;; - Authorization:*) - authorization=${line#Authorization: } - ;; - *:*) - ;; - "") - date=$(date +'%d/%m/%Y:%H:%M:%S %z') - status_length=$(execute "$method" "$path" "$authorization" "$accept") - printf "%s %s \"http://%s%s\" %s\n" "$date" "$method" "$host" "$path" "$status_length" - trap - INT - exit - ;; - *) - ;; - esac - done -} - -# dependency verification -for cmd in nc argon2 podman; do - command -v $cmd >/dev/null || { echo "$0: \`$cmd\` not found"; exit 2; } -done - -while getopts p:h opt; do - case $opt in - p) port=$OPTARG;; - h) usage; exit 255;; - *) usage; exit 255;; - esac -done -shift $((OPTIND-1)) - -if [ $# -eq 0 ]; then - docroot=. -else - docroot="$1" -fi -docroot=$(cd "$docroot"; pwd) - -# FIFO to write HTTP response ## todo mktemp -if ! fifo1=$(makefifo "${0##*/}.$$.1"); then - echo "$0: creating a named pipe failed. Already running?" 1>&2 - exit 1 -fi - -# FIFO for internal event notification ## todo mktemp -if ! fifo2=$(makefifo "${0##*/}.$$.2"); then - echo "$0: creating a named pipe failed. Already running?" 1>&2 - exit 1 -fi - -trap interrupt INT - -echo $$ > "$pid_file" - -cat 1>&2 <<EOF -Simple HTTP Server - -Listening at the port number $port. -Type ^C to quit. -EOF - -# todo nc error should crash -# shellcheck disable=SC2002 -while cat "$fifo1" | "$nc" -l -p "$port" | parse; do - : -done -cleanup -exit diff --git a/toymaker.sh b/toymaker.sh new file mode 100755 index 0000000000000000000000000000000000000000..ad646f5371ce9db4eac1b6f5fdafc1ea7de562ef --- /dev/null +++ b/toymaker.sh @@ -0,0 +1,391 @@ +#!/bin/sh +# +# Based on server by Upper Stream (c) 2017, MIT license +# (https://gist.github.com/upperstream/b9b77429bc024541b7605aea76086ad8) + +. start.sh +. get.sh +. password.sh + +set -e + +exec >>~/toymaker.log +exec 2>&1 + +# program name +program=${0##*/} + +usage() { + cat <<-EOF + Usage: + + $program [-p port] [docroot] + $program -h + + -p port : specify listening port number; defaults to 1313 + -h : print this help and quit + docroot : specify document root directory; defaults to the current directory +EOF +} + +# default listening port +port=1313 + +# Configure in accordance with your environment +nc=$(command -v nc) || true + +# todo mktmp +pid_file=/tmp/$program.pid + +slashes() { + printf "%s" "$1" | sed 's|[^/]||g' | wc -m +} + +field() { + echo "$1" | cut -d '/' -f "$2" +} +makefifo() { + name="$1" + mkfifo -m 0600 "/tmp/$name.fifo" && echo "/tmp/$name.fifo" +} + +templateMessage() { + message=$1 + templateName=$2 + contentType=$3 + case $contentType in + text/plain) + ext='txt' + ;; + text/html|\*|\*/\*|'') + ext='html' + ;; + image/\*|image/svg) + ext='svg' + ;; + *) + return 1 + ;; + esac + [ -f "templates/$templateName.$ext" ] || return 1 + #shellcheck disable=SC1090 + . "templates/$templateName.$ext" + template "$message" +} + +respond200() { + page="$1" + contentType="$2" + [ -z "$contentType" ] && contentType='text/html' + + # { cat "$fifo2" > /dev/null; printf ""; } > "$fifo1" & + + printf "HTTP/1.1 200 OK\r\nContent-Length: %s\r\nContent-Type: %s\r\n\r\n%s\r\n" "${#page}" "$contentType" "$page" > "$fifo1" + + # printf "%s\r\n" "$page" >> "$fifo1" + + # sleep 1; echo "end" > "$fifo2" + printf "200 %s" "${#page}" +} + +respond200file() { + filePath="$1" + contentType="$2" + + { cat "$fifo2" > /dev/null; printf ""; } > "$fifo1" & + + printf "HTTP/1.1 200 OK\r\nContent-Length: %s\r\nContent-Type: %s\r\n\r\n" "${#page}" "$contentType" >> "$fifo1" + + cat "$filePath" >> "$fifo1" + + sleep 1; echo "end" > "$fifo2" + printf "200 %s" "${#page}" +} + +respond202() { + toy=$1 + item=$2 + message=$3 + contentType="$4" + [ -z "$contentType" ] && contentType='text/html' + printf "HTTP/1.1 202 Accepted\r\nContent-Length: %s\r\nLocation: /toys/%s/%s\r\n\r\n%s\r\n" "${#message}" "$toy" "$item" "$message" > "$fifo1" + printf "202 -" +} + +respond401() { + message="" + printf "HTTP/1.1 401 Unauthorized\r\nContent-Length: %s\r\n\r\n%s\r\n" "${#message}" "$message" > "$fifo1" + printf "401 -" +} + +respond403() { + message="" + printf "HTTP/1.1 403 Forbidden\r\nContent-Length: %s\r\n\r\n%s\r\n" "${#message}" "$message" > "$fifo1" + printf "403 -" +} + +respond404() { + path=$1 + # todo html + message="$path not found" + printf "HTTP/1.1 404 Not Found\r\nContent-Length: %s\r\n\r\n%s\r\n" "${#message}" "$message" > "$fifo1" + printf "404 -" +} + +respond405() { + method=$1 + # todo html + message="$method unsupported" + printf "HTTP/1.1 405 Method Not Allowed\r\nContent-Length: %s\r\n\r\n%s\r\n" "${#message}" "$message" > "$fifo1" + printf "405 -" +} + +respond406() { + contentType=$1 + # todo html + message="$contentType unsupported" + printf "HTTP/1.1 406 Not Acceptable\r\nContent-Length: %s\r\n\r\n%s\r\n" "${#message}" "$message" > "$fifo1" + printf "406 -" +} + +respond500() { + # todo html + message="Server error: $1" + printf "HTTP/1.1 500 Internal Server Error\r\nContent-Length: %s\r\n\r\n%s\r\n" "${#message}" "$message" > "$fifo1" + printf "500 -" +} + +check_auth() { + authorization=$1 + stored=$(cat password) + if [ -z "$stored" ] || [ -z "$authorization" ] + then + respond401 + return 1 + fi + if ! printf '%s' "$authorization" | verify "$stored" + then + respond403 + return 1 + fi +} + +execute() { + method="$1" + path="$2" + authorization="$3" + accept="$4" + _toys=$(field "$path" 2) + if [ "$_toys" != 'toys' ] + then + respond404 "$path" + return 1 + fi + + case $method in + GET) + if [ "$(slashes "$path")" -eq 2 ] + then + toy=$(field "$path" 3) + if [ "$toy" = '' ] + then + if page=$(templateMessage "$(list_toys)" 'toys' 'text/html') + then + respond200 "$page" + else + respond406 "$accept" + fi + else + r=$(list_items "$toy") + status=$(echo "$r" | cut -d ';' -f 1) + if [ "$status" = 'n' ] + then + respond404 "$path" + elif page=$(templateMessage "$r" 'items' 'text/html') + then + respond200 "$page" + else + respond406 "$accept" + fi + fi + elif [ "$(slashes "$path")" -eq 3 ] + then + toy=$(field "$path" 3) + item=$(field "$path" 4) + if [ -z "$item" ] + then + respond404 "$path" + return 1 + fi + r=$(show_item "$toy" "$item") + status=$(echo "$r" | cut -d ';' -f 1) + if [ "$status" = 'n' ] + then + respond404 "$path" + elif page=$(templateMessage "$r" 'item' "$accept") + then + if [ "$status" = 'r' ] + then + respond202 "$toy" "$item" "$page" "$accept" + else + respond200 "$page" "$accept" + fi + else + respond406 "$accept" + fi + elif [ "$(slashes "$path")" -eq 4 ] + then + toy=$(field "$path" 3) + item=$(field "$path" 4) + artifactName=$(field "$path" 5) + r=$(get_artifact "$toy" "$item" "$artifactName") + status=$(echo "$r" | cut -d ';' -f 1) + if [ "$status" = 'n' ] + then + respond404 "$path" + else + contentType=$(echo "$r" | cut -d ';' -f 2) + filePath=$(echo "$r" | cut -d ';' -f 3) + respond200file "$filePath" "$contentType" + fi + else + respond404 "$path" + fi + ;; + POST) + check_auth "$authorization" || return + + toy=$(field "$path" 3) + if [ "$(slashes "$path")" -ne 3 ] || [ -n "$(field "$path" 4)" ] + then + respond404 "$path" + fi + + item=$(start_item "$toy") + if [ $! -gt 0 ] + then + respond500 "error while creating $toy" + else + respond202 "$toy" "$item" + fi + + # todo delete old items, leave 1 stable, 1 errored + ;; + DELETE) + check_auth "$authorization" || return + + if [ "$(slashes "$path")" -eq 3 ] + then + toy=$(field "$path" 3) + item=$(field "$path" 4) + if stop_item "$toy" "$item" + then + respond200 "" + else + respond500 "error while stopping $toy/$item" + fi + else + respond404 "$path" + fi + ;; + *) + respond405 "$method" + ;; + esac +} + +cleanup() { + rm "$fifo1" "$fifo2" "$pid_file" +} + +interrupt() { + cleanup + exit 0 +} + +parse() { + cr=$(printf "\r") + while read -r line; do + line="${line%"$cr"}" + case "$line" in + GET*|POST*|PUT*|DELETE*|HEAD*|OPTIONS*|TRACE*) + # shellcheck disable=SC2086 + set -- $line + method=$1 + path=$2 + ;; + Host:*) + host=${line#Host: } + ;; + Accept:*) + accept=${line#Accept: } + ;; + Authorization:*) + authorization=${line#Authorization: } + ;; + *:*) + ;; + "") + date=$(date +'%d/%m/%Y:%H:%M:%S %z') + status_length=$(execute "$method" "$path" "$authorization" "$accept") + printf "%s %s \"http://%s%s\" %s\n" "$date" "$method" "$host" "$path" "$status_length" + trap - INT + exit + ;; + *) + ;; + esac + done +} + +# dependency verification +for cmd in nc argon2 podman; do + command -v $cmd >/dev/null || { echo "$0: \`$cmd\` not found"; exit 2; } +done + +while getopts p:h opt; do + case $opt in + p) port=$OPTARG;; + h) usage; exit 255;; + *) usage; exit 255;; + esac +done +shift $((OPTIND-1)) + +if [ $# -eq 0 ]; then + docroot=. +else + docroot="$1" +fi +docroot=$(cd "$docroot"; pwd) + +# FIFO to write HTTP response ## todo mktemp +if ! fifo1=$(makefifo "${0##*/}.$$.1"); then + echo "$0: creating a named pipe failed. Already running?" 1>&2 + exit 1 +fi + +# FIFO for internal event notification ## todo mktemp +if ! fifo2=$(makefifo "${0##*/}.$$.2"); then + echo "$0: creating a named pipe failed. Already running?" 1>&2 + exit 1 +fi + +trap interrupt INT + +echo $$ > "$pid_file" + +cat 1>&2 <<EOF +Simple HTTP Server + +Listening at the port number $port. +Type ^C to quit. +EOF + +# todo nc error should crash +# shellcheck disable=SC2002 +while cat "$fifo1" | "$nc" -l -p "$port" | parse; do + : +done +cleanup +exit