#!/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