Skip to content

Commit

Permalink
Rough support for POST requests
Browse files Browse the repository at this point in the history
  • Loading branch information
fumyuun committed Mar 10, 2022
1 parent 87eb85b commit ef70aa4
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 20 deletions.
27 changes: 17 additions & 10 deletions README.md
Expand Up @@ -3,15 +3,24 @@ A simple webserver written in bash. Needless to say, please don't use this for a

Basic usage:

./bashserv.sh -s "./static_content/" -g "./get_handler.sh"
./bashserv.sh -s "./static_content/" -g "./get_handler.sh" --post "./post_handler.sh"

Where static files to serve are placed in the `static_content` directory, and an external script to render GET requests that don't match the static content.
Where static files to serve are placed in the `static_content` directory, and external script to render GET requests that don't match the static content, or POST requests.

The GET handler script will be called with two environment variables set: `$REQUEST_PATH_SANE` and `$REQUEST_PATH`: the first gives a slightly sanitised path, and the second gives the full original path as requested. Furthermore it currently receives all http header fields as arguments, in a triplet form. The first contains the number of words in the value field (as these can contain spaces), the second contains the key, and it will be followed by one or more words forming the value.
These scripts should simply write their respective responses to STDOUT. Furthermore, a convenience-script is also available to write a response header:
.
./header.sh -t "content_type" -l "content_length" "return_code"

For example `./header.sh -t "text/html" -l 1337 "200 OK"` would generate a response header stating the request was OK and we'll send an HTML page of 1337 characters.

# Handling GET requests
The GET handler script will be called with three environment variables set: `$REQUEST_PATH_SANE`, `$REQUEST_PATH` and `$REQUEST_FIELDS`: the first gives a slightly sanitised path, and the second gives the full original path as requested. The third contains all http header fields in a triplet form. The first contains the number of words in the value field (as these can contain spaces), the second contains the key, and it will be followed by one or more words forming the value.

To parse these values, you could do something like this in your get-handler:

`while [[ $# -gt 0 ]]; do
```
set $REQUEST_FIELDS
while [[ $# -gt 0 ]]; do
length="$1"
key="$2"
shift
Expand All @@ -29,10 +38,8 @@ To parse these values, you could do something like this in your get-handler:
mobile=1
fi
;;
esac`

It can use the header script to generate a header as well, which can be called as such:

./header.sh -t "content_type" -l "content_length" "return_code"
esac
```

For example `./header.sh -t "text/html" -l 1337 200`.
## Handling POST requests
In addition to the above, POST requests have one additional environment variable, `$POST_DATA`, set. This simply contains the POST data in a big string, as-received and unparsed (for now).
5 changes: 5 additions & 0 deletions bashserv.sh
Expand Up @@ -20,6 +20,11 @@ while [[ $# -gt 0 ]]; do
shift
shift
;;
--post)
export POST_HANDLER="$2"
shift
shift
;;
-p|--port)
PORT="$2"
shift
Expand Down
55 changes: 45 additions & 10 deletions handle_connection.sh
Expand Up @@ -14,6 +14,18 @@ while read -r line; do

# End of request, time to handle it
if [ -z "$line" ]; then
export REQUEST_PATH="$path"
export REQUEST_PATH_SANE="$path_sane"

request_fields=""
for i in $(seq 0 $((${#request_keys[@]} - 1))); do
key="${request_keys[$i]}"
value="${request_values[$i]}"
length=$(echo "$value" | wc -w)
request_fields+="$length $key $value "
done
export REQUEST_FIELDS="$request_fields"

# GET, static content directory defined and matches a file
if [ "$request" == "GET" -a -n "$STATIC_DIR" -a -n "$path_sane" -a -r "$STATIC_DIR/$path_sane" ]; then
filetype=$(echo "$path_sane" | sed 's/^.*\.//')
Expand Down Expand Up @@ -43,21 +55,37 @@ EOF
# GET, get handler registered
if [ "$request" == "GET" -a -n "$GET_HANDLER" ]; then
echo "[$(date +%T)] GET dynamic, path: $path; path_sane: $path_sane" >> connection.log
request_fields=""
for i in $(seq 0 $((${#request_keys[@]} - 1))); do
key="${request_keys[$i]}"
value="${request_values[$i]}"
length=$(echo "$value" | wc -w)
request_fields+="$length $key $value "
done

export REQUEST_PATH="$path"
export REQUEST_PATH_SANE="$path_sane"
$GET_HANDLER
ret=$?
[ $ret -eq 0 ] && exit 0
fi

# POST, post handler registered
if [ "$request" == "POST" -a -n "$POST_HANDLER" ]; then
length=0
for i in $(seq 0 $(( ${#request_keys[@]} - 1 )) ); do
if [ "${request_keys[$i]}" == "Content-Length" ]; then
length=${request_values[$i]}
break
fi
done
read -r -n "$length" data
echo "[$(date +%T)] POST path: $path; path_sane: $path_sane, content-length: $length" >> connection.log
echo "[$(date +%T)] Data: '$data'" >> connection.log

$GET_HANDLER $request_fields
export POST_DATA="$data"
$POST_HANDLER
ret=$?
[ $ret -eq 0 ] && exit 0
fi

# Can't handle the request, better 404 for now
cat <<EOF
$($BASHSERV_DIR/header.sh -t "text/plain" -l 16 404)
File not found!\n
EOF
exit 0
fi

# GET request - GET <path> HTTP/x.x
Expand All @@ -67,6 +95,13 @@ EOF
path_sane=$(echo "$path" | sed 's/^[./]*//')
continue
fi
# POST request - POST <path> HTTP/x.x
if [[ "$line" =~ ^POST ]]; then
request="POST"
path=$(echo "$line" | cut -d ' ' -f2)
path_sane=$(echo "$path" | sed 's/^[./]*//')
continue
fi

# Parse request fields of form Key: Value
key=$(echo "$line" | cut -d ':' -f1 | sed 's/^\s*//')
Expand Down

0 comments on commit ef70aa4

Please sign in to comment.