Initial commit
This commit is contained in:
commit
a18f52b6a1
50
README.md
Normal file
50
README.md
Normal file
@ -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/)
|
33
examples/cats.sh
Normal file
33
examples/cats.sh
Normal file
@ -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';
|
||||||
|
}
|
114
src/exprash.sh
Normal file
114
src/exprash.sh
Normal file
@ -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
Block a user