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