Skip to content

Instantly share code, notes, and snippets.

@piksel
Created March 16, 2024 18:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save piksel/7768184ac7391190277d824ec9df3485 to your computer and use it in GitHub Desktop.
Save piksel/7768184ac7391190277d824ec9df3485 to your computer and use it in GitHub Desktop.
#!/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