2017-11-18 00:26:25 +00:00
#!/bin/bash
2017-11-24 00:43:59 +00:00
ssl = " $2 "
settingsfile = " $1 "
2017-11-18 00:26:25 +00:00
eval " $( cat $settingsfile ) " # declare settings array
2017-11-18 22:52:03 +00:00
. misc.sh
declare -A server
server[ remoteAddress] = " $SOCAT_PEERADDR "
server[ remotePort] = " $SOCAT_PEERPORT "
2017-11-21 21:33:36 +00:00
server[ remoteHost] = " $( dig +noall +answer -x $SOCAT_PEERADDR | awk '{ print $5 }' ) "
server[ serverAddress] = " $SOCAT_SOCKADDR "
server[ serverPort] = " $SOCAT_SOCKPORT "
server[ serverName] = " ${ settings [name] } "
server[ serverAdmin] = " ${ settings [admin] } "
server[ serverSoftware] = " ${ settings [server] } "
server[ documentRoot] = " $( realpath " ${ settings [home] } " ) "
server[ requestTime] = " $( date +%s) "
server[ requestTimeFloat] = " $(( $( date +%s%N) / 1000 )) "
server[ requestTimeReadable] = " $( date --rfc-3339= ns) "
2017-11-18 22:52:03 +00:00
2017-11-23 23:09:43 +00:00
requestTimeout( ) {
echo " $( date --rfc-3339= ns) - ${ server [remoteAddress] } : ${ server [remotePort] } - Error: request timeout " 1>& 2
content = " 408 Request Timeout
The client did not produce a request within the time that the server was prepared to wait. The client MAY repeat the request without modifications at any later time
"
status = 408
length = $( printf "%s" " $content " | wc -c)
echo -en " HTTP/1.1 $status $( ./statusString.sh $status ) \r\n "
echo -en " Content-Length: $length \r\n "
echo -en "Content-Type: text/plain\r\n"
echo -en "\r\n"
printf "%s" " $content "
exit 1
}
trap requestTimeout SIGUSR1
headerTimeout = 10
set +bm
headerTimer( ) {
pid = $1
timeout = $2
sleep $2
kill -USR1 $pid
}
headerTimer $$ $headerTimeout &
headerTimerPid = $!
2017-11-18 00:26:25 +00:00
declare -A headers
first = 1
while true; do
IFS = $'\r' read header
if test " $header " = "" ; then
break
fi
if test $first = 1; then
2017-11-21 21:33:36 +00:00
server[ requestMethod] = " $( echo " $header " | awk '{ print $1 }' ) "
2017-11-19 00:22:21 +00:00
server[ http] = " $( echo " $header " | awk '{ print $3 }' | awk -F/ '{ print $2} ' ) "
2017-11-24 00:43:59 +00:00
server[ https] = " $ssl "
2017-11-21 21:33:36 +00:00
server[ serverProtocol] = " $( echo " $header " | awk '{ print $3 }' ) "
server[ request_unchecked] = $( echo " $header " | awk '{ print $2 }' )
server[ requestURI] = " $( realpath -sm " ${ server [request_unchecked] } " ) "
server[ queryString] = " $( echo " ${ server [requestURI] } " | awk -F? '{for (i=2; i<=NF; i++) print $i}' ) "
server[ scriptName] = " $( echo " ${ server [requestURI] } " | awk -F? '{ print $1 }' ) "
server[ scriptFilename] = " $( realpath -sm " ${ settings [home] } ${ server [scriptName] } " ) "
2017-11-18 00:26:25 +00:00
first = 0
continue
fi
headers[ $( echo $header | awk -F: '{ print $1}' ) ] = " $( echo " $header " | awk '{for (i=2; i<=NF; i++) print $i}' ) "
done
2017-11-23 23:09:43 +00:00
kill -TERM $headerTimerPid
wait $headerTimerPid 2> /dev/null
2017-11-18 22:52:03 +00:00
if test " $first " = 1; then
# wut?
echo " $( date --rfc-3339= ns) - ${ server [remoteAddress] } : ${ server [remotePort] } - Error: Malformed HTTP request; ignoring " 1>& 2
exit 1
fi
2017-11-18 00:26:25 +00:00
2017-11-21 21:33:36 +00:00
server[ httpAccept] = " ${ headers [Accept] } "
server[ httpAcceptCharset] = " ${ headers [Accept-Charset] } "
server[ httpAcceptEncoding] = " ${ headers [Accept-Encoding] } "
server[ httpAcceptLanguage] = " ${ headers [Accept-Language] } "
server[ httpConnection] = " ${ headers [Accept-Connection] } "
server[ httpHost] = " ${ headers [Host] } "
server[ httpReferer] = " ${ headers [Referer] } "
server[ httpUserAgent] = " ${ headers [User-Agent] } "
server[ remoteUser] = "" # TODO not implemented
server[ redirectRemoteUser] = "" # TODO not implemented
server[ authType] = "" # TODO not implemented
server[ serverSignature] = ""
if true; then # TODO add condition setting
server[ serverSignature] = " ${ server [httpHost] } ( ${ server [serverSoftware] } ) "
fi
2017-11-18 00:26:25 +00:00
placeholder( ) {
declare -A tokens
2017-11-21 21:33:36 +00:00
tokens[ path] = " ${ server [scriptName] } "
tokens[ host] = " ${ server [httpHost] } "
tokens[ server] = " ${ server [serverSoftware] } "
tokens[ signature] = " ${ server [serverSignature] } "
2017-11-18 00:26:25 +00:00
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
2017-11-19 00:22:21 +00:00
if test -f " $container $i " ; then
2017-11-18 00:26:25 +00:00
i = $(( $i + 1 ))
continue
fi
2017-11-19 00:22:21 +00:00
container = " $container $i "
2017-11-18 00:26:25 +00:00
break
done
2017-11-19 00:22:21 +00:00
touch " $container "
echo " $container "
2017-11-18 00:26:25 +00:00
}
removeStatusContainer( ) {
2017-11-19 00:22:21 +00:00
container = " $1 "
rm " $container "
2017-11-18 00:26:25 +00:00
}
2017-11-21 21:33:36 +00:00
isExecutableShell( ) {
2017-11-19 00:22:21 +00:00
path = " $1 "
if test ! -x " $path " -o ! -f " $path " ; then
2017-11-18 00:26:25 +00:00
return 1
fi
2017-11-19 00:22:21 +00:00
ext = " $( echo " $path " | awk -F. '{ print $NF }' ) "
2017-11-21 21:33:36 +00:00
for i in ${ settings [shellExec] } ; do
if test " $ext " = " $i " ; then
return 0
fi
done
return 1
}
isExecutableCGI( ) {
path = " $1 "
if test ! -x " $path " -o ! -f " $path " ; then
return 1
fi
ext = " $( echo " $path " | awk -F. '{ print $NF }' ) "
for i in ${ settings [cgiExec] } ; do
if test " $ext " = " $i " ; then
return 0
fi
done
return 1
}
isExecutablePHP( ) {
path = " $1 "
if test ! -x " $path " -o ! -f " $path " ; then
return 1
fi
ext = " $( echo " $path " | awk -F. '{ print $NF }' ) "
for i in ${ settings [phpExec] } ; do
2017-11-18 00:26:25 +00:00
if test " $ext " = " $i " ; then
return 0
fi
done
return 1
}
2017-11-23 17:55:55 +00:00
cgiExec( ) {
export GATEWAY_INTERFACE = "CGI/1.1"
export AUTH_TYPE = " ${ server [authType] } "
export DOCUMENT_ROOT = " ${ server [documentRoot] } "
export HTTP_ACCEPT_CHARSET = " ${ server [httpAcceptCharset] } "
export HTTP_ACCEPT_ENCODING = " ${ server [httpAcceptEncoding] } "
export HTTP_ACCEPT_LANGUAGE = " ${ server [httpAcceptLanguage] } "
export HTTP_ACCEPT = " ${ server [httpAccept] } "
export HTTP_CONNECTION = " ${ server [httpConnection] } "
export HTTP_HOST = " ${ server [httpHost] } "
export HTTPS = " ${ server [https] } "
export HTTP_USER_AGENT = " ${ server [httpUserAgent] } "
export QUERY_STRING = " ${ server [queryString] } "
export REDIRECT_REMOTE_USER = " ${ server [redirectRemoteUser] } "
export REMOTE_ADDR = " ${ server [remoteAddress] } "
export REMOTE_HOST = " ${ server [remoteHost] } "
export REMOTE_PORT = " ${ server [remotePort] } "
export REMOTE_USER = " ${ server [remoteUser] } "
export REQUEST_METHOD = " ${ server [requestMethod] } "
export REQUEST_TIME_FLOAT = " ${ server [requestTimeFloat] } "
export REQUEST_TIME = " ${ server [requestTime] } "
export REQUEST_URI = " ${ server [requestURI] } "
export SCRIPT_FILENAME = " ${ server [scriptFilename] } "
export SCRIPT_NAME = " ${ server [scriptName] } "
export SERVER_ADDR = " ${ server [serverAddress] } "
export SERVER_ADMIN = " ${ server [serverAdmin] } "
export SERVER_NAME = " ${ server [serverName] } "
export SERVER_PORT = " ${ server [serverPort] } "
export SERVER_PROTOCOL = " ${ server [serverProtocol] } "
export SERVER_SIGNATURE = " ${ server [serverSignature] } "
export SERVER_SOFTWARE = " ${ server [serverSoftware] } "
" $1 "
}
2017-11-18 22:52:03 +00:00
baseSource = " $( pwd ) /base.source.sh "
2017-11-18 00:26:25 +00:00
status = 500
content = ""
declare -A responseHeaders
responseHeaders[ Content-Type] = "text/html"
responseHeaders[ Server] = " ${ settings [server] } "
2017-11-18 22:52:03 +00:00
responseHeaders[ Connection] = "max=1"
2017-11-18 00:26:25 +00:00
2017-11-18 22:52:03 +00:00
type = ""
2017-11-18 00:26:25 +00:00
2017-11-21 21:33:36 +00:00
if test ! -e " ${ server [scriptFilename] } " ; then
2017-11-18 00:26:25 +00:00
status = 404
content = " $( placeholder < ./404.html) "
2017-11-18 22:52:03 +00:00
type = "-"
2017-11-21 21:33:36 +00:00
elif test " ${ settings [index] } " = "true" -a -d " ${ server [scriptFilename] } " ; then
2017-11-19 00:22:21 +00:00
container = " $( addStatusContainer) "
content = " $( ./index.sh " $baseSource " " $container " " $( declare -p settings) " " $( declare -p server) " " $( declare -p headers) " ) "
status = $( head -n 1 " $container " )
2017-11-18 22:52:03 +00:00
if test " $status " = "" ; then
status = 200
fi
while read line; do
2017-11-19 00:22:21 +00:00
responseHeaders[ $( echo " $line " | awk -F: '{ print $1 }' ) ] = " $( echo " $line " | awk '{for (i=2; i<=NF; i++) print $i}' ) "
done <<< $( tail -n 1 " $container " )
2017-11-18 22:52:03 +00:00
2017-11-19 00:22:21 +00:00
removeStatusContainer " $container "
2017-11-18 00:26:25 +00:00
2017-11-18 22:52:03 +00:00
type = "index"
2017-11-21 21:33:36 +00:00
elif isExecutableShell " ${ server [scriptFilename] } " ; then
2017-11-19 00:22:21 +00:00
container = " $( addStatusContainer) "
2017-11-21 21:33:36 +00:00
pushd " $( dirname " ${ server [scriptFilename] } " ) " > /dev/null
content = $( " ${ server [scriptFilename] } " " $baseSource " " $container " " $( declare -p settings) " " $( declare -p server) " " $( declare -p headers) " )
2017-11-18 00:26:25 +00:00
popd > /dev/null
2017-11-19 00:22:21 +00:00
status = $( head -n 1 " $container " )
2017-11-18 00:26:25 +00:00
if test " $status " = "" ; then
status = 200
fi
while read line; do
2017-11-18 22:52:03 +00:00
if test " $( echo " $line " | grep ':' ) " = "" ; then
continue
fi
2017-11-19 00:22:21 +00:00
responseHeaders[ $( echo " $line " | awk -F: '{ print $1 }' ) ] = " $( echo " $line " | awk '{for (i=2; i<=NF; i++) print $i}' ) "
done <<< $( tail -n 1 " $container " )
2017-11-18 00:26:25 +00:00
2017-11-19 00:22:21 +00:00
removeStatusContainer " $container "
2017-11-18 22:52:03 +00:00
2017-11-21 21:33:36 +00:00
type = "shell"
elif isExecutableCGI " ${ server [scriptFilename] } " ; then
content = ""
status = 200
headerDone = 0
while read line; do
if test $headerDone = 1; then
content = " ${ content } ${ line } " $'\n'
continue
fi
if test " $( echo " $line " | grep ':' ) " = "" ; then
headerDone = 1
continue
fi
if test " $( echo " $line " | awk -F: '{print $1}' ) " = "Status" ; then
status = " $( echo " $line " | awk '{print $2}' ) "
continue
fi
responseHeaders[ $( echo " $line " | awk -F: '{ print $1 }' ) ] = " $( echo " $line " | awk '{for (i=2; i<=NF; i++) print $i}' ) "
done <<< $( # open subshell
2017-11-23 17:55:55 +00:00
cgiExec " ${ server [scriptFilename] } "
2017-11-21 21:33:36 +00:00
)
type = "cgi"
2017-11-23 17:55:55 +00:00
elif isExecutablePHP " ${ server [scriptFilename] } " ; then
content = ""
status = 200
headerDone = 0
while read line; do
if test $headerDone = 1; then
content = " ${ content } ${ line } " $'\n'
continue
fi
if test " $( echo " $line " | grep ':' ) " = "" ; then
headerDone = 1
continue
fi
if test " $( echo " $line " | awk -F: '{print $1}' ) " = "Status" ; then
status = " $( echo " $line " | awk '{print $2}' ) "
continue
fi
responseHeaders[ $( echo " $line " | awk -F: '{ print $1 }' ) ] = " $( echo " $line " | awk '{for (i=2; i<=NF; i++) print $i}' ) "
done <<< $( # open subshell
export REDIRECT_STATUS = 1
cgiExec php-cgi -f " ${ server [scriptFilename] } "
)
type = "php"
2017-11-18 00:26:25 +00:00
else
status = 200
2017-11-21 21:33:36 +00:00
#responseHeaders['Content-Type']="$(file -b --mime-type ${server[scriptFilename]})"
responseHeaders[ 'Content-Type' ] = " $( mimetype -b " ${ server [scriptFilename] } " ) "
content = " $( cat ${ server [scriptFilename] } ) "
2017-11-18 22:52:03 +00:00
type = "static"
2017-11-18 00:26:25 +00:00
fi
length = $( printf "%s" " $content " | wc -c)
2017-11-18 22:52:03 +00:00
if test " ${ settings [verbose] } " -ge "0" ; then
2017-11-24 00:43:59 +00:00
echo " $( date --rfc-3339= ns) - ${ server [remoteAddress] } : ${ server [remotePort] } - ${ headers [Host] } ${ server [requestURI] } - $type - $status - $length bytes " 1>& 2
2017-11-18 22:52:03 +00:00
fi
2017-11-18 00:26:25 +00:00
echo -en " HTTP/1.1 $status $( ./statusString.sh $status ) \r\n "
echo -en " Content-Length: $length \r\n "
for key in ${ !responseHeaders[@] } ; do
2017-11-21 21:33:36 +00:00
#echo -en "$(urlencode "$key"): $(urlencode "${responseHeaders[$key]}")\r\n"
echo -en " $key : ${ responseHeaders [ $key ] } \r\n "
2017-11-18 00:26:25 +00:00
done
echo -en "\r\n"
printf "%s" " $content "