123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421 |
- #!/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=${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
- nc=$(command -v nc) || true
- 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
- contentTypeHeader=$3
- while [ -n "$contentTypeHeader" ]
- do
- contentType=$(echo "$contentTypeHeader" | cut -d ',' -f1)
- contentTypeHeader=$(echo "$contentTypeHeader" | cut -d ',' -f2-)
- contentType=$(echo "$contentType" | cut -d ';' -f1)
- case $contentType in
- text/plain)
- ext='txt'
- ;;
- text/html|\*|\*/\*|'')
- ext='html'
- ;;
- image/\*|image/svg)
- ext='svg'
- ;;
- *)
- continue
- ;;
- esac
- [ -f "templates/$templateName.$ext" ] && break
- ext=''
- done
- [ -n "$ext" ] || return 1
- #shellcheck disable=SC1090
- . "templates/$templateName.$ext"
- printf '%s\n' "$contentType"
- template "$message"
- }
- respond200() {
- page="$1"
- contentType="$2"
- [ -z "$contentType" ] && contentType='text/html'
- 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 "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
- message=$(templateMessage "$path not found" 'message' 'text/html' | tail -n+2)
- 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
- message=$(templateMessage "$method unsupported" 'message' 'text/html' | tail -n+2)
- 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
- 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() {
- message=$(templateMessage "Server error: $1" 'message' 'text/html' | tail -n+2)
- 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 "$(echo "$page" | tail -n+2)"
- else
- respond406 "$accept"
- return 1
- fi
- else
- r=$(list_items "$toy")
- status=$(echo "$r" | cut -d ';' -f1)
- content=$(echo "$r" | cut -d ';' -f2)
- if [ "$status" = 'n' ]
- then
- respond404 "toy $toy"
- return 1
- elif page=$(templateMessage "$toy;$content" 'toy' 'text/html')
- then
- respond200 "$(echo "$page" | tail -n+2)"
- else
- respond406 "$accept"
- return 1
- 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")
- artifacts=$(item_artifacts "$toy" "$item")
- status=$(echo "$r" | head -n1 | cut -d ';' -f1)
- if [ "$status" = 'n' ]
- then
- respond404 "item $toy/$item"
- return 1
- elif page=$(templateMessage "$artifacts;$toy;$item;$r" 'item' "$accept")
- then
- body=$(echo "$page" | tail -n+2)
- contentType=$(echo "$page" | head -n1)
- if [ "$status" = 'r' ]
- then
- respond202 "$toy" "$item" "$body" "$contentType"
- else
- respond200 "$body" "$contentType"
- fi
- else
- respond406 "$accept"
- return 1
- 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 "artifact $toy/$item/$artifactName"
- return 1
- else
- contentType=$(echo "$r" | cut -d ';' -f 2)
- filePath=$(echo "$r" | cut -d ';' -f 3)
- respond200file "$filePath" "$contentType"
- fi
- else
- respond404 "$path"
- return 1
- fi
- ;;
- POST)
- check_auth "$authorization" || return 1
- toy=$(field "$path" 3)
- if [ "$(slashes "$path")" -ne 3 ] || [ -n "$(field "$path" 4)" ]
- then
- respond404 "$path"
- return 1
- fi
- if ! (podman image ls --format "{{.Repository}}" | grep -x "localhost/toy_$toy" -q)
- then
- respond404 "toy $toy"
- return 1
- fi
- if item=$(start_item "$toy")
- then
- respond202 "$toy" "$item"
- else
- respond500 "error while creating $toy"
- return 1
- fi
- ;;
- DELETE)
- check_auth "$authorization" || return 1
- if [ "$(slashes "$path")" -eq 3 ]
- then
- toy=$(field "$path" 3)
- item=$(field "$path" 4)
- if [ -z "$toy" ] || [ -z "$item" ]
- then
- respond404 "$path"
- return 1
- fi
- if stop_item "$toy" "$item"
- then
- respond200 ""
- else
- respond500 "error while stopping $toy/$item"
- return 1
- fi
- else
- respond404 "$path"
- return 1
- fi
- ;;
- *)
- respond405 "$method"
- return 1
- ;;
- 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: }
- ;;
- accept:*)
- accept=${line#accept: }
- ;;
- Authorization:*)
- authorization=${line#Authorization: }
- ;;
- 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 podman awk
- 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
- 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
- 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
- # shellcheck disable=SC2002
- while cat "$fifo1" | "$nc" -l -p "$port" | parse; do
- :
- done
- cleanup
- exit
|