#!/bin/sh setglobal wwwoosh_port = '"8080'" setglobal wwwoosh_http_version = '"HTTP/1.1'" setglobal wwwoosh_fifo = '"/tmp/wwwoosh_fifo'" setglobal wwwoosh_debug_enabled = ''"" setglobal CR = '$'\r'' setglobal LF = '$'\n'' setglobal CRLF = ""$CR$LF"" proc wwwoosh { local app="$1" # TODO: is there a better way than a named pipe? rm -f $wwwoosh_fifo mkfifo $wwwoosh_fifo test $Argc -gt 1 && setglobal wwwoosh_port = $2 test $Argc -gt 2 && setglobal wwwoosh_debug_enabled = $3 echo "Starting Wwwoosh on port $wwwoosh_port..." while true { wwwoosh_listen $wwwoosh_port < $wwwoosh_fifo | wwwoosh_debug | wwwoosh_handle_request $app | wwwoosh_debug | wwwoosh_handle_response > $wwwoosh_fifo } } proc wwwoosh_debug { if test $wwwoosh_debug_enabled { tee /dev/stderr } else { cat } } proc wwwoosh_handle_request { local app="$1" # read the request line read request_line # read the header lines until we reach a blank line while read header && test ! $header = $'\r' { # FIXME: multiline headers setglobal header_name = ""HTTP_$[echo $header | cut -d ':' -f 1 | tr 'a-z-' 'A-Z_]"" export $header_name="$[echo $header | cut -d ':' -f 2 | sed 's/^ //]" } # extract HTTP method and HTTP version export REQUEST_METHOD=$[echo $request_line | cut -d ' ' -f 1] export wwwoosh_http_version=$[echo $request_line | cut -d ' ' -f 3 | tr -d $'\r] # extract the request_path, then PATH_INFO and QUERY_STRING components setglobal request_path = $[echo $request_line | cut -d ' ' -f 2] export PATH_INFO=$[echo $request_path | cut -d '?' -f 1] export QUERY_STRING=$[echo $request_path | cut -d '?' -f 2] export SCRIPT_NAME="" export SERVER_NAME="localhost" export SERVER_PORT="$port" $app } proc wwwoosh_handle_response { local response_status="200 OK" local response_headers="" local content_length="-" proc add_header { local header="$1" if test $response_headers { setglobal response_headers = ""$response_headers$CRLF$header"" } else { setglobal response_headers = $header } } while read header && test ! $header = "" { local header_name="$[echo $header | cut -d ':' -f 1 | tr 'A-Z' 'a-z]" local header_value="$[echo $header | cut -d ':' -f 2]" if test $header_name = "status" { setglobal response_status = $[echo $header | cut -d ':' -f 2 | sed 's/^ //] } elif test $header_name = "content-length" { setglobal content_length = $header_value add_header $header } else { add_header $header } } add_header "Connection: close" add_header "Date: $[date -u '+%a, %d %b %Y %R:%S GMT]" # echo status line, headers, blank line, body echo "$wwwoosh_http_version $response_status$CRLF$response_headers$CRLF$CR" cat setglobal log_remote_host = '"-'" setglobal log_user = '"-'" setglobal log_date = $[date -u '+%d/%b/%Y:%H:%M:%S] setglobal log_header = ""$REQUEST_METHOD $PATH_INFO $wwwoosh_http_version"" # doesn't work setglobal log_status = $[echo $response_status | cut -d " " -f1] setglobal log_size = $content_length echo "$log_remote_host - $log_user [$log_date] \"$log_header\" $log_status $log_size" !1 > !2 } proc wwwoosh_listen { # first try the standard netcat, then the bsd netcat nc -l -p $1 !2 > /dev/null || nc -l $1 } match $0 { with *wwwoosh.sh wwwoosh $ifsjoin(Argv) }