diff --git a/http.class.sh b/http.class.sh new file mode 100644 index 0000000000000000000000000000000000000000..1061a0e59f27209066848d8f313a7e3071445b4a --- /dev/null +++ b/http.class.sh @@ -0,0 +1,1084 @@ +#!/bin/bash +# ====================================================================== +# +# REST API CLIENT USING CURL +# +# REQUIREMENTS +# - Bash (Linux or MS Windows i.e with Cygwin) +# - curl +# - sha1sum (optional; for export functionality with AUTOFILE only) +# ---------------------------------------------------------------------- +# License: GPL 3.0 +# Source: <https://git-repo.iml.unibe.ch/iml-open-source/bash-rest-api-client> +# Docs: <https://os-docs.iml.unibe.ch/bash-rest-api-client/> +# ---------------------------------------------------------------------- +# (1) source this script +# (2) enter "http.help" to get a list of available commands +# ---------------------------------------------------------------------- +# 2020-02-07 v0.2 axel.hahn@iml.unibe.ch BETABETA +# 2020-02-12 v0.4 axel.hahn@iml.unibe.ch Caching +# 2020-03-02 v0.5 axel.hahn@iml.unibe.ch a few more response check functions +# 2021-01-21 v0.6 axel.hahn@iml.unibe.ch add Content-type in request header +# 2022-01-11 v0.7 axel.hahn@iml.unibe.ch fixes using shellcheck +# 2024-10-09 v0.8 axel.hahn@unibe.ch add setAuthorization; customize accept header, add custom request headers +# 2024-10-10 v0.9 axel.hahn@unibe.ch update docs +# 2024-10-23 v0.10 axel.hahn@unibe.ch update help +# 2024-11-20 v0.11 axel.hahn@unibe.ch no insecure requests by default; add setInsecure, setCA, addCurlparam +# ====================================================================== + + http_cfg__about="Bash REST API client v0.11" + typeset -i http_cfg__debug=0 + typeset -i http_cfg__cacheTtl=0 + http_cfg__cacheDir=/var/tmp/http-cache + http_cfg__UA="${http_cfg__about}" + http_cfg__prjurl="https://git-repo.iml.unibe.ch/iml-open-source/bash-rest-api-client" + http_cfg__docsurl="https://os-docs.iml.unibe.ch/bash-rest-api-client/" + +# --- curl meta infos to collect +# see variables in man curl --write-out param + curlMeta="\ + http_code \ + http_connect \ + local_ip \ + local_port \ + num_connects \ + num_redirects \ + redirect_url \ + remote_ip \ + remote_port \ + size_download \ + size_header \ + size_request \ + size_upload \ + speed_download \ + speed_upload \ + ssl_verify_result \ + time_appconnect \ + time_connect \ + time_namelookup \ + time_pretransfer \ + time_redirect \ + time_starttransfer \ + time_total \ + url_effective \ +" + + +# ---------------------------------------------------------------------- +# +# functions +# +# ---------------------------------------------------------------------- + + # ...................................................................... + # + # Write a debug message to STDERR + # Do no not change the prefix - is is read in inc_functions + # + # params strings output message + # + function http._wd(){ + if [ $http_cfg__debug -gt 0 ]; then + echo -e "\e[33m# RESTAPI::DEBUG $*\e[0m" >&2 + fi + } + + # ...................................................................... + # + # Write an error message to STDERR + # Do no not change the prefix - is is read in inc_functions + # + # params strings output message + # + function http._we(){ + echo -e "\e[31m# RESTAPI::ERROR $*\e[0m" >&2 + } + + function http(){ + cat <<EOH + +$http_cfg__about + +A REST API Client with curl + +Enter http.help to show all commands. + +EOH + # $0 is not the current file if we source a script + # grep "function http.[a-z]" $0 | sort + } + + # ...................................................................... + # + # Initialize the client + # + # Initialize the client for a new request. Call this before any other + # function. It will reset all variables. + # + function http.init(){ + http._wd "${FUNCNAME[0]}()" + which curl >/dev/null || http.quit + + # request vars + + http_req__auth= + http_req__authorization= + http_req__accept="application/json" + http_req__headers=() + http_req__curlparams=() + + http_req__body= + http_req__method=GET + http_req__url= + http_req__fullurl= + http_req__docs= + http_req__cafile= + http_req__insecure= + + http_req__dataprefix="RESTAPICLIENTMETADATA_$(date +%s)_$$" + local writevar= + for myvar in $curlMeta + do + writevar="${writevar}|${myvar}:%{${myvar}}" + done + http_curl__writeout="\\n${http_req__dataprefix}${writevar}\\n" + + # cache + http_cfg__cacheTtl=0 + http_cfg__cacheFile= + + # response + http_resp__all= + http_resp__neutral= + mkdir ${http_cfg__cacheDir} 2>/dev/null + chmod 777 ${http_cfg__cacheDir} 2>/dev/null + } + + # ...................................................................... + # + # Execute the request + # + # param string optional: method; GET|POST|PUT|DELETE|... + # param string optional: full url + # param string optional: request body + # + # description: + # + # This function does the following: + # + # 1. Check if to use a cache + # 2. If not cached, make the request + # 3. If cached, use the cached result + # + function http.makeRequest(){ + http._wd "${FUNCNAME[0]}()" + + # --- handle optional prams + if [ $# -ne 0 ]; then + if echo "$1" | grep -q "^[A-Z]*$"; then + http.setMethod "$1" + shift 1 + fi + http.setUrl "$1" + http.setBody "$2" + fi + # test -z "$1" || http.setFullUrl "$1" + + # --- detect caching + local useCache=0 + local makeRequest=1 + if [ $http_cfg__cacheTtl -gt 0 ] && [ "${http_req__method}" = "GET" ]; then + useCache=1 + test -z "${http_cfg__cacheFile}" && http_cfg__cacheFile=$(http._genOutfilename "${http_cfg__cacheDir}/AUTOFILE") + if [ -f "${http_cfg__cacheFile}" ]; then + http.responseImport "${http_cfg__cacheFile}" + local iAge; typeset -i iAge + iAge=$(http.getRequestAge) + http._wd "INFO: Age of cache is $iAge sec - vs TTL $http_cfg__cacheTtl sec - file $http_cfg__cacheFile" + if [ $iAge -gt 0 ] && [ $iAge -lt $http_cfg__cacheTtl ]; then + http._wd "INFO: Using cache" + makeRequest=0 + else + http._wd "INFO: Cache file will be updated after making the request" + rm -f "${http_cfg__cacheFile}" 2>/dev/null + fi + fi + fi + + + # --- make the request + if [ $makeRequest -eq 1 ]; then + + # build curl parameters + local curl_params=( + -s + -i "${http_req__fullurl}" + -X "${http_req__method}" + -A "${http_cfg__UA}" + ) + test -n "$http_req__auth" && curl_params+=( -u "$http_req__auth" ) + test -n "$http_req__authorization" && curl_params+=( -H "Authorization: $http_req__authorization" ) + test -n "$http_req__accept" && curl_params+=( -H "Accept: $http_req__accept" ) + test -n "$http_req__body" && curl_params+=( -d "${http_req__body}" ) + test -n "$http_req__insecure" && curl_params+=( -k ) + test -n "$http_req__cafile" && curl_params+=( --cacert "$http_req__cafile" ) + curl_params+=( "${http_req__headers[@]}" ) + curl_params+=( "${http_req__curlparams[@]}" ) + + http._wd "${FUNCNAME[0]}($1) ${http_req__method} ${http_req__fullurl}" + http._wd "${FUNCNAME[0]}($1) ${curl_params[@]}" + curl_params+=( -w "${http_curl__writeout}" ) + + http_resp__all=$( curl "${curl_params[@]}") || http.quit + + http._wd "OK - Curl finished the http request ... processing data" + http_resp__neutral=$(http._fetchAllAndReformat) + if [ $useCache -eq 1 ]; then + http._wd "INFO: writing cache ..." + http.responseExport "${http_cfg__cacheFile}" + fi + fi + http._wd "Request function finished; Code $(http.getStatuscode)" + } + + # ...................................................................... + # + # Show error message with last return code and quit with this exitcode + # + # This function is used to quit the script with a meaningful error message + # and the exit code of the last command. + # + # The message is printed to STDERR and contains the return code. + # If a documentation URL is known, it is printed as a hint. + # + # The exit code is the one of the last command. + # To prevent the script from exiting when this function is called from a + # sourced file, the exit is commented out. + # + # no params + # + function http.quit(){ + rc=$? + http._wd "${FUNCNAME[0]}()" + echo >&2 + echo -e "\e[31m# ERROR: command FAILED with rc $rc. \e[0m" >&2 + if [ ! -z "${RestApiDocs}" ]; then + echo "HINT: see ${RestApiDocs}" >&2 + fi + # dont make exit in a sourced file + # exit $rc + } + + + # ...................................................................... + # + # Load a config file + # + # This function is marked as deprecated. + # It will be removed in the future. + # + # param string config file name + # + # Sourcing that file will set the following vars + # - RestApiUser + # - RestApiPassword + # - RestApiBaseUrl + # - RestApiDocs + # + # The function will then set the "internal" vars + # - http_req__auth + # - http_req__fullurl + # - http_req__docs + # + function http.loadcfg(){ + http._wd "${FUNCNAME[0]}($1) !!! DEPRECATED !!!" + # reset expected vars from config + RestApiUser= + RestApiPassword= + RestApiBaseUrl= + RestApiDocs= + + # source config file + . "${1}" || http.quit + + # set "internal" vars + if [ -z "$RestApiPassword" ]; then + http.setAuth "$RestApiUser:$RestApiPassword" + else + http.setAuth + fi + http.setBaseUrl "${RestApiBaseUrl}" + http.setDocs "${RestApiDocs}" + } + + # ====================================================================== + # GETTER + # ====================================================================== + + # ...................................................................... + # + # Get the response header or response body + # + # param string what to return; one of header|body + # + # Return the response header or the response body. The output is the same + # as that of http.getResponseHeader or http.getResponse. The difference is + # the implementation + # + function http._fetchResponseHeaderOrBody(){ + http._wd "${FUNCNAME[0]}($1)" + local isheader=true + + # keep leading spaces + IFS='' + + echo "${http_resp__all}" | grep -v "${http_req__dataprefix}" | while read -r line; do + if $isheader; then + if [[ $line = $'\r' ]]; then + isheader=false + else + test "$1" = "header" && echo $line + fi + else + # body="$body"$'\n'"$line" + test "$1" = "body" && echo $line + fi + done + } + + # ...................................................................... + # + # Get the response data + # + # Return the curl meta infos like http_code, http_connect, local_ip, ... + # as key/ value pairs. Each line is a single key value pair. + # + # The data is extracted from the response header. The format is: + # ${http_req__dataprefix}|key|value|key|value|... + # + function http._fetchResponseData(){ + http._wd "${FUNCNAME[0]}()" + echo "${http_resp__all}" | sed "s#${http_req__dataprefix}#\n${http_req__dataprefix}#" | grep "${http_req__dataprefix}" | tail -1 | cut -f 2- -d "|" | sed "s#|#\n#g" | grep -v "${http_req__dataprefix}" | while read -r line; do + echo "$line" + done + } + + # ...................................................................... + # + # Generate the dump with request and response + function http._fetchAllAndReformat(){ + http._wd "${FUNCNAME[0]}()" + IFS='' + line="#------------------------------------------------------------" + + echo "#_META_|about:$http_cfg__about" + echo "#_META_|host:$(hostname -f)" + echo $line + echo "#_REQUEST_|fullurl:$http_req__fullurl" + echo "#_REQUEST_|method:$http_req__method" + echo "#_REQUEST_|time:$(date)" + echo "#_REQUEST_|timestamp:$(date +%s)" + echo "#_REQUEST_|auth:$(echo $http_req__auth | sed 's#:.*#:xxxxxxxx#')" + echo "#_REQUEST_|body:$http_req__body" + echo "#_REQUEST_|baseurl:$http_req__baseurl" + echo "#_REQUEST_|url:$http_req__url" + echo "#_REQUEST_|docs:$http_req__docs" + echo $line + http._fetchResponseHeaderOrBody header | sed "s,^,#_HEADER_|,g" + echo $line + http._fetchResponseData | sed "s,^,#_DATA_|,g" + echo $line + http._fetchResponseHeaderOrBody body | sed "s,^,#_BODY_|,g" + echo $line END + } + + # ...................................................................... + # + # Get a section from dump data + # + # param string what to return; one of HEADER|DATA|BODY + # + # returns string the requested part of the response + # + function http._getFilteredResponse(){ + http._wd "${FUNCNAME[0]}($1)" + echo "${http_resp__neutral}" | grep "^#_${1}_|" | cut -f 2- -d "|" + } + + # ---------- PUBLIC REQUEST GETTER + + # ...................................................................... + # + # Get timestamp of the response as a Unix timestamp. + # + # no param + # + # returns string the timestamp of the response + # + function http.getRequestTs(){ + http._wd "${FUNCNAME[0]}()" + http._getFilteredResponse REQUEST | grep "^timestamp" | cut -f 2 -d ":" + } + + # ...................................................................... + # + # Get age of the response in sec. + # It is especially useful after responseImport + # + # no param + # + # returns integer age of the response in sec + # + function http.getRequestAge(){ + http._wd "${FUNCNAME[0]}()" + local iAge; typeset -i iAge + local iTs; typeset -i iTs + iTs=$( http.getRequestTs ) + iAge=$( date +%s )-${iTs} + echo "$iAge" + } + + # ---------- PUBLIC RESPONSE GETTER + + # ...................................................................... + # + # Get response body + # + # no param + # + # returns string the response body + # + function http.getResponse(){ + http._wd "${FUNCNAME[0]}()" + http._getFilteredResponse BODY + } + + # ...................................................................... + # + # Get curl data of this request with status, transferred bytes, speed, ... + # + # no param + # + # returns string the response data + # + function http.getResponseData(){ + http._wd "${FUNCNAME[0]}()" + http._getFilteredResponse DATA + } + + # ...................................................................... + # + # Get response header + # + # no param + # + # returns string the response header + # + function http.getResponseHeader(){ + http._wd "${FUNCNAME[0]}()" + http._getFilteredResponse HEADER + } + + # ...................................................................... + # + # Get raw response (not available after import) + # + # no params + # + # no param + # + function http.getResponseRaw(){ + http._wd "${FUNCNAME[0]}()" + echo "${http_resp__all}" + } + + # ...................................................................... + # + # Get Http status as string OK|Redirect|Error + # + # no params + # + function http.getStatus(){ + http._wd "${FUNCNAME[0]}($1)" + http.isOk >/dev/null && echo OK + http.isRedirect >/dev/null && echo Redirect + http.isError >/dev/null && echo Error + } + + # ...................................................................... + # + # Get Http status code of the request as 3 digit number + # + # no params + # + function http.getStatuscode(){ + http._wd "${FUNCNAME[0]}()" + http.getResponseData | grep "^http_code:" | cut -f 2 -d ":" + } + + # ...................................................................... + # + # Check: was response a 2xx status code? + # output is a statuscode if it matches ... or empty + # Additionally you can verify the return code + # + # $? -eq 0 means YES + # $? -ne 0 means NO + # + # no params + # + function http.isOk(){ + http._wd "${FUNCNAME[0]}()" + http.getStatuscode | grep '2[0-9][0-9]' + } + + # ...................................................................... + # + # Was the repsonse a redirect? + # + # no params + # + function http.isRedirect(){ + http._wd "${FUNCNAME[0]}()" + http.getStatuscode | grep '3[0-9][0-9]' + } + + # ...................................................................... + # + # Was the repsonse a client error (4xx or 5xx) + # + # no params + # + function http.isError(){ + http._wd "${FUNCNAME[0]}()" + http.getStatuscode | grep '[45][0-9][0-9]' + } + + # ...................................................................... + # + # Was the repsonse a client error (4xx) + # + # no params + # + function http.isClientError(){ + http._wd "${FUNCNAME[0]}()" + http.getStatuscode | grep '4[0-9][0-9]' + } + + # ...................................................................... + # + # Was the repsonse a client error (5xx) + # + # no params + # + function http.isServerError(){ + http._wd "${FUNCNAME[0]}()" + http.getStatuscode | grep '5[0-9][0-9]' + } + + # ...................................................................... + # + # Dump information about request and response + # + # no params + # + function http.dump(){ + http._wd "${FUNCNAME[0]}()" + http.responseExport + } + + # ====================================================================== + # Import/ Export + # ====================================================================== + + # ...................................................................... + # + # Helper to replace "AUTOFILE" with something uniq using full url + # + # param string import or export filename + # + function http._genOutfilename(){ + http._wd "${FUNCNAME[0]}($1)" + if echo "$1" | grep -q "AUTOFILE"; then + echo "$1" + else + local sum + sum=$(echo ${http_req__fullurl} | sha1sum ) + local autofile + autofile=$(echo "${sum}__${http_req__fullurl}" | sed "s#[^a-z0-9]#_#g") + echo "$1" | sed "s#AUTOFILE#${autofile}#" + fi + } + + # ...................................................................... + # + # Export response to a file + # + # param string optional: custom filename + # + function http.responseExport(){ + http._wd "${FUNCNAME[0]}($1)" + if [ -z "$1" ]; then + echo "${http_resp__neutral}" + else + local outfile + outfile=$(http._genOutfilename "$1") + http._wd "${FUNCNAME[0]}($1) writing to outfile $outfile" + echo "${http_resp__neutral}" >"$outfile" + fi + } + + # ...................................................................... + # + # Import a former response from a file + # + # param string filename with stored response + # + function http.responseImport(){ + http._wd "${FUNCNAME[0]}($1)" + local infile + infile=$(http._genOutfilename "$1") + if [ -r "${infile}" ]; then + if grep -q "^#_META_|about:$http_cfg__about" "${infile}"; then + http_resp__neutral=$(cat "${infile}") + else + echo "ERROR: Ooops [${infile}] does not seem to be an export dump." + http.quit + fi + else + echo "ERROR: Ooops the file [${infile}] is not readable." + http.quit + fi + } + + # ...................................................................... + # + # Delete an exported file; this is especially useful if you use + # AUTOFILE functionality + # + # param string filename with stored response + # + function http.responseDelete(){ + http._wd "${FUNCNAME[0]}($1)" + local infile + infile=$(http._genOutfilename "$1") + if [ -r "${infile}" ]; then + if grep -q "^#_META_|about:$http_cfg__about" "${infile}"; then + if rm -f "${infile}"; then + http._wd "OK, ${infile} was deleted." + else + http._wd "ERROR: unable to delete existing ${infile}. Check permissions." + fi + else + http._wd "SKIP: ${infile} is not an export file." + fi + else + http._wd "SKIP: ${infile} is not readable." + fi + } + + # ====================================================================== + # SETTER + # ====================================================================== + + # ...................................................................... + # + # Add a line to the request header + # + # param string line to add, eg "Connection: keep-alive" + # + function http.addHeader(){ + http._wd "${FUNCNAME[0]}($1)" + http_req__headers+=( -H "$1") + } + + # ...................................................................... + # + # Add an additional curl parameter + # + # param string line to add, eg "Connection: keep-alive" + # + function http.addCurlparam(){ + http._wd "${FUNCNAME[0]}($1)" + http_req__curlparams+=( "$1") + } + + # ...................................................................... + # + # set Accept request header and override default + # + # param string accept header value, eg text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 + # + function http.setAccept(){ + http._wd "${FUNCNAME[0]}($1)" + if [ -z "$1" ]; then + http_req__accept= + else + http_req__accept="$1" + fi + } + + # ...................................................................... + # + # Set basic authentication + # Without given parameter, authentication is removed + # + # param string optional: USER:PASSWORD + # + function http.setAuth(){ + http._wd "${FUNCNAME[0]}($1)" + if [ -z "$1" ]; then + http_req__auth= + else + http_req__auth="$1" + fi + } + + # ...................................................................... + # + # Set authentication via Athorization header + # Without given parameter, authorization is removed + # + # param string optional: type, eg. Basic|Bearer|Negotiate + # param string optional: token or encoded user + password + # + function http.setAuthorization(){ + http._wd "${FUNCNAME[0]}($1 $2)" + if [ -z "$1" ]; then + http_req__authorization= + else + http_req__authorization="${1} ${2}" + fi + } + + # ...................................................................... + # + # Set body to send for PUTs and POSTs + # + # param string body + # + function http.setBody(){ + http._wd "${FUNCNAME[0]}($1)" + http_req__body=$1 + } + + # ...................................................................... + # + # Set a base url of an API + # Remark: Then use http.setUrl to complet the url to request + # + # param string url + # + function http.setBaseUrl(){ + http._wd "${FUNCNAME[0]}($1)" + http_req__baseurl=$1 + http.setFullUrl "" + } + + # ...................................................................... + # + # set and unset CA cert file to use + # + # param string optional: filename to use; no value to disable cafile + # + function http.setCA(){ + http._wd "${FUNCNAME[0]}($1)" + http_req__cafile="$1" + } + + # ...................................................................... + # + # Enable or disable debug mode + # + # param integer 0|1 + # + function http.setDebug(){ + http._wd "${FUNCNAME[0]}($1)" + http_cfg__debug=$1 + } + + + function http.setDocs(){ + http._wd "${FUNCNAME[0]}($1)" + http_req__docs=$1 + } + # ...................................................................... + # + # Allow and disallow insecure connections + # + # param string optional: 1 to enable insecure flag; no value to disable insecure flag + # + function http.setInsecure(){ + http._wd "${FUNCNAME[0]}($1)" + http_req__insecure="$1" + } + + # ...................................................................... + # + # Set the method to use; GET|POST|PUT|DELETE|... + # + # param string name of method + # + function http.setMethod(){ + http._wd "${FUNCNAME[0]}($1)" + http_req__method=$1 + } + + # ...................................................................... + # + # Set a full url to request + # + # param string optional: url + # + function http.setFullUrl(){ + http._wd "${FUNCNAME[0]}($1)" + if [ -z "$1" ]; then + http_req__fullurl=${http_req__baseurl}${http_req__url} + else + http_req__fullurl=$1 + fi + } + + # ...................................................................... + # + # Complete the base url + # + # param string url part behind base url + # + function http.setUrl(){ + http._wd "${FUNCNAME[0]}($1)" + http_req__url=$1 + http.setFullUrl + } + + # ----- caching + + # ...................................................................... + # + # Set cache ttl in seconds + # + # param integer ttl in seconds + # + function http.setCacheTtl(){ + http._wd "${FUNCNAME[0]}($1)" + http_cfg__cacheTtl=$1 + } + + # ...................................................................... + # + # Set cache file + # + # param string filename + # + function http.setCacheFile(){ + http._wd "${FUNCNAME[0]}($1)" + http_cfg__cacheFile="$1" + } + + # ...................................................................... + # + # Flush the cache + # + # no params + # + function http.flushCache(){ + http._wd "${FUNCNAME[0]}($1)" + rm -f ${http_cfg__cacheDir}/* + } + + # ...................................................................... + # + # show a help text + # + # no params + # + function http.help(){ + cat <<EOH + +$http_cfg__about + +This is a bash solution to script REST API calls. + +Source: <$http_cfg__prjurl> +Docs: <$http_cfg__docsurl> +License: GNU GPL 3 + + +INSTRUCTION: + +- Source the file once +- Then you can run functions starting with "http." + + http.init + Start a new request. It resets internal vars of the last request + (if there was one). + + http.setDebug 0|1 + Enable or disable debugging infos during processing. It is written + to STDERR. + +- initialize a request + + setAccept "<ACCEPTHEADER>" + Set authentication with user and password for basic auth + Default: $http_req__accept + + setAuth "<USER>:<PASSWORD>" + Set authentication with user and password for basic auth + Without given parameter, authentication is removed + + setAuthorization "<TYPE>" "<TOKEN|HASH>" + Set authentication with Authorization header. + As TYPE you can use Basic|Bearer|Negotiate|... + 2nd param is the token or hashed user+password + Without given parameter, authorization is removed + + http.setBody "<DATA>" + set a body for POST/ PUT requests. + + http.setBaseUrl "<URL>" + Set a base url to an api. + renmark: + Use http.setUrl to built a complete url. + + http.setCA "<FILE>" + Set CA file to verify the server certificate. + Default: [empty] = use system defaults + Without parameter the cafile is removed + + http.setDocs "<URL>" + Set a docs url. If set it will be shown as additional hint when a + request fails. + + http.setInsecure 1 + Set insecure flag by giving any non empty value. + Default: [empty] = secure requests + Without parameter the insecure flag is removed + + http.setMethod "<METHOD>" + Set a http method. Use an uppercase string for GET|POST|PUT|DELETE|... + + http.setFullUrl "<URL>" + Set a complete url for a request. + + http.setUrl "<REQUEST?QUERY>" + Set a relative url for a request. + This requires to use http.setBaseUrl before. + + http.addHeader "<HEADER_LINE>" + Add a header line to the request. + This command can be repeated multiple times to add multiple headers. + + http.addCurlparam "<CURL_PARAMETER>" + Add any missing parameter for curl requestst. + This command can be repeated multiple times to add multiple prameters. + You also can add multiple parameters with one command. + +- caching functions + + http.setCacheTtl <SECONDS> + Enable caching with values > 0 + Remark: only GET requests will be cached. + Default: 0 (no caching) + + http.setCacheFile "<FILENAME>" + Set a file where to read/ store a request + Default: empty; autogenerated file below $http_cfg__cacheDir + + http.flushCache + Delete all files in $http_cfg__cacheDir + +- make the request + + http.makeRequest [[<METHOD>] ["<URL>"] ["<BODY>"]] + The parameters are optional. Without parameter the request will be + started with given data in http.set* functions described above. + If minimum one param is given then they are handled: + METHOD optional: set a method (must be uppercase) - see http.setMethod + URL set a relative url - see http.setUrl + BODY optional: set a body - see http.setBody + + The request will be skipped and uses a cached content if ... + - METHOD is GET + - http.setCacheTtl set a value > 0 + - the cache file exists and is younger than the given TTL + +- handle response + + http.getResponse + Get the Response Body + + http.getResponseData + Get Meta infos from curl + + http.getResponseHeader + Get The http reponse header + +- check http status code + + http.getStatus + Get the http status as string Ok|Redirect|Error + + http.getStatuscode + Get the http status code of a request as 3 digit integer + + http.isOk + Check if the http response code is a 2xx + + http.isRedirect + Check if the http response code is a 3xx + + http.isError + Check if the http response code is a 4xx or 5xx + + http.isClientError + Check if the http response code is a 4xx + + http.isServerError + Check if the http response code is a 5xx + + http.getRequestAge + Get the age of the request in seconds. + Remark: This function is useful after an import + see http.responseImport. + + http.getRequestTs + Get the Unix timestamp of the request + +- import/ export + + http.responseExport ["<FILE>"] + dump the response data + Without parameter it is written on STDOUT. + You can set a filename to write it to a file. + The filename can contain "AUTOFILE" this string + will be replaced with a uniq string. + (requires sha1sum and a set url) + Example: + http.makeRequest "https://example.com/api/" + http.responseExport /tmp/something_AUTOFILE_.txt + + http.responseImport "<FILE>" + Import an export file. + To use the AUTOFILE mechanism from export set + the url first. + Example: + http.setFullUrl "https://example.com/api/" + http.responseImport /tmp/something_AUTOFILE_.txt + + http.responseDelete "<FILE>" + Delete a file after http.responseExport. + It is useful if you use the AUTOFILE mechanism. + +EOH + } + +# ---------------------------------------------------------------------- +# +# main +# +# ---------------------------------------------------------------------- + + http.init + +# ---------------------------------------------------------------------- diff --git a/rest-api-client.sh b/rest-api-client.sh index 0ac9e9d0a66aedb0a7ea3eb973d33e90988245ae..b07340cae95f7208920d12545800d1e8188e9f4c 100644 --- a/rest-api-client.sh +++ b/rest-api-client.sh @@ -1,1027 +1,6 @@ #!/bin/bash -# ====================================================================== -# -# REST API CLIENT USING CURL -# -# REQUIREMENTS -# - Bash (Linux or MS Windows i.e with Cygwin) -# - curl -# - sha1sum (optional; for export functionality with AUTOFILE only) -# ---------------------------------------------------------------------- -# License: GPL 3.0 -# Source: <https://git-repo.iml.unibe.ch/iml-open-source/bash-rest-api-client> -# Docs: <https://os-docs.iml.unibe.ch/bash-rest-api-client/> -# ---------------------------------------------------------------------- -# (1) source this script -# (2) enter "http.help" to get a list of available commands -# ---------------------------------------------------------------------- -# 2020-02-07 v0.2 axel.hahn@iml.unibe.ch BETABETA -# 2020-02-12 v0.4 axel.hahn@iml.unibe.ch Caching -# 2020-03-02 v0.5 axel.hahn@iml.unibe.ch a few more response check functions -# 2021-01-21 v0.6 axel.hahn@iml.unibe.ch add Content-type in request header -# 2022-01-11 v0.7 axel.hahn@iml.unibe.ch fixes using shellcheck -# 2024-10-09 v0.8 axel.hahn@unibe.ch add setAuthorization; customize accept header, add custom request headers -# 2024-10-10 v0.9 axel.hahn@unibe.ch update docs -# 2024-10-23 v0.10 axel.hahn@unibe.ch update help -# ====================================================================== - http_cfg__about="Bash REST API client v0.10" - typeset -i http_cfg__debug=0 - typeset -i http_cfg__cacheTtl=0 - http_cfg__cacheDir=/var/tmp/http-cache - http_cfg__UA="${http_cfg__about}" - http_cfg__prjurl="https://git-repo.iml.unibe.ch/iml-open-source/bash-rest-api-client" - http_cfg__docsurl="https://os-docs.iml.unibe.ch/bash-rest-api-client/" +>&2 echo "DEPRECATED: Source 'http.class.sh' instead of 'rest-api-client.sh'" -# --- curl meta infos to collect -# see variables in man curl --write-out param - curlMeta="\ - http_code \ - http_connect \ - local_ip \ - local_port \ - num_connects \ - num_redirects \ - redirect_url \ - remote_ip \ - remote_port \ - size_download \ - size_header \ - size_request \ - size_upload \ - speed_download \ - speed_upload \ - ssl_verify_result \ - time_appconnect \ - time_connect \ - time_namelookup \ - time_pretransfer \ - time_redirect \ - time_starttransfer \ - time_total \ - url_effective \ -" - - -# ---------------------------------------------------------------------- -# -# functions -# -# ---------------------------------------------------------------------- - - # ...................................................................... - # - # Write a debug message to STDERR - # Do no not change the prefix - is is read in inc_functions - # - # params strings output message - # - function http._wd(){ - if [ $http_cfg__debug -gt 0 ]; then - echo -e "\e[33m# RESTAPI::DEBUG $*\e[0m" >&2 - fi - } - - # ...................................................................... - # - # Write an error message to STDERR - # Do no not change the prefix - is is read in inc_functions - # - # params strings output message - # - function http._we(){ - echo -e "\e[31m# RESTAPI::ERROR $*\e[0m" >&2 - } - - function http(){ - cat <<EOH - -$http_cfg__about - -A REST API Client with curl - -Enter http.help to show all commands. - -EOH - # $0 is not the current file if we source a script - # grep "function http.[a-z]" $0 | sort - } - - # ...................................................................... - # - # Initialize the client - # - # Initialize the client for a new request. Call this before any other - # function. It will reset all variables. - # - function http.init(){ - http._wd "${FUNCNAME[0]}()" - which curl >/dev/null || http.quit - - # request vars - - http_req__auth= - http_req__authorization= - http_req__accept="application/json" - http_req__headers=() - - http_req__body= - http_req__method=GET - http_req__url= - http_req__fullurl= - http_req__docs= - - http_req__dataprefix="RESTAPICLIENTMETADATA_$(date +%s)_$$" - local writevar= - for myvar in $curlMeta - do - writevar="${writevar}|${myvar}:%{${myvar}}" - done - http_curl__writeout="\\n${http_req__dataprefix}${writevar}\\n" - - # cache - http_cfg__cacheTtl=0 - http_cfg__cacheFile= - - # response - http_resp__all= - http_resp__neutral= - mkdir ${http_cfg__cacheDir} 2>/dev/null - chmod 777 ${http_cfg__cacheDir} 2>/dev/null - } - - # ...................................................................... - # - # Execute the request - # - # param string optional: method; GET|POST|PUT|DELETE|... - # param string optional: full url - # param string optional: request body - # - # description: - # - # This function does the following: - # - # 1. Check if to use a cache - # 2. If not cached, make the request - # 3. If cached, use the cached result - # - function http.makeRequest(){ - http._wd "${FUNCNAME[0]}()" - - # --- handle optional prams - if [ $# -ne 0 ]; then - if echo "$1" | grep -q "^[A-Z]*$"; then - http.setMethod "$1" - shift 1 - fi - http.setUrl "$1" - http.setBody "$2" - fi - # test -z "$1" || http.setFullUrl "$1" - - # --- detect caching - local useCache=0 - local makeRequest=1 - if [ $http_cfg__cacheTtl -gt 0 ] && [ "${http_req__method}" = "GET" ]; then - useCache=1 - test -z "${http_cfg__cacheFile}" && http_cfg__cacheFile=$(http._genOutfilename "${http_cfg__cacheDir}/AUTOFILE") - if [ -f "${http_cfg__cacheFile}" ]; then - http.responseImport "${http_cfg__cacheFile}" - local iAge; typeset -i iAge - iAge=$(http.getRequestAge) - http._wd "INFO: Age of cache is $iAge sec - vs TTL $http_cfg__cacheTtl sec - file $http_cfg__cacheFile" - if [ $iAge -gt 0 ] && [ $iAge -lt $http_cfg__cacheTtl ]; then - http._wd "INFO: Using cache" - makeRequest=0 - else - http._wd "INFO: Cache file will be updated after making the request" - rm -f "${http_cfg__cacheFile}" 2>/dev/null - fi - fi - fi - - - # --- make the request - if [ $makeRequest -eq 1 ]; then - - # build curl parameters - local curl_params=( - -k - -s - -i "${http_req__fullurl}" - -X "${http_req__method}" - -w "${http_curl__writeout}" - -A "${http_cfg__UA}" - ) - test -n "$http_req__auth" && curl_params+=( -u "$http_req__auth" ) - test -n "$http_req__authorization" && curl_params+=( -H "Authorization: $http_req__authorization" ) - test -n "$http_req__accept" && curl_params+=( -H "Accept: $http_req__accept" ) - test -n "$http_req__body" && curl_params+=( -d "${http_req__body}" ) - curl_params+=( "${http_req__headers[@]}" ) - - http._wd "${FUNCNAME[0]}($1) ${http_req__method} ${http_req__fullurl}" - http_resp__all=$( curl "${curl_params[@]}") || http.quit - - http._wd "OK - Curl finished the http request ... processing data" - http_resp__neutral=$(http._fetchAllAndReformat) - if [ $useCache -eq 1 ]; then - http._wd "INFO: writing cache ..." - http.responseExport "${http_cfg__cacheFile}" - fi - fi - http._wd "Request function finished; Code $(http.getStatuscode)" - } - - # ...................................................................... - # - # Show error message with last return code and quit with this exitcode - # - # This function is used to quit the script with a meaningful error message - # and the exit code of the last command. - # - # The message is printed to STDERR and contains the return code. - # If a documentation URL is known, it is printed as a hint. - # - # The exit code is the one of the last command. - # To prevent the script from exiting when this function is called from a - # sourced file, the exit is commented out. - # - # no params - # - function http.quit(){ - rc=$? - http._wd "${FUNCNAME[0]}()" - echo >&2 - echo -e "\e[31m# ERROR: command FAILED with rc $rc. \e[0m" >&2 - if [ ! -z "${RestApiDocs}" ]; then - echo "HINT: see ${RestApiDocs}" >&2 - fi - # dont make exit in a sourced file - # exit $rc - } - - - # ...................................................................... - # - # Load a config file - # - # This function is marked as deprecated. - # It will be removed in the future. - # - # param string config file name - # - # Sourcing that file will set the following vars - # - RestApiUser - # - RestApiPassword - # - RestApiBaseUrl - # - RestApiDocs - # - # The function will then set the "internal" vars - # - http_req__auth - # - http_req__fullurl - # - http_req__docs - # - function http.loadcfg(){ - http._wd "${FUNCNAME[0]}($1) !!! DEPRECATED !!!" - # reset expected vars from config - RestApiUser= - RestApiPassword= - RestApiBaseUrl= - RestApiDocs= - - # source config file - . "${1}" || http.quit - - # set "internal" vars - if [ -z "$RestApiPassword" ]; then - http.setAuth "$RestApiUser:$RestApiPassword" - else - http.setAuth - fi - http.setBaseUrl "${RestApiBaseUrl}" - http.setDocs "${RestApiDocs}" - } - - # ====================================================================== - # GETTER - # ====================================================================== - - # ...................................................................... - # - # Get the response header or response body - # - # param string what to return; one of header|body - # - # Return the response header or the response body. The output is the same - # as that of http.getResponseHeader or http.getResponse. The difference is - # the implementation - # - function http._fetchResponseHeaderOrBody(){ - http._wd "${FUNCNAME[0]}($1)" - local isheader=true - - # keep leading spaces - IFS='' - - echo "${http_resp__all}" | grep -v "${http_req__dataprefix}" | while read -r line; do - if $isheader; then - if [[ $line = $'\r' ]]; then - isheader=false - else - test "$1" = "header" && echo $line - fi - else - # body="$body"$'\n'"$line" - test "$1" = "body" && echo $line - fi - done - } - - # ...................................................................... - # - # Get the response data - # - # Return the curl meta infos like http_code, http_connect, local_ip, ... - # as key/ value pairs. Each line is a single key value pair. - # - # The data is extracted from the response header. The format is: - # ${http_req__dataprefix}|key|value|key|value|... - # - function http._fetchResponseData(){ - http._wd "${FUNCNAME[0]}()" - echo "${http_resp__all}" | sed "s#${http_req__dataprefix}#\n${http_req__dataprefix}#" | grep "${http_req__dataprefix}" | tail -1 | cut -f 2- -d "|" | sed "s#|#\n#g" | grep -v "${http_req__dataprefix}" | while read -r line; do - echo "$line" - done - } - - # ...................................................................... - # - # Generate the dump with request and response - function http._fetchAllAndReformat(){ - http._wd "${FUNCNAME[0]}()" - IFS='' - line="#------------------------------------------------------------" - - echo "#_META_|about:$http_cfg__about" - echo "#_META_|host:$(hostname -f)" - echo $line - echo "#_REQUEST_|fullurl:$http_req__fullurl" - echo "#_REQUEST_|method:$http_req__method" - echo "#_REQUEST_|time:$(date)" - echo "#_REQUEST_|timestamp:$(date +%s)" - echo "#_REQUEST_|auth:$(echo $http_req__auth | sed 's#:.*#:xxxxxxxx#')" - echo "#_REQUEST_|body:$http_req__body" - echo "#_REQUEST_|baseurl:$http_req__baseurl" - echo "#_REQUEST_|url:$http_req__url" - echo "#_REQUEST_|docs:$http_req__docs" - echo $line - http._fetchResponseHeaderOrBody header | sed "s,^,#_HEADER_|,g" - echo $line - http._fetchResponseData | sed "s,^,#_DATA_|,g" - echo $line - http._fetchResponseHeaderOrBody body | sed "s,^,#_BODY_|,g" - echo $line END - } - - # ...................................................................... - # - # Get a section from dump data - # - # param string what to return; one of HEADER|DATA|BODY - # - # returns string the requested part of the response - # - function http._getFilteredResponse(){ - http._wd "${FUNCNAME[0]}($1)" - echo "${http_resp__neutral}" | grep "^#_${1}_|" | cut -f 2- -d "|" - } - - # ---------- PUBLIC REQUEST GETTER - - # ...................................................................... - # - # Get timestamp of the response as a Unix timestamp. - # - # no param - # - # returns string the timestamp of the response - # - function http.getRequestTs(){ - http._wd "${FUNCNAME[0]}()" - http._getFilteredResponse REQUEST | grep "^timestamp" | cut -f 2 -d ":" - } - - # ...................................................................... - # - # Get age of the response in sec. - # It is especially useful after responseImport - # - # no param - # - # returns integer age of the response in sec - # - function http.getRequestAge(){ - http._wd "${FUNCNAME[0]}()" - local iAge; typeset -i iAge - local iTs; typeset -i iTs - iTs=$( http.getRequestTs ) - iAge=$( date +%s )-${iTs} - echo "$iAge" - } - - # ---------- PUBLIC RESPONSE GETTER - - # ...................................................................... - # - # Get response body - # - # no param - # - # returns string the response body - # - function http.getResponse(){ - http._wd "${FUNCNAME[0]}()" - http._getFilteredResponse BODY - } - - # ...................................................................... - # - # Get curl data of this request with status, transferred bytes, speed, ... - # - # no param - # - # returns string the response data - # - function http.getResponseData(){ - http._wd "${FUNCNAME[0]}()" - http._getFilteredResponse DATA - } - - # ...................................................................... - # - # Get response header - # - # no param - # - # returns string the response header - # - function http.getResponseHeader(){ - http._wd "${FUNCNAME[0]}()" - http._getFilteredResponse HEADER - } - - # ...................................................................... - # - # Get raw response (not available after import) - # - # no params - # - # no param - # - function http.getResponseRaw(){ - http._wd "${FUNCNAME[0]}()" - echo "${http_resp__all}" - } - - # ...................................................................... - # - # Get Http status as string OK|Redirect|Error - # - # no params - # - function http.getStatus(){ - http._wd "${FUNCNAME[0]}($1)" - http.isOk >/dev/null && echo OK - http.isRedirect >/dev/null && echo Redirect - http.isError >/dev/null && echo Error - } - - # ...................................................................... - # - # Get Http status code of the request as 3 digit number - # - # no params - # - function http.getStatuscode(){ - http._wd "${FUNCNAME[0]}()" - http.getResponseData | grep "^http_code:" | cut -f 2 -d ":" - } - - # ...................................................................... - # - # Check: was response a 2xx status code? - # output is a statuscode if it matches ... or empty - # Additionally you can verify the return code - # - # $? -eq 0 means YES - # $? -ne 0 means NO - # - # no params - # - function http.isOk(){ - http._wd "${FUNCNAME[0]}()" - http.getStatuscode | grep '2[0-9][0-9]' - } - - # ...................................................................... - # - # Was the repsonse a redirect? - # - # no params - # - function http.isRedirect(){ - http._wd "${FUNCNAME[0]}()" - http.getStatuscode | grep '3[0-9][0-9]' - } - - # ...................................................................... - # - # Was the repsonse a client error (4xx or 5xx) - # - # no params - # - function http.isError(){ - http._wd "${FUNCNAME[0]}()" - http.getStatuscode | grep '[45][0-9][0-9]' - } - - # ...................................................................... - # - # Was the repsonse a client error (4xx) - # - # no params - # - function http.isClientError(){ - http._wd "${FUNCNAME[0]}()" - http.getStatuscode | grep '4[0-9][0-9]' - } - - # ...................................................................... - # - # Was the repsonse a client error (5xx) - # - # no params - # - function http.isServerError(){ - http._wd "${FUNCNAME[0]}()" - http.getStatuscode | grep '5[0-9][0-9]' - } - - # ...................................................................... - # - # Dump information about request and response - # - # no params - # - function http.dump(){ - http._wd "${FUNCNAME[0]}()" - http.responseExport - } - - # ====================================================================== - # Import/ Export - # ====================================================================== - - # ...................................................................... - # - # Helper to replace "AUTOFILE" with something uniq using full url - # - # param string import or export filename - # - function http._genOutfilename(){ - http._wd "${FUNCNAME[0]}($1)" - if echo "$1" | grep -q "AUTOFILE"; then - echo "$1" - else - local sum - sum=$(echo ${http_req__fullurl} | sha1sum ) - local autofile - autofile=$(echo "${sum}__${http_req__fullurl}" | sed "s#[^a-z0-9]#_#g") - echo "$1" | sed "s#AUTOFILE#${autofile}#" - fi - } - - # ...................................................................... - # - # Export response to a file - # - # param string optional: custom filename - # - function http.responseExport(){ - http._wd "${FUNCNAME[0]}($1)" - if [ -z "$1" ]; then - echo "${http_resp__neutral}" - else - local outfile - outfile=$(http._genOutfilename "$1") - http._wd "${FUNCNAME[0]}($1) writing to outfile $outfile" - echo "${http_resp__neutral}" >"$outfile" - fi - } - - # ...................................................................... - # - # Import a former response from a file - # - # param string filename with stored response - # - function http.responseImport(){ - http._wd "${FUNCNAME[0]}($1)" - local infile - infile=$(http._genOutfilename "$1") - if [ -r "${infile}" ]; then - if grep -q "^#_META_|about:$http_cfg__about" "${infile}"; then - http_resp__neutral=$(cat "${infile}") - else - echo "ERROR: Ooops [${infile}] does not seem to be an export dump." - http.quit - fi - else - echo "ERROR: Ooops the file [${infile}] is not readable." - http.quit - fi - } - - # ...................................................................... - # - # Delete an exported file; this is especially useful if you use - # AUTOFILE functionality - # - # param string filename with stored response - # - function http.responseDelete(){ - http._wd "${FUNCNAME[0]}($1)" - local infile - infile=$(http._genOutfilename "$1") - if [ -r "${infile}" ]; then - if grep -q "^#_META_|about:$http_cfg__about" "${infile}"; then - if rm -f "${infile}"; then - http._wd "OK, ${infile} was deleted." - else - http._wd "ERROR: unable to delete existing ${infile}. Check permissions." - fi - else - http._wd "SKIP: ${infile} is not an export file." - fi - else - http._wd "SKIP: ${infile} is not readable." - fi - } - - # ====================================================================== - # SETTER - # ====================================================================== - - # ...................................................................... - # - # Add a line to the request header - # - # param string line to add, eg "Connection: keep-alive" - # - function http.addHeader(){ - http._wd "${FUNCNAME[0]}($1)" - http_req__headers+=( -H "$1") - } - - # ...................................................................... - # - # set Accept request header and override default - # - # param string accept header value, eg text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 - # - function http.setAccept(){ - http._wd "${FUNCNAME[0]}($1)" - if [ -z "$1" ]; then - http_req__accept= - else - http_req__accept="$1" - fi - } - - # ...................................................................... - # - # Set basic authentication - # Without given parameter, authentication is removed - # - # param string optional: USER:PASSWORD - # - function http.setAuth(){ - http._wd "${FUNCNAME[0]}($1)" - if [ -z "$1" ]; then - http_req__auth= - else - http_req__auth="$1" - fi - } - - # ...................................................................... - # - # Set authentication via Athorization header - # Without given parameter, authorization is removed - # - # param string optional: type, eg. Basic|Bearer|Negotiate - # param string optional: token or encoded user + password - # - function http.setAuthorization(){ - http._wd "${FUNCNAME[0]}($1 $2)" - if [ -z "$1" ]; then - http_req__authorization= - else - http_req__authorization="${1} ${2}" - fi - } - - # ...................................................................... - # - # Set body to send for PUTs and POSTs - # - # param string body - # - function http.setBody(){ - http._wd "${FUNCNAME[0]}($1)" - http_req__body=$1 - } - - # ...................................................................... - # - # Set a base url of an API - # Remark: Then use http.setUrl to complet the url to request - # - # param string url - # - function http.setBaseUrl(){ - http._wd "${FUNCNAME[0]}($1)" - http_req__baseurl=$1 - http.setFullUrl "" - } - - # ...................................................................... - # - # Enable or disable debug mode - # - # param integer 0|1 - # - function http.setDebug(){ - http._wd "${FUNCNAME[0]}($1)" - http_cfg__debug=$1 - } - function http.setDocs(){ - http._wd "${FUNCNAME[0]}($1)" - http_req__docs=$1 - } - - # ...................................................................... - # - # Set the method to use; GET|POST|PUT|DELETE|... - # - # param string name of method - # - function http.setMethod(){ - http._wd "${FUNCNAME[0]}($1)" - http_req__method=$1 - } - - # ...................................................................... - # - # Set a full url to request - # - # param string optional: url - # - function http.setFullUrl(){ - http._wd "${FUNCNAME[0]}($1)" - if [ -z "$1" ]; then - http_req__fullurl=${http_req__baseurl}${http_req__url} - else - http_req__fullurl=$1 - fi - } - - # ...................................................................... - # - # Complete the base url - # - # param string url part behind base url - # - function http.setUrl(){ - http._wd "${FUNCNAME[0]}($1)" - http_req__url=$1 - http.setFullUrl - } - - # ----- caching - - # ...................................................................... - # - # Set cache ttl in seconds - # - # param integer ttl in seconds - # - function http.setCacheTtl(){ - http._wd "${FUNCNAME[0]}($1)" - http_cfg__cacheTtl=$1 - } - - # ...................................................................... - # - # Set cache file - # - # param string filename - # - function http.setCacheFile(){ - http._wd "${FUNCNAME[0]}($1)" - http_cfg__cacheFile="$1" - } - - # ...................................................................... - # - # Flush the cache - # - # no params - # - function http.flushCache(){ - http._wd "${FUNCNAME[0]}($1)" - rm -f ${http_cfg__cacheDir}/* - } - - # ...................................................................... - # - # show a help text - # - # no params - # - function http.help(){ - cat <<EOH - -$http_cfg__about - -This is a bash solution to script REST API calls. - -Source: <$http_cfg__prjurl> -Docs: <$http_cfg__docsurl> -License: GNU GPL 3 - - -INSTRUCTION: - -- Source the file once -- Then you can run functions starting with "http." - - http.init - Start a new request. It resets internal vars of the last request - (if there was one). - - http.setDebug 0|1 - Enable or disable debugging infos during processing. It is written - to STDERR. - -- initialize a request - - setAccept "<ACCEPTHEADER>" - Set authentication with user and password for basic auth - Default: $http_req__accept - - setAuth "<USER>:<PASSWORD>" - Set authentication with user and password for basic auth - Without given parameter, authentication is removed - - setAuthorization "<TYPE>" "<TOKEN|HASH>" - Set authentication with Authorization header. - As TYPE you can use Basic|Bearer|Negotiate|... - 2nd param is the token or hashed user+password - Without given parameter, authorization is removed - - http.setBody "<DATA>" - set a body for POST/ PUT requests. - - http.setBaseUrl "<URL>" - Set a base url to an api. - renmark: - Use http.setUrl to built a complete url. - - http.setDocs "<URL>" - Set a docs url. If set it will be shown as additional hint when a - request fails. - - http.setMethod "<METHOD>" - Set a http method. Use an uppercase string for GET|POST|PUT|DELETE|... - - http.setFullUrl "<URL>" - Set a complete url for a request. - - http.setUrl "<REQUEST?QUERY>" - Set a relative url for a request. - This requires to use http.setBaseUrl before. - - http.addHeader "<HEADER_LINE>" - Add a header line to the request. - This command can be repeated multiple times. - -- caching functions - - http.setCacheTtl <SECONDS> - Enable caching with values > 0 - Remark: only GET requests will be cached. - Default: 0 (no caching) - - http.setCacheFile "<FILENAME>" - Set a file where to read/ store a request - Default: empty; autogenerated file below $http_cfg__cacheDir - - http.flushCache - Delete all files in $http_cfg__cacheDir - -- make the request - - http.makeRequest [[<METHOD>] ["<URL>"] ["<BODY>"]] - The parameters are optional. Without parameter the request will be - started with given data in http.set* functions described above. - If minimum one param is given then they are handled: - METHOD optional: set a method (must be uppercase) - see http.setMethod - URL set a relative url - see http.setUrl - BODY optional: set a body - see http.setBody - - The request will be skipped and uses a cached content if ... - - METHOD is GET - - http.setCacheTtl set a value > 0 - - the cache file exists and is younger than the given TTL - -- handle response - - http.getResponse - Get the Response Body - - http.getResponseData - Get Meta infos from curl - - http.getResponseHeader - Get The http reponse header - -- check http status code - - http.getStatus - Get the http status as string Ok|Redirect|Error - - http.getStatuscode - Get the http status code of a request as 3 digit integer - - http.isOk - Check if the http response code is a 2xx - - http.isRedirect - Check if the http response code is a 3xx - - http.isError - Check if the http response code is a 4xx or 5xx - - http.isClientError - Check if the http response code is a 4xx - - http.isServerError - Check if the http response code is a 5xx - - http.getRequestAge - Get the age of the request in seconds. - Remark: This function is useful after an import - see http.responseImport. - - http.getRequestTs - Get the Unix timestamp of the request - -- import/ export - - http.responseExport ["<FILE>"] - dump the response data - Without parameter it is written on STDOUT. - You can set a filename to write it to a file. - The filename can contain "AUTOFILE" this string - will be replaced with a uniq string. - (requires sha1sum and a set url) - Example: - http.makeRequest "https://example.com/api/" - http.responseExport /tmp/something_AUTOFILE_.txt - - http.responseImport "<FILE>" - Import an export file. - To use the AUTOFILE mechanism from export set - the url first. - Example: - http.setFullUrl "https://example.com/api/" - http.responseImport /tmp/something_AUTOFILE_.txt - - http.responseDelete "<FILE>" - Delete a file after http.responseExport. - It is useful if you use the AUTOFILE mechanism. - -EOH - } - -# ---------------------------------------------------------------------- -# -# main -# -# ---------------------------------------------------------------------- - - http.init - -# ---------------------------------------------------------------------- +script_dir=$(dirname "$BASH_SOURCE[0]") +. "$script_dir/http.class.sh" $*