127 lines
3.9 KiB
Bash
Executable file
127 lines
3.9 KiB
Bash
Executable file
#!/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,,}
|
|
client_req[hdr-$hdr_key]=${line#*: }
|
|
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
|
|
client_resp[has-payload]=yes
|
|
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
|