bash/shttpd: a smol shell-based web server
This commit is contained in:
parent
c1da58c9c8
commit
fba41aeaeb
|
@ -0,0 +1,126 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
declare -A clients http_codes=(
|
||||
[200]=OK [400]="Bad Request" [403]=Forbidden
|
||||
[404]="Not Found" [405]="Method Not Allowed"
|
||||
)
|
||||
|
||||
enable -f "${SHERVER_SO_PATH-.}"/sherver.so sherver_{bind,select,accept}
|
||||
|
||||
if ! sherver-bind -p 8080 -h 0.0.0.0 -v server_fd; then
|
||||
exit
|
||||
fi
|
||||
|
||||
while true; do
|
||||
if ! sherver-select -iv ready_fds "$server_fd" "${!clients[@]}"; then
|
||||
continue
|
||||
fi
|
||||
|
||||
for fd in "${ready_fds[@]}"; do
|
||||
if (( fd == server_fd )) && sherver-accept -v client_info "$server_fd"; then
|
||||
declare -A __client_{req,resp}_"${client_info[0]}"_
|
||||
declare -n client_req=__client_req_${client_info[0]}_
|
||||
client_req[ip]=${client_info[1]}
|
||||
client_req[port]=${client_info[2]}
|
||||
clients[${client_info[0]}]=0
|
||||
else
|
||||
declare -n client_req=__client_req_${fd}_
|
||||
declare -n client_resp=__client_resp_${fd}_
|
||||
|
||||
if ! IFS= read -ru "$fd" line || (( ! ${#line} )); then
|
||||
unset "__client_"{req,resp}"_${fd}_" "clients[$fd]"
|
||||
exec {fd}>&-
|
||||
else
|
||||
(( clients[$fd] += ${#line} + 1 ))
|
||||
line=${line%$'\r'}
|
||||
|
||||
if [[ ! -v client_req[method] ]]; then
|
||||
read -ra req_line <<< "$line"
|
||||
client_req[method]=${req_line[0],,}
|
||||
client_req[req-file]=${req_line[1]}
|
||||
client_req[version]=${req_line[2]-HTTP/1.0}
|
||||
|
||||
case ${client_req[method]} in
|
||||
get)
|
||||
client_resp[has-payload]=yes
|
||||
;;
|
||||
head)
|
||||
client_resp[has-payload]=no
|
||||
;;
|
||||
*)
|
||||
client_req[error]=yes
|
||||
client_resp[code]=405
|
||||
esac
|
||||
|
||||
if [[ ${client_req[req-file]} = /* ]]; then
|
||||
client_req[real-file]=.${client_req[req-file]}
|
||||
|
||||
if [[ ${client_req[req-file]} = */ ]]; then
|
||||
client_req[real-file]+=index.html
|
||||
elif [[ -d client_req[real-file] ]]; then
|
||||
client_req[real-file]+=/index.html
|
||||
fi
|
||||
else
|
||||
client_req[error]=yes
|
||||
client_resp[code]=400
|
||||
fi
|
||||
|
||||
if [[ ${client_req[error]} != yes ]]; then
|
||||
if [[ -f ${client_req[real-file]} ]]; then
|
||||
if [[ -r ${client_req[real-file]} ]]; then
|
||||
client_resp[code]=200
|
||||
else
|
||||
client_req[error]=yes
|
||||
client_resp[code]=403
|
||||
fi
|
||||
else
|
||||
client_req[error]=yes
|
||||
client_resp[code]=404
|
||||
fi
|
||||
fi
|
||||
elif [[ $line = *": "* ]]; then
|
||||
hdr_key=${line%%:*} hdr_key=${hdr_key,,}
|
||||
hdr_val=${line#*: } client_req[hdr-$hdr_key]=$hdr_val
|
||||
elif [[ -z $line ]]; then
|
||||
req_host=${client_req[hdr-host]-"$HOSTNAME"}
|
||||
ver=${client_req[version]} code=${client_resp[code]}
|
||||
|
||||
if [[ ${client_req[error]} = yes ]]; then
|
||||
client_req[real-file]=./error.html
|
||||
fi
|
||||
|
||||
payload=$(<"${client_req[real-file]}")
|
||||
client_resp[hdr-Content-Length]=${#payload}
|
||||
|
||||
case ${client_req[req-file]} in
|
||||
*.html)
|
||||
client_resp[hdr-Content-Type]=text/html
|
||||
;;
|
||||
*.txt)
|
||||
client_resp[hdr-Content-Type]=text/plain
|
||||
;;
|
||||
*)
|
||||
client_resp[hdr-Content-Type]=application/octet-stream
|
||||
esac
|
||||
|
||||
printf -v 'client_resp[hdr-Date]' '%(%Y-%m-%d %H:%M:%S)T' -1
|
||||
printf '%s %d %s\r\n' "$ver" "$code" "${http_codes[$code]}" >&"$fd"
|
||||
|
||||
for hdr in "${!client_resp[@]}"; do
|
||||
if [[ $hdr = hdr-* ]]; then
|
||||
printf '%s: %s\r\n' "${hdr#hdr-}" "${client_resp[$hdr]}" >&"$fd"
|
||||
fi
|
||||
done
|
||||
|
||||
printf '\r\n' >&"$fd"
|
||||
|
||||
if [[ ${client_resp[has-payload]} = yes ]]; then
|
||||
printf %s "$payload" >&"$fd"
|
||||
fi
|
||||
|
||||
printf '[%s] %s has requested http://%s%s (%s)\n' "${client_resp[hdr-Date]}" "${client_req[ip]}" "$req_host" "${client_req[req-file]}" "${client_req[real-file]}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
done
|
Loading…
Reference in New Issue