diff --git a/README.md b/README.md index d06583c..cd5306c 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ We have some guides and general documentation in the [docs](docs/) directory. Am - [HTTP Router](docs/router.md) - [Template engine](docs/template.md) - [Script integrations](docs/util.md) +- [HTTP request API](docs/http.md) - [List of security fixes](docs/sec-fixes/) ## Dependencies diff --git a/docs/http.md b/docs/http.md new file mode 100644 index 0000000..0dcd150 --- /dev/null +++ b/docs/http.md @@ -0,0 +1,43 @@ +# HTTP - the proto, the API + +very work in progress file. + +--- + +## GET/POST parameters + +- `${get_data["param"]}` +- `${post_data["param"]}` + +Case-insensitive. If K/V pairs aren't used (but a string is provided, just without `=`) then +it can be accessed through `${get_data}` / `${post_data}` (array element 0). + +### Arrays + +HTTP does arrays through concatenating multiple parameters with the same name. In our case, values +are passed to a secondary array, and a reference to it is left for application use. + +``` +GET asdf/?a=1&a=2&a=3&b=1 +``` + +will result in: + +``` +declare -A get_data=([b]="1" [a]="[array]" ) +``` + +To get the value set out of an array, call `http_array `. For instance: + +``` +#!/bin/bash +if ! http_array a array; then + echo "Not an array" + return +fi + +for (( i=0; i<${#array[@]}; i++ )) { + echo "a[$i]=${array[i]}" +} +``` + diff --git a/src/misc.sh b/src/misc.sh index 30caf1b..6d6b7ac 100755 --- a/src/misc.sh +++ b/src/misc.sh @@ -71,3 +71,58 @@ function url_decode() { function worker_add() { : } + +# internal function +# common GET/POST application/x-www-form-urlencoded parser +# +# _param_parse(input, destination_ref) +_param_parse() { + [[ ! "$1" || ! "$2" ]] && return 1 + local -n ref="$2" + + local i name value + + while read -d'&' i; do + name="${i%%=*}" + if [[ "$name" ]]; then + value="${i#*=}" + if [[ "${ref["$name"]}" ]]; then # array mode + if [[ ! "${http_array_refs["$name"]}" ]]; then + http_array_refs["$name"]=_param_$RANDOM + local -n arr="${http_array_refs["$name"]}" + + arr=("${ref["$name"]}") + ref["$name"]="[array]" + else + local -n arr="${http_array_refs["$name"]}" + fi + + arr+=("$(url_decode "$value")") + else + ref["$name"]="$(url_decode "$value")" + fi + fi + done <<< "$1" +} + + +# Safely receive a reference to a HTTP urlencoded array +# +# http_array(name, out_ref) +http_array() { + [[ ! "$1" || ! "$2" ]] && return 1 + if [[ ! "${http_array_refs[$1]}" ]]; then + declare -ga $2 + local -n ref=$2 + + if [[ "${post_data[$1]}" ]]; then + ref=("${post_data[$1]}") + elif [[ "${get_data[$1]}" ]]; then + ref=("${get_data[$1]}") + else + return 1 + fi + else + declare -gn $2=${http_array_refs[$1]} + fi +} diff --git a/src/server.sh b/src/server.sh index a6bbb78..ebe16c9 100755 --- a/src/server.sh +++ b/src/server.sh @@ -23,6 +23,7 @@ declare -A cookies # cookies! declare -A get_data # all GET params declare -A post_data # all POST params declare -A params # parsed router data +declare -A http_array_refs # references to GET/POST arrays r[status]=210 # Mommy always said that I was special r[req_headers]='' @@ -44,16 +45,7 @@ if [[ "${param,,}" =~ ^(get|post|patch|put|delete|meow) ]]; then # TODO: OPTIONS r[url]="$(sed -E 's/^ *//;s/HTTP\/[0-9]+\.[0-9]+//;s/ //g;s/\/*\r//g;s/\/\/*/\//g' <<< "$param")" unset IFS - if [[ "${r[url]}" == *'?'* ]]; then - while read -d'&' i; do - name="${i%%=*}" - if [[ "$name" ]]; then - value="${i#*=}" - get_data[$name]="$(url_decode "$value")" - fi - done <<< "${r[url]#*\?}&" - fi - + _param_parse "${r[url]#*\?}&" get_data else exit 1 # TODO: throw 400 here fi @@ -272,12 +264,7 @@ if [[ "${r[post]}" == true ]] && [[ "${r[status]}" == 200 || "${r[status]}" == if [[ "${r[payload_type]}" == "urlencoded" ]]; then unset IFS - while read -r -d'&' i; do - name="${i%%=*}" - value="${i#*=}" - post_data[$name]="$(url_decode "$value")" - echo post_data[$name]="$value" >/dev/stderr - done <<< "${data}&" + _param_parse "${data}&" post_data else # this is fine? post_data[0]="${data%\&}" diff --git a/tests/01-http-basic.sh b/tests/01-http-basic.sh index e89b6c5..3b327ef 100644 --- a/tests/01-http-basic.sh +++ b/tests/01-http-basic.sh @@ -30,19 +30,26 @@ EOF match="nyaa" } -server_get_random() { +server_get_array() { prepare() { cat <<"EOF" > app/webroot/meow.shs #!/bin/bash -echo "${get_data[meow]}" +http_array meow ref +echo "${ref[1]}" EOF } tst() { - curl -s "localhost:1337/meow.shs?meow=nyaa" + curl -s "localhost:1337/meow.shs?meow=nyaa&meow=second+element&meow=meow" } - match="nyaa" + match="second element" +} + +server_post_array() { + tst() { + curl -s "localhost:1337/meow.shs" -d 'meow=nyaa&meow=second+element&meow=meow' + } } server_post_param() { @@ -202,6 +209,9 @@ subtest_list=( server_get_param server_post_param + server_get_array + server_post_array + # currently functionally equivalent server_patch_dummy server_put_dummy