Exprash/src/exprash.sh

266 lines
5.0 KiB
Bash
Raw Normal View History

2022-09-28 22:05:02 -07:00
#!/bin/bash
# shellcheck disable=SC2034 # false flags nameref params
2022-09-29 22:35:02 -07:00
# ======================
# Required shell options
# ======================
# Necessary for piping to "send" funciton
shopt -s lastpipe
# ===============
# Setup Functions
# ===============
2022-09-29 22:35:02 -07:00
# $1: Log file for redirecting stdout to
function redirectStdout() {
local output="$1";
[ -z "$output" ] && output="/dev/null";
exec 5<&1;
2022-09-29 22:35:02 -07:00
_exprashRedirectStdout="$output";
exec 1>>"$_exprashRedirectStdout";
printf '%s: %s\n' "${REQUEST_METHOD:-UNKNOWN}" "${PATH_INFO:-/}"
}
2022-09-28 22:05:02 -07:00
# =============
# Route Function
2022-09-28 22:05:02 -07:00
# =============
# $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;
2022-09-28 22:05:02 -07:00
# Reset params
_exprashParams=();
pathMatch "$1" _exprashParams || return 1;
_exprashRouteHandled=1;
2022-09-28 22:05:02 -07:00
2022-09-28 22:33:08 -07:00
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;
2022-09-28 22:05:02 -07:00
return 0;
}
function useError() {
[ "$_exprashRouteHandled" -eq 1 ] && return 1;
[ -z "$_exprashErrorMessage" ] && return 1;
_exprashRouteHandled=1;
return 0;
}
# ====
# Next
# ====
# $1 (optional): error message
2022-09-28 22:05:02 -07:00
function next() {
_exprashRouteHandled=0;
[ -n "$1" ] && _exprashErrorMessage="$1";
}
# =============
# App Functions
# =============
function errorMessage() {
printf '%s' "$_exprashErrorMessage";
}
function hasErrorMessage() {
[ -n "$_exprashErrorMessage" ];
2022-09-28 22:05:02 -07:00
}
# =================
# Request Functions
# =================
# $1: param name
function param() {
printf '%s\n' "${_exprashParams[$1]}";
2022-09-28 22:05:02 -07:00
}
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
2022-09-28 22:05:02 -07:00
}
# =========
# Internals
# =========
function sendRaw() {
[ -n "$_exprashRedirectStdout" ] && exec >&5;
cat;
[ -n "$_exprashRedirectStdout" ] && exec 1>>"$_exprashRedirectStdout";
}
2022-09-28 22:05:02 -07:00
# $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";
2022-09-28 22:05:02 -07:00
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 ));
2022-09-28 22:05:02 -07:00
for ((i=0; i<maxLen; i++)); do
2022-09-28 22:05:02 -07:00
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;
2022-09-28 22:05:02 -07:00
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=''