basic version: index handler, cgi-style dynamic site generation, correct mime type for static files

This commit is contained in:
overflowerror 2017-11-18 01:26:25 +01:00
parent 9cc28dc1a9
commit 73a5498b83
16 changed files with 540 additions and 0 deletions

10
404.html Normal file
View file

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<title>Not found</title>
</head>
<body>
<h1>Not found</h1>
The resource [path] was not found on server [host].
</body>
</html>

20
home/hello-world.sh Executable file
View file

@ -0,0 +1,20 @@
#!/bin/bash
statusContainer=$1
eval "declare -A settings="${2#*=}
eval "declare -A headers="${3#*=}
echo 200 > $statusContainer
cat <<EOF
<!DOCTYPE html>
<html>
<head>
<title>Hello World</title>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
EOF

31
home/info.sh Executable file
View file

@ -0,0 +1,31 @@
#!/bin/bash
statusContainer=$1
eval "declare -A settings="${2#*=}
eval "declare -A headers="${3#*=}
echo 200 > $statusContainer
cat <<EOF
<!DOCTYPE html>
<html>
<head>
<title>Info</title>
</head>
<body>
<h1>Settings</h1>
EOF
for key in "${!settings[@]}"; do
echo "$key -> ${settings[$key]}<br />"
done
cat <<EOF
<h1>Headers</h1>
EOF
for key in "${!headers[@]}"; do
echo "$key -> ${headers[$key]}<br />"
done
cat << EOF
</body>
</html>
EOF

60
home/stuff/add.sh Executable file
View file

@ -0,0 +1,60 @@
#!/bin/bash
statusContainer=$1
eval "declare -A settings="${2#*=}
eval "declare -A headers="${3#*=}
if test "${headers[query]}" == ""; then
echo 200 > $statusContainer
cat<<EOF
<!DOCTYPE>
<html>
<head>
<title>Add to guestbook</title>
</head>
<body>
<h1>Add something to the guestbook</h1>
<form action="?" method="GET">
<input name="name" type="text" placeholder="Your name"><br />
<textarea name="text"></textarea><br />
<input type="submit">
</form>
</body>
</html>
EOF
exit
fi
name=""
text=""
fields=$(echo ${headers[query]} | tr "&" "\n")
for field in $fields; do
key=$(echo $field | awk -F= '{ print $1 }')
value=$(echo $field | awk -F= '{for (i=2; i<=NF; i++) print $i}')
if test "$key" = "name"; then
name=$value
elif test "$key" = "text"; then
text=$value
fi
done
if test "$name" = "" -o "$text" = ""; then
echo 400 > $statusContainer
echo 400 - Bad Request
exit
fi
cat >> ./guestbook.txt <<EOF
===============================
$(date): $(python -c "import urllib, sys; print urllib.unquote_plus(sys.argv[1]).decode('utf8')" "$name")
$(python -c "import urllib, sys; print urllib.unquote_plus(sys.argv[1]).decode('utf8')" "$text")
EOF
echo 302 > $statusContainer
echo "Location: ./guestbook.txt" >> $statusContainer

6
home/stuff/guestbook.txt Normal file
View file

@ -0,0 +1,6 @@
===============================
Sat Nov 18 01:20:41 CET 2017: overflow
Welcome. : )

8
home/test/cgi-test.c Normal file
View file

@ -0,0 +1,8 @@
#include <stdio.h>
int main() {
printf("Hello World");
return 0;
}

BIN
home/test/cgi-test.cgi Executable file

Binary file not shown.

View file

@ -0,0 +1,3 @@
#!/bin/bash
echo Hello World

4
home/test/php-test.php Executable file
View file

@ -0,0 +1,4 @@
#!/usr/bin/php
<?php
echo "PHP version: " . phpversion();

1
home/test/plaintest.txt Normal file
View file

@ -0,0 +1 @@
Hello World

8
home/test/redirect.sh Executable file
View file

@ -0,0 +1,8 @@
#!/bin/bash
statusContainer=$1
eval "declare -A settings="${2#*=}
eval "declare -A headers="${3#*=}
echo 302 > $statusContainer
echo "Location: ../stuff/guestbook.txt" >> $statusContainer

65
index.sh Executable file
View file

@ -0,0 +1,65 @@
#!/bin/bash
server="$1"
host="$2"
rpath="$3"
path="$4"
statuscontainer=$5
echo 200 > $statuscontainer
cat <<EOF
<!DOCTYPE>
<html>
<head>
<title>Index of $rpath</title>
</head>
<body>
<h1>Index of $rpath</h1>
<hr />
<table>
<tr>
<th>
Name
</th>
<th>
Type
</th>
<th>
Executeable
</th>
<th>
Size
</th>
</tr>
EOF
for file in $(ls -a $path); do
if test "$file" = ".." -a "$rpath" = "/"; then
continue;
fi
cat <<EOF
<tr>
<td>
<a href="$(realpath -sm "$rpath/$file")">$file</a>
</td>
<td>
$(file -b $path/$file)
</td>
<td>
$(if test ! -d $path/$file; then if test -x $path/$file; then echo yes; else echo no; fi; fi)
</td>
<td>
$(if test ! -d $path/$file; then du -kh $path/$file | cut -f1; fi)
</td>
</tr>
EOF
done
cat <<EOF
</table>
<hr />
$host ($server)
</body>
</html>
EOF

133
response.sh Executable file
View file

@ -0,0 +1,133 @@
#!/bin/bash
settingsfile=$1
eval "$(cat $settingsfile)" # declare settings array
declare -A headers
first=1
while true; do
IFS=$'\r' read header
if test "$header" = ""; then
break
fi
if test $first = 1; then
headers[method]=$(echo "$header" | awk '{ print $1 }')
headers[http]=$(echo "$header" | awk '{ print $3 }' | awk -F/ '{ print $2} ')
headers[path]=$(echo "$header" | awk '{ print $2 }')
first=0
continue
fi
headers[$(echo $header | awk -F: '{ print $1}')]="$(echo "$header" | awk '{for (i=2; i<=NF; i++) print $i}')"
done
rpath="$(realpath -sm "${headers[path]}")"
headers[query]="$(echo "$rpath" | awk -F? '{for (i=2; i<=NF; i++) print $i}')"
rpath="$(echo "$rpath" | awk -F? '{ print $1 }')"
path="${settings[home]}${rpath}"
path="$(realpath -sm "$path")"
urlencode() {
python -c "import urllib, sys; print urllib.quote(sys.argv[1])" "$1"
}
placeholder() {
declare -A tokens
tokens[path]="$rpath ($path)"
tokens[host]="${headers[Host]}"
tokens[server]="${settings[server]}"
text="$(cat)"
for key in "${!tokens[@]}"; do
key="$(echo "$key" | sed -e 's/[]\/$*.^|[]/\\&/g')"
value="$(echo "${tokens[$key]}" | sed -e 's/[]\/$*.^|[]/\\&/g')"
text=$(echo "$text" | sed -e "s/\[$key\]/$value/g")
done
echo "$text"
}
addStatusContainer() {
i=0
container="/dev/shm/st-cont-$$-";
while true; do
if test -f $container$i; then
i=$(($i+1))
continue
fi
container=$container$i
break
done
touch $container
echo $container
}
removeStatusContainer() {
container=$1
rm $container
}
isExecutable() {
path=$1
if test ! -x $path -o ! -f $path; then
return 1
fi
ext="$(echo $path | awk -F. '{ print $NF }')"
for i in ${settings[executeable]}; do
if test "$ext" = "$i"; then
return 0
fi
done
return 1
}
status=500
content=""
declare -A responseHeaders
responseHeaders[Content-Type]="text/html"
responseHeaders[Server]="${settings[server]}"
if test ! -e "$path"; then
status=404
content="$(placeholder < ./404.html)"
elif test ${settings[index]} = true -a -d "$path"; then
container=$(addStatusContainer)
content=$(./index.sh "${settings[server]}" "${headers[Host]}" "$rpath" "$path" "$container")
status=$(cat $container)
removeStatusContainer $container
elif $(isExecutable $path); then
container=$(addStatusContainer)
pushd "$(dirname $path)" > /dev/null
content=$($path $container "$(declare -p settings)" "$(declare -p headers)")
popd > /dev/null
status=$(head -n 1 $container)
if test "$status" = ""; then
status=200
fi
while read line; do
responseHeaders[$(echo $line | awk -F: '{ print $1 }')]="$(echo "$line" | awk '{for (i=2; i<=NF; i++) print $i}')"
done <<< $(tail -n 1 $container)
removeStatusContainer $container
else
status=200
responseHeaders['Content-Type']="$(file -b --mime-type $path)"
content=$(cat $path)
fi
length=$(printf "%s" "$content" | wc -c)
echo -en "HTTP/1.1 $status $(./statusString.sh $status)\r\n"
echo -en "Content-Length: $length\r\n"
for key in ${!responseHeaders[@]}; do
echo -en "$(urlencode "$key"): $(urlencode "${responseHeaders[$key]}")\r\n"
done
echo -en "\r\n"
printf "%s" "$content"

68
server.sh Executable file
View file

@ -0,0 +1,68 @@
#!/bin/bash
EXIT_FAILURE=1
EXIT_SUCCESS=0
port=-1
progname="server"
help() {
cat << EOF
usage: $progname --port=PORT
Options:
-p, --port=PORT set port
-h, --home=HOME set home directory
-v, --verbose set to verbose mode
-q, --quiet don't output anything
EOF
}
verboselevel=0
echoOnVerbose() {
if test "$verboselevel" -ge $1; then
echo -n "$2"
fi
}
progname="$0"
OPTS=$(getopt -o "p:vqh:" -l "port:,verbose,quiet,home:" -- $@)
if test $? != 0; then
exit $EXIT_FAILURE
fi
eval set -- "$OPTS"
home="./home/"
while true; do
case "$1" in
-p|--port) port=$2; shift 2;;
-v|--verbose) verboselevel=$(($verboselevel+1)); shift;;
-q|--quiet) verboselevel=-1; shift;;
-h|--home) home=$2; shift 2;;
--) shift; break;;
esac
done
if test "$port" -lt 1; then
help
exit $EXIT_FAILURE
fi
settingsfile="/dev/shm/wserver-$$"
declare -A settings
settings[home]=$home
settings[verbose]=$verboselevel
settings[executeable]="sh php py cgi"
settings[server]="server"
settings[index]="true"
declare -p settings > $settingsfile
echo "Starting... "
socat $(echoOnVerbose 2 "-vv") tcp-listen:$port,reuseaddr,fork SYSTEM:"./response.sh $settingsfile" > /dev/null
if test $? != 0; then
exit $EXIT_FAILURE
fi

72
statusString.sh Executable file
View file

@ -0,0 +1,72 @@
#!/bin/bash
status=$1
case $status in
100) echo Continue ;;
101) Switching Protocols ;;
102) Processing ;;
200) echo OK ;;
201) echo Created ;;
202) echo Accepted ;;
203) echo Non-Authoritative Information ;;
204) echo No Content ;;
205) echo Reset Content ;;
206) echo Partial Content ;;
207) echo Multi-Status ;;
208) echo Already Reported ;;
226) echo IM Used ;;
300) echo Multiple Choices ;;
301) echo Moved Permanently ;;
302) echo Found ;;
303) echo See Other ;;
304) echo Not Modified ;;
305) echo Use Proxy ;;
306) echo Switch Proxy ;;
307) echo Temporary Redirect ;;
308) echo Permanent Redirect ;;
400) echo Bad Request ;;
401) echo Unauthorized ;;
402) echo Payment Required ;;
403) echo Forbidden ;;
404) echo Not Found ;;
405) echo Method Not Allowed ;;
406) echo Not Acceptable ;;
407) echo Proxy Authentication Required ;;
408) echo Request Timeout ;;
409) echo Conflict ;;
410) echo Gone ;;
411) echo Length Required ;;
412) echo Precondition Failed ;;
413) echo Payload Too Large ;;
414) echo URI Too Long ;;
415) echo Unsupported Media Type ;;
416) echo Range Not Satisfiable ;;
417) echo Expectation Failed ;;
418) echo I\'m a teapot ;;
421) echo Misdirected Request ;;
422) echo Unprocessable Entity ;;
423) echo Locked ;;
424) echo Failed Dependency ;;
426) echo Upgrade Required ;;
428) echo Precondition Required ;;
429) echo Too Many Requests ;;
431) echo Request Header Fields Too Large ;;
451) echo Unavailable For Legal Reasons ;;
500) echo Internal Server Error ;;
501) echo Not Implemented ;;
502) echo Bad Gateway ;;
503) echo Service Unavailable ;;
504) echo Gateway Timeout ;;
505) echo HTTP Version Not Supported ;;
506) echo Variant Also Negotiates ;;
507) echo Insufficient Storage ;;
508) echo Loop Detected ;;
510) echo Not Extended ;;
511) echo Network Authentication Required ;;
*) echo Unknown
esac

