You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
265 lines
5.0 KiB
265 lines
5.0 KiB
#!/bin/bash |
|
# shellcheck disable=SC2034 # false flags nameref params |
|
|
|
# ===================== |
|
# Require shell options |
|
# ===================== |
|
|
|
# Necessary for piping to "send" funciton |
|
shopt -s lastpipe |
|
|
|
# =============== |
|
# Setup Functions |
|
# =============== |
|
|
|
# $1: Log file to redirect output |
|
function redirectStdout() { |
|
local output="$1"; |
|
[ -z "$output" ] && output="/dev/null"; |
|
|
|
exec 5<&1; |
|
_exprashRedirectStdout="$1"; |
|
exec 1>>"$_exprashRedirectStdout"; |
|
printf '%s: %s\n' "${REQUEST_METHOD:-UNKNOWN}" "${PATH_INFO:-/}" |
|
} |
|
|
|
# ============= |
|
# Route Function |
|
# ============= |
|
|
|
# $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() { |
|
[ "$_exprashRouteHandled" -eq 1 ] && return 1; |
|
[ -n "$_exprashErrorMessage" ] && return 1; |
|
|
|
# Reset params |
|
_exprashParams=(); |
|
|
|
pathMatch "$1" _exprashParams || return 1; |
|
_exprashRouteHandled=1; |
|
|
|
return 0; |
|
} |
|
|
|
function use() { |
|
[ "$_exprashRouteHandled" -eq 1 ] && return 1; |
|
[ -n "$_exprashErrorMessage" ] && return 1; |
|
|
|
_exprashRouteHandled=1; |
|
return 0; |
|
} |
|
|
|
# ===================== |
|
# Error Route Functions |
|
# ===================== |
|
|
|
# $1: path |
|
function getError() { |
|
[ "$REQUEST_METHOD" != "GET" ] && return 1; |
|
allError "$1"; |
|
} |
|
|
|
# $1: path |
|
function postError() { |
|
[ "$REQUEST_METHOD" != "POST" ] && return 1; |
|
allError "$1"; |
|
} |
|
|
|
# $1: path |
|
function putError() { |
|
[ "$REQUEST_METHOD" != "PUT" ] && return 1; |
|
allError "$1"; |
|
} |
|
|
|
# $1: path |
|
function deleteError() { |
|
[ "$REQUEST_METHOD" != "DELETE" ] && return 1; |
|
allError "$1"; |
|
} |
|
|
|
# $1: path |
|
function allError() { |
|
[ "$_exprashRouteHandled" -eq 1 ] && return 1; |
|
[ -z "$_exprashErrorMessage" ] && return 1; |
|
|
|
# Reset params |
|
_exprashParams=(); |
|
|
|
pathMatch "$1" _exprashParams || return 1; |
|
_exprashRouteHandled=1; |
|
|
|
return 0; |
|
} |
|
|
|
function useError() { |
|
[ "$_exprashRouteHandled" -eq 1 ] && return 1; |
|
[ -z "$_exprashErrorMessage" ] && return 1; |
|
|
|
_exprashRouteHandled=1; |
|
return 0; |
|
} |
|
|
|
# ==== |
|
# Next |
|
# ==== |
|
|
|
# $1 (optional): error message |
|
function next() { |
|
_exprashRouteHandled=0; |
|
[ -n "$1" ] && _exprashErrorMessage="$1"; |
|
} |
|
|
|
# ============= |
|
# App Functions |
|
# ============= |
|
|
|
function errorMessage() { |
|
printf '%s' "$_exprashErrorMessage"; |
|
} |
|
|
|
function hasErrorMessage() { |
|
[ -n "$_exprashErrorMessage" ]; |
|
} |
|
|
|
# ================= |
|
# Request Functions |
|
# ================= |
|
|
|
# $1: param name |
|
function param() { |
|
printf '%s\n' "${_exprashParams[$1]}"; |
|
} |
|
|
|
function hasParam() { |
|
[[ -v "_exprashParams[$1]" ]]; |
|
} |
|
|
|
# ================== |
|
# Response Functions |
|
# ================== |
|
|
|
function send() { |
|
if [ "$_exprashHeadersSent" -eq 0 ]; then |
|
sendHeaders |
|
_exprashHeadersSent=1 |
|
fi |
|
|
|
sendRaw |
|
} |
|
|
|
# $1: Header Name |
|
# $2: Header Value |
|
function setHeader() { |
|
_exprashHeaders["$1"]="$2"; |
|
} |
|
|
|
function sendHeaders() { |
|
printf '%s\n' "Content-Type: ${_exprashHeaders['Content-Type']}" | sendRaw |
|
for key in "${!_exprashHeaders[@]}"; do |
|
[ "$key" == 'Content-Type' ] && continue |
|
printf '%s\n' "${key}: ${_exprashHeaders[$key]}" | sendRaw |
|
done; |
|
printf '\n' | sendRaw |
|
} |
|
|
|
# ========= |
|
# Internals |
|
# ========= |
|
|
|
function sendRaw() { |
|
[ -n "$_exprashRedirectStdout" ] && exec >&5; |
|
cat; |
|
[ -n "$_exprashRedirectStdout" ] && exec 1>>"$_exprashRedirectStdout"; |
|
} |
|
|
|
# $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"; |
|
|
|
# Params associative array |
|
local -n routeParams="$2"; |
|
|
|
local pathArr; |
|
pathToArray "$path" pathArr; |
|
|
|
local routeArr; |
|
pathToArray "$route" routeArr; |
|
|
|
# Get max path length |
|
local routeLen=${#routeArr[@]}; |
|
local pathLen=${#pathArr[@]}; |
|
local maxLen=$(( routeLen >= pathLen ? routeLen : pathLen )); |
|
|
|
for ((i=0; i<maxLen; i++)); do |
|
local routeComponent="${routeArr[$i]}"; |
|
local pathComponent="${pathArr[$i]}"; |
|
|
|
# If route component starts with ":" |
|
if [[ "$routeComponent" == :* ]]; then |
|
routeParams["${routeComponent:1}"]="$pathComponent"; |
|
elif [[ "$routeComponent" == '*' ]] && [ -n "$pathComponent" ]; then |
|
continue; |
|
elif [[ "$routeComponent" == '**' ]] && [ -n "$pathComponent" ]; then |
|
break; |
|
else |
|
# Confirm paths match |
|
[ "$routeComponent" != "$pathComponent" ] && return 1; |
|
fi; |
|
done; |
|
|
|
return 0; |
|
} |
|
|
|
# ======= |
|
# Globals |
|
# ======= |
|
|
|
# Route globals |
|
function _exprashResetRouteGlobals() { |
|
_exprashRouteHandled=0 |
|
_exprashErrorMessage='' |
|
declare -gA _exprashParams |
|
|
|
_exprashHeadersSent=0 |
|
declare -gA _exprashHeaders |
|
_exprashHeaders['Content-Type']='text/html' |
|
} |
|
_exprashResetRouteGlobals |
|
|
|
# Setup globals |
|
_exprashRedirectStdout=''
|
|
|