From 5976177e6449bad41077a9028c5bd58f1bc347d0 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Tue, 9 Mar 2021 13:37:49 +0200 Subject: Add a testsuite. Minor fixes. * bulkredirect.lua (bulkredirect.request): Ensure leading / on a redirection target, unless it starts with schema. (load_redirect_file): Override redirection options that differ from the global ones. Don't modify the table while iterating over it. * .gitignore: New file. * t/testsuite: New file. * t/haproxy.cfg.in: New file. * t/a.tab: New file. * t/b.tab: New file. * t/c.tab: New file. * t/d.tab: New file. --- .gitignore | 4 + bulkredirect.lua | 21 ++- t/a.tab | 6 + t/b.tab | 6 + t/c.tab | 9 ++ t/d.tab | 11 ++ t/haproxy.cfg.in | 13 ++ t/testsuite | 389 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 453 insertions(+), 6 deletions(-) create mode 100644 .gitignore create mode 100644 t/a.tab create mode 100644 t/b.tab create mode 100644 t/c.tab create mode 100644 t/d.tab create mode 100644 t/haproxy.cfg.in create mode 100755 t/testsuite diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..65dc152 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*~ +\#* +.emacs* +tmp/ diff --git a/bulkredirect.lua b/bulkredirect.lua index 20666ca..522d570 100644 --- a/bulkredirect.lua +++ b/bulkredirect.lua @@ -171,6 +171,11 @@ function bulkredirect.request (txn) end if location then + if not (location:match('^http://') or location:match('^https://') + or location:match('^/')) then + location = '/'..location + end + if not exact or i == path or i..'/' == path then if not strippath then location = location .. path:sub(i:len() + 1) @@ -338,19 +343,19 @@ local function load_redirect_file (f, filename) parseopt(optlist, optab, filename .. ':' .. ln) end - if optab['exact'] then + if optab['exact'] ~= exact then if type(dst) == 'string' then dst = { dst } end dst[2] = optab['exact'] end - if optab['strippath'] then + if optab['strippath'] ~= strippath then if type(dst) == 'string' then dst = { dst } end dst[3] = optab['strippath'] end - if optab['stripquery'] then + if optab['stripquery'] ~= stripquery then if type(dst) == 'string' then dst = { dst } end dst[4] = optab['stripquery'] end - if optab['temporary'] then + if optab['temporary'] ~= temporary then if type(dst) == 'string' then dst = { dst } end dst[5] = optab['temporary'] end @@ -399,17 +404,21 @@ local function load_redirect_file (f, filename) end if www then + crt = {} for d,t in pairs(rt) do local compl = www_complement(d) if rt[compl] then for k,v in pairs(t) do -- FIXME: Error message if rt[compl][k] exists - rt[compl][k] = clone(v) + crt[compl][k] = clone(v) end else - rt[compl] = d + crt[compl] = d end end + for d,t in pairs(crt) do + rt[d] = t + end www = nil end diff --git a/t/a.tab b/t/a.tab new file mode 100644 index 0000000..09aa015 --- /dev/null +++ b/t/a.tab @@ -0,0 +1,6 @@ +[example.com] +/foo /alpha +/foo/bar /beta +/foo/bar/baz /gamma + + diff --git a/t/b.tab b/t/b.tab new file mode 100644 index 0000000..3e2e928 --- /dev/null +++ b/t/b.tab @@ -0,0 +1,6 @@ +option www +[example.com] +/foo /alpha +/foo/bar /beta +/foo/bar/baz /gamma nowww + diff --git a/t/c.tab b/t/c.tab new file mode 100644 index 0000000..1a822af --- /dev/null +++ b/t/c.tab @@ -0,0 +1,9 @@ +[*] +/foo /alpha +/foo/bar /beta +[example.org] +/foo/bar/baz /gamma +/foo /delta exact +/bar /epsilon strippath +/baz /zeta stripquery +/dux /eta temporary diff --git a/t/d.tab b/t/d.tab new file mode 100644 index 0000000..c097565 --- /dev/null +++ b/t/d.tab @@ -0,0 +1,11 @@ +option www,strippath,stripquery +[example.org] +/a /A +/b /B nostrippath +option nostrippath +/c /C +/d /D nowww +option nostripquery +/e /E + + diff --git a/t/haproxy.cfg.in b/t/haproxy.cfg.in new file mode 100644 index 0000000..bcc5884 --- /dev/null +++ b/t/haproxy.cfg.in @@ -0,0 +1,13 @@ +global + lua-load %TOPDIR%/bulkredirect.lua + daemon + +defaults + timeout connect 5 + timeout client 5 + timeout server 5 + +frontend http-in + mode http + bind :::%PORT% v4v6 + http-request lua.bulkredirect diff --git a/t/testsuite b/t/testsuite new file mode 100755 index 0000000..69b4325 --- /dev/null +++ b/t/testsuite @@ -0,0 +1,389 @@ +#!/bin/sh + +sourcename=bulkredirect.lua + +while getopts "b:C:p:" OPTION +do + case $OPTION in + C) cd $OPTARG || exit 2;; + p) haproxy_port=$OPTARG;; + b) haproxy_bin=$OPTARG;; + *) echo >&2 "$0: usage error" + exit 2 + esac +done + +shift $(( $OPTIND - 1 )) + +if [ ! -f haproxy.cfg.in ]; then + echo >&2 "$0: must be run from the t/ subdirectory" + exit 2 +fi +if [ ! -f ../$sourcename ]; then + echo >&2 "$0: must be run from the t/ subdirectory" + exit 2 +fi + +top_srcdir=$(cd ..; pwd) +testsource_dir=$(pwd) +PATH=/usr/sbin:$PATH +testsuite_dir=$(pwd)/testsuite.dir +numfile=$testsuite_dir/numbers +: ${haproxy_bin:=haproxy} +: ${haproxy_port:=8080} + +# Check if haproxy is installed +if ! $haproxy_bin -v >/dev/null 2>&1; then + echo >&2 "$0: haproxy not installed or unusable" + exit 2 +fi + +# Test if egrep is installed +if ! egrep --help >/dev/null 2>&1; then + echo >&2 "$0: egrep not installed or unusable" + exit 2 +fi + +# Test if curl is installed +if ! curl --help >/dev/null 2>&1; then + echo >&2 "$0: curl not installed or unusable" + exit 2 +fi + +# Test if nc is available and supports the -z option +if ! nc -h 2>&1 | egrep -q '^[[:space:]]+-z'; then + echo >&2 "$0: nc is not available or does not support -z option" + exit 2 +fi + +# Test if sleep supports sub-second resolution +if sleep 0.1 >/dev/null 2>&1; then + haproxy_sleep_time=0.1 + haproxy_start_init=20 +else + haproxy_sleep_time=1 + haproxy_start_init=2 +fi + +# Test if the requested port is not in use +if nc -z -w5 127.0.0.1 $haproxy_port 2>/dev/null; then + echo >&2 "$0: requested port $haproxy_port is in use; use -p option to select another port" + exit 2 +fi + +haproxy_create_config() { + sed -e "s^%PORT%^$haproxy_port^" -e "s^%TOPDIR%^$top_srcdir^" \ + $testsource_dir/haproxy.cfg.in > $testsuite_dir/haproxy.cfg +} + +haproxy_start() { + ( export HAPROXY_BULKREDIRECT=$testsource_dir/$1 + exec > $testsuite_dir/$test_num/haproxy.err + exec 2>&1 + exec $haproxy_bin -f $testsuite_dir/haproxy.cfg -db ) & + echo $! > $testsuite_dir/$test_num/haproxy.pid + haproxy_start_result=$haproxy_start_init + while ! nc -z -w5 127.0.0.1 $haproxy_port 2>/dev/null + do + if [ $haproxy_start_result -eq 0 ]; then + break + fi + haproxy_start_result=$(($haproxy_start_result - 1)) + sleep $haproxy_sleep_time + done + if [ $haproxy_start_result -gt 0 ]; then + trap 'haproxy_stop' HUP INT QUIT TERM + fi +} + +haproxy_stop() { + if [ -f $testsuite_dir/$test_num/haproxy.pid ]; then + kill $(cat $testsuite_dir/$test_num/haproxy.pid) >/dev/null 2>&1 + fi +} + +test_num=1 + +# runtest +runtest() { + local RTFILE + local HTTP_CODE=301 + local URL + local HTTP_LOCATION + local DESCR= + + while [ $# -gt 0 ] + do + case $1 in + DESCR=*) + DESCR=${1##DESCR=};; + RTFILE=*) + RTFILE=${1##RTFILE=};; + HTTP_CODE=*) + HTTP_CODE=${1##HTTP_CODE=};; + HTTP_LOCATION=*) + HTTP_LOCATION=${1##HTTP_LOCATION=};; + URL=*) + URL=${1##URL=};; + *) + echo >&2 "$0: INTERNAL ERROR: bad argument $1" + exit 3 + esac + shift + done + + if [ -z "$RTFILE" ]; then + echo >&2 "$0: INTERNAL ERROR: RTFILE not defined" + exit 3 + fi + + if [ -z "$URL" ]; then + echo >&2 "$0: INTERNAL ERROR: URL not defined" + exit 3 + fi + + rm -rf $testsuite_dir/$test_num + if [ -f $numfile ] && ! grep -q "^$test_num\$" $numfile; then + : + else + mkdir $testsuite_dir/$test_num + cd $testsuite_dir/$test_num + printf "%4d: %-60s " $test_num "${DESCR:-$URL}" + haproxy_start $RTFILE + if [ $haproxy_start_result -eq 0 ]; then + runtest_result=ERR + else + HOST=${URL%%/*} + URI=/${URL#*/} + curl -q -sS --stderr stderr -o stdout -D headers \ + --connect-timeout 2 \ + -H "Host: $HOST" "http://localhost:$haproxy_port$URI" + if [ $? -ne 0 ]; then + runtest_result=FAIL + else + cat headers | tr -d '\r' > headers.txt + http_code=$(sed -n -e '1s^HTTP/1\.1 \([0-9][0-9][0-9]\).*^\1^p' headers.txt) + http_location=$(sed -n -e 's/^location: //ip' headers.txt) + if [ -n "$HTTP_CODE" ] && [ "$http_code" != "$HTTP_CODE" ]; then + runtest_result=FAIL + elif [ -n "$HTTP_LOCATION" ] && [ "$http_location" != "$HTTP_LOCATION" ]; then + runtest_result=FAIL + else + runtest_result=OK + fi + fi + fi + printf "$runtest_result\n" + haproxy_stop + cd $testsuite_dir + case $runtest_result in + OK|XFAIL) + runtest_success_count=$(($runtest_success_count + 1)) + rm -rf $test_num;; + *) + runtest_failure_count=$(($runtest_failure_count + 1)) + esac + fi + test_num=$(($test_num + 1)) +} + +# ############## +set -e +rm -rf testsuite.dir +mkdir testsuite.dir + +if [ $# -gt 0 ]; then + while [ $# -gt 0 ] + do + echo "$1" + shift + done > $numfile +fi +set +e + +group_header() { + echo + echo "# $* #" | sed -e 's/./#/g' + echo "# $* #" + echo "# $* #" | sed -e 's/./#/g' + echo +} + +# #################### +# Start tests +haproxy_create_config + +runtest_success_count=0 +runtest_failure_count=0 + +# #################### +group_header Basic functionality + +runtest \ + RTFILE=a.tab \ + URL=example.com/foo \ + HTTP_CODE=301 \ + HTTP_LOCATION=/alpha + +runtest \ + RTFILE=a.tab \ + URL=example.com/foo/bar \ + HTTP_LOCATION=/beta + +runtest \ + RTFILE=a.tab \ + URL=example.com/foo/bar/baz \ + HTTP_LOCATION=/gamma + +runtest \ + RTFILE=a.tab \ + URL=example.com/foo/bar/mugu?q=1 \ + HTTP_LOCATION=/beta/mugu?q=1 + +runtest \ + RTFILE=a.tab \ + URL=example.org/foo \ + HTTP_CODE=503 + +# #################### +group_header www option + +runtest \ + RTFILE=b.tab \ + URL=example.com/foo \ + HTTP_LOCATION=/alpha + +runtest \ + RTFILE=b.tab \ + URL=www.example.com/foo \ + HTTP_LOCATION=/alpha + +runtest \ + RTFILE=b.tab \ + URL=example.com/foo/bar \ + HTTP_LOCATION=/beta + +runtest \ + RTFILE=b.tab \ + URL=example.com/foo/bar/baz \ + HTTP_LOCATION=/gamma + +runtest \ + RTFILE=b.tab \ + URL=www.example.com/foo/bar/baz \ + HTTP_LOCATION=/beta/baz + +runtest \ + RTFILE=b.tab \ + URL=example.com/foo/bar/mugu?q=1 + HTTP_LOCATION=/beta/mugu?q=1 + +# #################### +group_header Default domain + +runtest \ + RTFILE=c.tab \ + URL=example.com/foo \ + HTTP_LOCATION=/alpha + +runtest \ + RTFILE=c.tab \ + URL=www.example.com/foo \ + HTTP_LOCATION=/alpha + +runtest \ + RTFILE=c.tab \ + URL=example.com/foo/bar/baz \ + HTTP_LOCATION=/beta/baz + +runtest \ + RTFILE=c.tab \ + DESCR="Explicit domain" \ + URL=example.org/foo/bar/baz \ + HTTP_LOCATION=/gamma + +runtest \ + RTFILE=c.tab \ + DESCR="'exact' modifier: match" \ + URL=example.org/foo \ + HTTP_LOCATION=/delta \ + +runtest \ + RTFILE=c.tab \ + DESCR="'exact' modifier: prefix match" \ + URL=example.org/foo/bar \ + HTTP_CODE=503 + +runtest \ + RTFILE=c.tab \ + DESCR="strippath" \ + URL=example.org/bar/baz/qux \ + HTTP_LOCATION=/epsilon + +runtest \ + RTFILE=c.tab \ + DESCR="stripquery" \ + URL=example.org/baz?q=10 \ + HTTP_LOCATION=/zeta + +runtest \ + RTFILE=c.tab \ + DESCR="temporary" \ + URL=example.org/dux \ + HTTP_LOCATION=/eta \ + HTTP_CODE=302 + +# #################### +group_header Option precedence + +runtest \ + RTFILE=d.tab \ + DESCR="strippath" \ + URL=example.org/a \ + HTTP_LOCATION=/A + +runtest \ + RTFILE=d.tab \ + DESCR="strippath: prefix" \ + URL=example.org/a/b \ + HTTP_LOCATION=/A + +runtest \ + RTFILE=d.tab \ + DESCR="strippath: local override" \ + URL=example.org/b/suffix \ + HTTP_LOCATION=/B/suffix + +runtest \ + RTFILE=d.tab \ + DESCR="strippath: option override" \ + URL=example.org/c/suffix \ + HTTP_LOCATION=/C/suffix + +runtest \ + RTFILE=d.tab \ + DESCR="strippath: option override + local override (1)" \ + URL=example.org/d/suffix \ + HTTP_LOCATION=/D/suffix + +runtest \ + RTFILE=d.tab \ + DESCR="strippath: option override + local override (2)" \ + URL=www.example.org/d/suffix \ + HTTP_CODE=503 + +runtest \ + RTFILE=d.tab \ + DESCR="override additivity" \ + URL=example.org/e/suffix?q=10 \ + HTTP_LOCATION=/E/suffix?q=10 + +echo +echo "Run $(($runtest_success_count + $runtest_failure_count)) tests." +if [ $runtest_failure_count -eq 0 ]; then + echo "All tests passed" + rm -rf $testsuite_dir +else + echo "$runtest_failure_count failed unexpectedly." + echo "Examine the testsuite.dir directory." +fi -- cgit v1.2.1