51
test-response.sh Executable file
View file

@ -0,0 +1,51 @@
#!/bin/bash
echo "Start response.sh" 1>&2
settingsfile=$1
eval "$(cat $settingsfile)" # declare settings array
declare -A headers
first=1
while true; do
IFS=$'\r' read header
if test "$header" = ""; then
break
fi
if test $first = 1; then
headers[method]=$(echo "$header" | awk '{ print $1 }')
headers[http]=$(echo "$header" | awk '{ print $3 }' | awk -F/ '{ print $2} ')
headers[path]=$(echo "$header" | awk '{ print $2 }')
first=0
continue
fi
headers[$(echo $header | awk -F: '{ print $1}')]="$(echo "$header" | awk '{for (i=2; i<=NF; i++) print $i}')"
done
content="<!DOCTYPE html>
<html>
<head>
<title>Test</title>
</head>
<body>
<h1>Hallo Welt</h1>
$(for key in ${!headers[@]}; do echo "$key" "->" "${headers[$key]}" "<br />"; done)
<br />
<br />
$(for key in ${!settings[@]}; do echo "$key" "->" "${settings[$key]}" "<br />"; done)
</body>
</html>
"
length=$(printf "%s" "$content" | wc -c)
echo "$length" 1>&2
echo "$content" 1>&2
echo -en "HTTP/1.1 200 OK\r\n"
echo -en "Content-Type: text/html\r\n"
echo -en "Server: testserver\r\n"
echo -en "Content-Length: $length\r\n"
echo -en "\r\n"
printf "%s" "$content"