Created
March 16, 2024 18:36
-
-
Save piksel/7768184ac7391190277d824ec9df3485 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
tmp_dir=${TMPDIR:-/tmp} | |
max_itm_len=24 | |
max_val_len=100 | |
hth_key_col=35 | |
hth_val_col=97 | |
hth_itk_col=94 | |
hth_itv_col=97 | |
sec_hdr_col=96 | |
_c="\033[" | |
_r="${_c}m" | |
function confirm() { | |
while true; do | |
read -r -p "$1" | |
case $REPLY in | |
[Yy]* ) return 0;; | |
[Nn]* ) return 1;; | |
* ) echo -e "Please answer ${_c}92mY${_c}m or ${_c}91mN${_c}m.\n";; | |
esac | |
done | |
} | |
function log() { | |
if (($silent)); then return; fi | |
echo -e "$@" | |
} | |
function warn(){ | |
echo -e "${_c}93mWarning${_c}m: $@" | |
} | |
function debug() { | |
if ((!$verbose)); then return; fi | |
echo -e "$@" | |
} | |
function section() { | |
if [[ "$1" == "debug" ]]; then | |
debug "\n${_c}90m==[${_c}${sec_hdr_col}m ${@:2} ${_c}90m]==${_c}m" | |
else | |
log "\n${_c}90m==[${_c}${sec_hdr_col}m $@ ${_c}90m]==${_c}m" | |
fi | |
} | |
function print_since { | |
local T | |
let T=( $(date +%s) - $1 ) | |
local D=$((T/60/60/24)) | |
local H=$((T/60/60%24)) | |
local M=$((T/60%60)) | |
local S=$((T%60)) | |
printf '\x1b[1m' | |
(( $D == 1 )) && printf 'one day ' $D | |
(( $D >= 2 )) && printf '%d days ' $D | |
(( $H == 1 )) && printf 'one hour ' $D | |
(( $H >= 2 )) && printf '%d hours ' $H | |
(( $M == 1 )) && printf 'one minute ' $M | |
(( $M >= 2 )) && printf '%d minutes ' $M | |
(( $D > 0 || $H > 0 || $M > 0 )) && printf '\x1b[0mand \x1b[1m' | |
printf '%d second(s)\x1b[0m ago' $S | |
} | |
function print_set_cookie_header { | |
local kvs | |
local kv | |
local kc=${hth_itk_col} | |
local vc=${hth_val_col} | |
IFS=';' read -ra kvs <<<"$@" | |
for ((i = 0; i < ${#kvs[@]}; i++)); do | |
if (($i == 1)); then | |
let kc-=60 | |
let vc-=60 | |
elif (($i)); then | |
printf ',' | |
fi | |
IFS='=' read -ra kv <<<"${kvs[$i]}" | |
printf "${_c}${kc}m%s${_c}m" "${kv[0]}" | |
if ((${#kv[@]} > 1)); then | |
printf "${_c}90m=${_c}${vc}m%.${max_itm_len}s${_c}m" "${kv[1]}" | |
if ((${#kv[1]} > $max_itm_len)); then | |
printf "${_c}90m...${_c}m" | |
fi | |
fi | |
if ((!$i)); then | |
printf "\n%11s" "" | |
fi | |
done | |
echo -e "${_c}m" | |
} | |
function print_multi_val_header { | |
local kvs | |
local kv | |
local kc=9 | |
local header_key="${2:-aaaaaaaaaaaa}:" | |
local indent=${#header_key} | |
local kvsep=${3:-'='} | |
local itsep=${4:-';'} | |
IFS=$itsep read -ra kvs <<<"$1" | |
for ((i = 0; i < ${#kvs[@]}; i++)); do | |
if (($i)); then printf "$itsep"; fi | |
IFS=$kvsep read -ra kv <<<"${kvs[$i]}" | |
if [[ $kvsep == " " ]]; then printf " "; fi | |
if ((${#kv[@]} > 1)); then | |
printf "${_c}${hth_itk_col}m%s${_c}m" "${kv[0]}" | |
local value="${kv[1]}" | |
printf "${_c}90m${kvsep}${_c}${hth_itv_col}m%.${max_itm_len}s${_c}m" "${value}" | |
if ((${#value} > $max_itm_len)); then | |
printf "${_c}90m...${_c}m" | |
fi | |
else | |
printf "${_c}${hth_val_col}m%s${_c}m" "${kv[0]}" | |
fi | |
done | |
echo -e "${_c}m" | |
} | |
function print_multi_val_header_arr { | |
local kvs | |
local kv | |
local kc=3 | |
local header_key="${2:-aaaaaaaaaaaa}:" | |
local indent=${#header_key} | |
local kvsep=${3:-'='} | |
local itsep=${4:-';'} | |
IFS=$itsep read -ra kvs <<<"$1" | |
for ((i = 0; i < ${#kvs[@]}; i++)); do | |
if (($i)); then printf "$itsep"; fi | |
# if (($i)); then printf ";\n%${indent}s" ""; fi | |
IFS=' ' read -ra kv <<<"${kvs[$i]}" | |
printf " ${_c}${hth_itk_col}m%s${_c}m" "${kv[0]}" | |
if ((${#kv[@]} > 1)); then | |
local values="${kv[@]:1}" | |
local value_count | |
let value_count=(${#kv[@]} - 1) | |
if ((${#values} > $max_itm_len)); then | |
local pad | |
let pad=(12 - ${#kv[0]}) | |
pad=0 | |
printf "${_c}90m %${pad}s[${_c}35m%d${_c}90m values]${_c}m" "" "${value_count}" | |
else | |
printf "${_c}90m ${_c}${kc}7m%.${max_itm_len}s${_c}m" "${values}" | |
fi | |
fi | |
done | |
echo -e "${_c}m" | |
} | |
function print_value_header { | |
local value=$1 | |
local vc=${2:-37} | |
printf "${_c}${vc}m%.${max_val_len}s" "${value}" | |
if ((${#value} > $max_val_len)); then | |
printf "${_c}90m..." | |
fi | |
printf "${_c}m\n" | |
} | |
function print_http_headers { | |
while read -r line; do | |
pifs=$IFS | |
IFS=':' | |
read -ra parts <<<"$line" | |
key="${parts[0]}" | |
value="${parts[@]:1}" | |
IFS=$pifs | |
if (( ${#key} < 2 )); then | |
continue; | |
fi | |
if [[ "${key}" == HTTP/* ]]; then | |
hp=($key) | |
status="${hp[1]}" | |
rest="${hp[@]:2}" | |
if (($status < 300)); then | |
status_col=2 | |
elif (($status < 400)); then | |
status_col=3 | |
else | |
status_col=1 | |
fi | |
printf "\n${_c}97m%s ${_c}9${status_col}m%d ${_c}37m%s${_c}m\n" "${hp[0]}" "${hp[1]}" "${rest:-}" | |
continue | |
fi | |
echo -en "${_c}${hth_key_col}m${key}${_c}m:" | |
case ${key,,} in | |
"content-type"|"transfer-encoding"|"pragma"|"strict-transport-security"|\ | |
"cross-origin-opener-policy"|"alt-svc") | |
print_multi_val_header "$value" "$key" ;; | |
"cache-control"|"permissions-policy") | |
print_multi_val_header "$value" "$key" "=" "," ;; | |
"expires"|"date") | |
print_value_header "$value" "${hth_val_col}" ;; | |
"set-cookie") | |
print_set_cookie_header "$value" ;; | |
"content-security-policy") | |
print_multi_val_header "$value" "$key" " " ;; | |
'') | |
;; | |
*) | |
print_value_header "$value" ;; | |
esac | |
done < "$1" | |
} | |
if [ "$1" == "clean" ]; then | |
files=("${tmp_dir}"/jqurl-out.*) | |
if [[ "$files" =~ \* ]]; then | |
echo "No files to clean found in ${tmp_dir}!" | |
exit 0 | |
fi | |
echo -e "Removing ${_c}95m${#files[@]}${_c}m file(s)..." | |
for f in ${files[@]}; do | |
echo -e " ${_c}90m${tmp_dir}/${_c}97m$(basename $f)${_c}m" | |
rm "$f" | |
done | |
echo -e "\nDone!" | |
exit 0 | |
fi | |
verbose=0 | |
silent=0 | |
process_jq=0 | |
use_cached=0 | |
dump_headers=0 | |
curl_args=("-H" 'Accept: application/json' "-L") | |
jq_args=("-C") | |
arg_num=0 | |
while (( "$#" )); do | |
a=$1 | |
non_opt=0; [[ $a == -* ]] || non_opt=$? | |
case "$a" in | |
"--") | |
process_jq=1 | |
;; | |
"--cached") | |
use_cached=1 | |
;; | |
"--silent"|"-s") | |
silent=1 | |
verbose=0 | |
curl_args+=("$a") | |
;; | |
"--verbose"|"-v") | |
silent=0 | |
verbose=1 | |
curl_args+=("$a") | |
;; | |
"--include"|"-i") | |
dump_headers=1 | |
;; | |
*) | |
if (($process_jq)); then | |
jq_args+=("$a") | |
else | |
# FIXME: This hack only detects URLs when they are starting with "htt" which might not always be the case | |
# As a fallback, the first argument is used if its not a option argument | |
if [[ $a == htt* ]] || (( $non_opt && $arg_num == 0 )); then | |
url_hash=$(echo -n "$a" | shasum -a 1 | cut -d' ' -s -f1) | |
fi | |
curl_args+=("$a") | |
fi | |
;; | |
esac | |
let arg_num+=1 | |
shift | |
done | |
if [ -z "$url_hash" ]; then | |
output=$(mktemp jqurl-out.XXXXXXXXXX) | |
if (($use_cached)); then | |
warn "Cannot determine URL. Cache will not be used!" | |
use_cached=0 | |
fi | |
else | |
debug "Using URL hash: ${_c}36m${url_hash}${_c}m" | |
output="${tmp_dir}/jqurl-out.${url_hash}" | |
fi | |
debug "Using temp file: ${_c}36m${output}${_c}m" | |
curl_args+=("-o" "$output") | |
if (($dump_headers)); then | |
curl_args+=("-D" "${output}-headers") | |
fi | |
section debug "cURL args" | |
for ((i = 0; i < ${#curl_args[@]}; i++)); do | |
debug " ${_c}95m$i${_c}m: ${_c}33m${curl_args[$i]}${_c}m" | |
done | |
section debug "jq args:" | |
for ((i = 0; i < ${#jq_args[@]}; i++)); do | |
debug " ${_c}95m$i${_c}m: ${_c}33m${jq_args[$i]}${_c}m" | |
done | |
if [ $use_cached -eq 1 ] && [ ! -f "$output" ]; then | |
warn "The output file does not exist!" | |
if confirm "Fetch from source? "; then | |
use_cached=0 | |
else | |
exit 1 | |
fi | |
fi | |
run_status=0 | |
if [ $use_cached -eq 0 ]; then | |
# Clear file contents if it exists | |
if [ -f "$output" ]; then | |
echo -n > $output | |
fi | |
section cURL | |
if ((!$silent)); then | |
echo -e "${_c}90m" | |
fi | |
curl "${curl_args[@]}" | |
curl_status=$? | |
echo -en "${_c}m" | |
if ((!$verbose && !$silent)); then echo; fi | |
else | |
if (($silent)); then echo; fi | |
echo -en "Using cached result, updated " | |
print_since $(stat -c %Y "$output") | |
echo -e "${_c}m." | |
fi | |
if (($dump_headers)); then | |
if [[ ! -f "${output}-headers" ]]; then | |
warn "The output was not cached with headers!" | |
if (($silent)); then echo; fi | |
else | |
section HTTP headers | |
print_http_headers "${output}-headers" | |
if (($silent)); then echo; fi | |
fi | |
fi | |
if [ $run_status -eq 0 ]; then | |
section jq | |
jq "${jq_args[@]}" < $output | |
run_status=$? | |
fi | |
if [ $run_status -ne 0 ] && [ -f "$output" ]; then | |
echo -e "\n${_c}93mFull response:${_c}97m" | |
cat $output | |
echo -e "${_c}m" | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment