Ben Ashton
2 years ago
commit
a18f52b6a1
3 changed files with 197 additions and 0 deletions
@ -0,0 +1,50 @@ |
|||||||
|
# Exprash |
||||||
|
|
||||||
|
An Express-like HTTP router for Bash compatible with CGI 1.1 |
||||||
|
|
||||||
|
I'm aware that the name of this project evokes a feeling of physical disgust. That is by design. |
||||||
|
|
||||||
|
## Usage/Examples |
||||||
|
|
||||||
|
Here is some leaked source code for my brand new Cat website. Do not steal! |
||||||
|
|
||||||
|
```shell |
||||||
|
#!/bin/bash |
||||||
|
printf '%s\n\n' 'Content-Type: text/html'; |
||||||
|
|
||||||
|
source ../src/exprash.sh; |
||||||
|
|
||||||
|
declare -A cats; |
||||||
|
cats[calico]="Calico"; |
||||||
|
cats[sphynx]="Sphynx"; |
||||||
|
cats[ragdoll]="Ragdoll"; |
||||||
|
cats[scottish_fold]="Scottish Fold"; |
||||||
|
|
||||||
|
get '/' && { |
||||||
|
printf '<h1>Cats</h1>\n'; |
||||||
|
printf '<ul>\n'; |
||||||
|
for key in "${!cats[@]}"; do |
||||||
|
printf '<li><a href="cats/%s">%s</a></li>\n' "$key" "${cats[$key]}"; |
||||||
|
done |
||||||
|
printf '</ul>\n'; |
||||||
|
} |
||||||
|
|
||||||
|
get '/cats/:cat' && { |
||||||
|
key=$(param 'cat'); |
||||||
|
|
||||||
|
if [[ -v "cats[$key]" ]]; then |
||||||
|
printf '<h1>Your Cat: %s</h1>\n' "${cats[$key]}"; |
||||||
|
else |
||||||
|
next; |
||||||
|
fi; |
||||||
|
} |
||||||
|
|
||||||
|
all '/cats/:cat' && { |
||||||
|
printf '<h1>Error: Cannot find that cat</h1>\n'; |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
|
||||||
|
## License |
||||||
|
|
||||||
|
[WTFPL](http://www.wtfpl.net/txt/copying/) |
@ -0,0 +1,33 @@ |
|||||||
|
#!/bin/bash |
||||||
|
printf '%s\n\n' 'Content-Type: text/html'; |
||||||
|
|
||||||
|
source ../src/exprash.sh; |
||||||
|
|
||||||
|
declare -A cats; |
||||||
|
cats[calico]="Calico"; |
||||||
|
cats[sphynx]="Sphynx"; |
||||||
|
cats[ragdoll]="Ragdoll"; |
||||||
|
cats[scottish_fold]="Scottish Fold"; |
||||||
|
|
||||||
|
get '/' && { |
||||||
|
printf '<h1>Cats</h1>\n'; |
||||||
|
printf '<ul>\n'; |
||||||
|
for key in "${!cats[@]}"; do |
||||||
|
printf '<li><a href="cats/%s">%s</a></li>\n' "$key" "${cats[$key]}"; |
||||||
|
done |
||||||
|
printf '</ul>\n'; |
||||||
|
} |
||||||
|
|
||||||
|
get '/cats/:cat' && { |
||||||
|
key=$(param 'cat'); |
||||||
|
|
||||||
|
if [[ -v "cats[$key]" ]]; then |
||||||
|
printf '<h1>Your Cat: %s</h1>\n' "${cats[$key]}"; |
||||||
|
else |
||||||
|
next; |
||||||
|
fi; |
||||||
|
} |
||||||
|
|
||||||
|
all '/cats/:cat' && { |
||||||
|
printf '<h1>Error: Cannot find that cat</h1>\n'; |
||||||
|
} |
@ -0,0 +1,114 @@ |
|||||||
|
#!/bin/bash |
||||||
|
# shellcheck disable=SC2034 # false flags nameref params |
||||||
|
|
||||||
|
# ============= |
||||||
|
# Route Methods |
||||||
|
# ============= |
||||||
|
|
||||||
|
# $1: path |
||||||
|
function get() { |
||||||
|
[ "$REQUEST_METHOD" != "GET" ] && return 1; |
||||||
|
all "$1"; |
||||||
|
} |
||||||
|
|
||||||
|
# $1: path |
||||||
|
function post() { |
||||||
|
[ "$REQUEST_METHOD" != "POST" ] && return 1; |
||||||
|
all "$1"; |
||||||
|
} |
||||||
|
|
||||||
|
# $1: path |
||||||
|
function put() { |
||||||
|
[ "$REQUEST_METHOD" != "PUT" ] && return 1; |
||||||
|
all "$1"; |
||||||
|
} |
||||||
|
|
||||||
|
# $1: path |
||||||
|
function delete() { |
||||||
|
[ "$REQUEST_METHOD" != "DELETE" ] && return 1; |
||||||
|
all "$1"; |
||||||
|
} |
||||||
|
|
||||||
|
# $1: path |
||||||
|
function all() { |
||||||
|
[ "$routeHandled" -eq 1 ] && return 1; |
||||||
|
|
||||||
|
# Reset params |
||||||
|
params=(); |
||||||
|
pathMatch "$1" params || return 1; |
||||||
|
|
||||||
|
# # if path ooesn't match PATH_INFO: return 1 |
||||||
|
routeHandled=1; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
function next() { |
||||||
|
routeHandled=0; |
||||||
|
} |
||||||
|
|
||||||
|
# ================= |
||||||
|
# Request Functions |
||||||
|
# ================= |
||||||
|
|
||||||
|
# $1: param name |
||||||
|
function param() { |
||||||
|
printf '%s\n' "${params[$1]}"; |
||||||
|
} |
||||||
|
|
||||||
|
# $1: param name |
||||||
|
function hasParam() { |
||||||
|
[[ -v "params[$1]" ]]; |
||||||
|
} |
||||||
|
|
||||||
|
# ========= |
||||||
|
# Internals |
||||||
|
# ========= |
||||||
|
|
||||||
|
# $1: path |
||||||
|
# $2: (nameref) array |
||||||
|
function pathToArray() { |
||||||
|
readarray -t "$2" < <(printf '%s\n' "$1" | tr '/' '\n' | grep .); |
||||||
|
} |
||||||
|
|
||||||
|
# $1: route |
||||||
|
# $2: (nameref) associative array for params |
||||||
|
function pathMatch() { |
||||||
|
[ -z ${PATH_INFO+x} ] && return 1; |
||||||
|
[ -z ${1+x} ] && return 1; |
||||||
|
|
||||||
|
local path="$PATH_INFO"; |
||||||
|
local route="$1"; |
||||||
|
|
||||||
|
local pathArr; |
||||||
|
pathToArray "$path" pathArr; |
||||||
|
|
||||||
|
local routeArr; |
||||||
|
pathToArray "$route" routeArr; |
||||||
|
|
||||||
|
# Params associative array |
||||||
|
local -n routeParams="$2"; |
||||||
|
|
||||||
|
# Not a match if pathArr and routeArr have different length |
||||||
|
[ "${#pathArr[@]}" -ne "${#routeArr[@]}" ] && return 1; |
||||||
|
|
||||||
|
for ((i=0; i<${#routeArr[@]}; i++)); do |
||||||
|
local routeComponent="${routeArr[$i]}"; |
||||||
|
local pathComponent="${pathArr[$i]}"; |
||||||
|
|
||||||
|
# If route component starts with ":" |
||||||
|
if [[ "$routeComponent" == :* ]]; then |
||||||
|
routeParams["${routeComponent:1}"]="$pathComponent"; |
||||||
|
else |
||||||
|
# Confirm paths match |
||||||
|
[ "$routeComponent" != "$pathComponent" ] && return 1; |
||||||
|
fi; |
||||||
|
done; |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
# ======= |
||||||
|
# Globals |
||||||
|
# ======= |
||||||
|
routeHandled=0; |
||||||
|
declare -A params; |
Loading…
Reference in new issue