#!/bin/bash

DIR="."
source "$DIR/variables"

WRK_MASK=0

error () {
	echo "ERROR: $1"
	exit 1
}

dev () {
	read -p "Test stopped, press 'Enter' to continue"
}

# if [[ "$NUM" == "" ]]; then error_param "backend_number"; fi
error_param () {
	error "the function '${FUNCNAME[1]}' expects the parameter '$1'"
}

msg () {
	echo ""
	echo "# $1"
}

start_debug () {
	rm -f $DEBUG_OUT
	tail -F $DEBUG_OUT 2>/dev/null &
}

stop_debug () {
	kill `ps aux |grep "tail -F $DEBUG_OUT" | grep -v grep | awk {'print$2'}` 2>/dev/null
}

debug () {
	if [[ $DEBUG -gt 0 ]]; then
		echo "debug> $1" >> $DEBUG_OUT
	fi
}

wait () {
	if [[ $DEBUG -ge 1 ]]; then
		echo "Waiting for '$1' seconds..."
	fi
	sleep $1
}

cmd () {
	local CMD="$1"
	local OUT=$($CMD 2>&1)

	if [[ $? -ne 0 ]]; then
		echo "FAILED> CMD: $CMD"
		echo "FAILED> OUT: $OUT"
		echo ""
		return 1
	elif [[ $DEBUG -ge 2 ]]; then
		echo "debug> CMD: $CMD"
		if [[ $DEBUG -ge 3 ]]; then
			echo "OUT: $OUT"
		fi
		echo ""
	fi
	return 0
}

catch_cmd () {
	local CMD="$1"
	echo "cmd: $CMD"
	$CMD 2>&1
}

## proxy actions
lookfor_segfault () {
	# May 14 08:30:58 lb zproxy: /home/cano/git/zproxy/build/bin/zproxy(+0x80635) [0x5618196cb635]
	if [[ "$SYSLOG_ZPROXY_START" == "" ]]; then
		SYSLOG_ZPROXY_START=`tac $SYSLOG | grep -m1 "zproxy start..."`
	fi

	LOGS_SEGFAULT=`grep -A10000 --text -F "$SYSLOG_ZPROXY_START" $SYSLOG | grep -E "$PROXY_BIN\(\+0x[0-9a-f]+\) \[0x[0-9a-f]+\]"`
	if [[ "$LOGS_SEGFAULT" != "" ]]; then
		echo "$LOGS_SEGFAULT"
		error "There was an zproxy SEGFAULT"
	fi
}

# If it receives a configuration file, it will be loaded
start_proxy () {
	if [ "$1" != "" ]; then
		deploy_tpl "$1" $PROXY_CFG_F
	fi

	local NO_DAEMON=""
	if [[ $VALGRIND -ne 0 ]]; then
		NO_DAEMON="-D"
	else
		VALGRIND_CMD=""
	fi

	if [[ $DEBUG_FLAG -ne 0 ]]; then
		echo "ip netns exec $PROXY_NS $VALGRIND_CMD $PROXY_BIN $NO_DAEMON -p $PROXY_PID_F -C $PROXY_CTL_SOCKET -f $PROXY_CFG_F &"
	fi

	ip netns exec $PROXY_NS $VALGRIND_CMD $PROXY_BIN $NO_DAEMON -p $PROXY_PID_F -C $PROXY_CTL_SOCKET -f $PROXY_CFG_F &
	wait $PROXY_GRACETIME

	SYSLOG_ZPROXY_START=`tail -1 $SYSLOG`
	lookfor_segfault
}

stop_proxy () {
	if [[ $VALGRIND -eq 0 ]]; then
		if [[ -f $PROXY_PID_F ]]; then
			cmd "ip netns exec $PROXY_NS kill -15 `cat $PROXY_PID_F`"
		fi
	else
		local OUT=`ps aux | grep valgrind | grep -Ev grep | awk '{print $2}'`

		if [[ "$OUT" != "" ]]; then
			kill -15 $OUT
			wait $PROXY_GRACETIME
		fi
	fi
}

stop_proxy_all () {
	cmd "pkill -9 zproxy"
}

restart_proxy () {
	stop_proxy

	ps aux | grep "$PROXY_BIN" | grep -v grep >/dev/null 2>&1
	if [[ $? -eq 0 ]]; then error "The zproxy process does not finish properly"; fi

	start_proxy $1
}

apply_proxy_api () {
	if [ "$1" == "" ]; then error_param "directory"; fi

	SOCKET_UNIX=1

	exec_curl $1

	if [[ $METHOD != "GET" ]]; then
		DUMP_DIR="${1}_dump"
		mkdir -p $DUMP_DIR

		# Dump the CTL configuration and compare the saved one
		clean_test
		METHOD="$PROXY_CTL_DUMP_METHOD"
		URL="$PROXY_CTL_DUMP_URL"
		exec_curl "$DUMP_DIR"
	fi

	SOCKET_UNIX=0
}

# If it receives a configuration file, it will be loaded
reload_proxy () {
	if [ "$1" == "" ]; then error_param "directory"; fi
	if [ "$2" == "" ]; then error_param "reload_file"; fi

	deploy_tpl "$2" $PROXY_CFG_F

	clean_test
	URL="$PROXY_CTL_RELOAD_URL"
	METHOD="$PROXY_CTL_RELOAD_METHOD"
	apply_proxy_api $1
	ERR=$?

	local OUT=`cat $1/body.out.tmp | grep '"result"' | grep '"error"' 2>/dev/null`
	if [[ $OUT != "" ]]; then
		ERR=1
	fi

	wait $PROXY_GRACETIME
	return $ERR
}

## functions to manage the httpd backend server
start_httpd () {
	local ID="$1"
	local HTTP_CFG_F=$(get_bck_cfg_f $ID)
	local HTTP_PID=$(get_bck_pid_f $ID)
	local BCK_ID="$ID"

	HTTP_SITES_TPL="tpl/nginx-sites.conf"
	HTTP_SITE="$TMP_DIR/nginx-sites-$ID.conf"
	HTTP_WEBROOT="$TMP_DIR/http-app-$ID"
	local WS_SRV_SCRIPT="tpl/ws-sv-http.py"
	local WS_PID_F="$TMP_DIR/ws-sv-$ID.pid"
	local WS_LOG_F="$TMP_DIR/ws-$ID.log"
	local WSS_PID_F="$TMP_DIR/wss-sv-$ID.pid"
	local WSS_LOG_F="$TMP_DIR/wss-$ID.log"

	deploy_tpl $HTTP_TPL $HTTP_CFG_F
	deploy_tpl $HTTP_SITES_TPL $HTTP_SITE
	mkdir $HTTP_WEBROOT
	cp -r "tpl/download" $HTTP_WEBROOT

	cmd "ip netns exec $NS $HTTPD_BIN -c $HTTP_CFG_F"
	# start ws server
	eval "ip netns exec $NS $WS_SRV_SCRIPT 0 >> $WS_LOG_F 2>&1 &"
	echo $! > $WS_PID_F
	# start wss server
	eval "ip netns exec $NS $WS_SRV_SCRIPT 1 >> $WSS_LOG_F 2>&1 &"
	echo $! > $WSS_PID_F

	if [[ $? -ne 0 ]]; then error "Error running the backend $ID"; fi

}

stop_httpd () {
	local ID=$1
	if [[ "$ID" == "" ]]; then ID=1; fi

	local HTTP_CFG_F=$(get_bck_cfg_f $ID)
	local HTTP_PID_F=$(get_bck_pid_f $ID)
	local NS=$(get_bck_ns $ID)
	local WS_PID_F="$TMP_DIR/ws-sv-$ID.pid"
	local WSS_PID_F="$TMP_DIR/wss-sv-$ID.pid"

	HTTP_SITE_F="$TMP_DIR/nginx-sites-$ID.conf"
	HTTP_WEBROOT="$TMP_DIR/http-app-$ID"

	cmd "ip netns exec $NS kill -15 `cat $HTTP_PID_F`"
	rm -rf $HTTP_CFG_F $HTTP_WEBROOT $HTTP_SITE_F

	ip netns exec $NS kill -15 `cat $WS_PID_F`
	ip netns exec $NS kill -15 `cat $WSS_PID_F`
}

create_proxy () {

	cmd "ip netns add $PROXY_NS"

	cmd "ip link add $PROXY_IF netns $PROXY_NS type dummy"
	cmd "ip -net $PROXY_NS addr add $PROXY_VIP/16 dev $PROXY_IF"
	cmd "ip -net $PROXY_NS link set up dev $PROXY_IF"
}

delete_proxy () {
	cmd "ip netns del $PROXY_NS"
}

create_client () {

	local ID="$1"
	if [[ "$ID" == "" ]]; then ID=1; fi

	local NS=$(get_cl_ns $ID)
	local IF=$(get_cl_if $ID)
	local IP=$(get_cl_ip $ID)
	local GW=$(get_cl_gw $ID)

	cmd "ip netns add $NS"
	cmd "ip link add $IF netns $PROXY_NS type veth peer name $IF netns $NS"
	cmd "ip -net $NS addr add $IP/16 dev $IF"
	cmd "ip -net $NS link set up dev $IF"

	# add GW info
	cmd "ip -net $PROXY_NS addr add $GW/16 dev $IF"
	cmd "ip -net $PROXY_NS link set up dev $IF"
	cmd "ip -net $PROXY_NS route add to $IP dev $IF"
	cmd "ip -net $NS route add default via $GW"
}

add_clients () {
	local NUM="$1"
	if [[ "$NUM" == "" ]]; then error_param "client_number"; fi

	for ID in $(seq 1 $NUM); do
		create_client $ID
	done
}

delete_client () {
	local ID=$1
	if [[ "$ID" == "" ]]; then ID=1; fi
	local NS=$(get_cl_ns $ID)
	cmd "ip netns del $NS"
}

delete_clients () {
	local NUM="$1"
	if [[ "$NUM" == "" ]]; then error_param "client_number"; fi
	for ID in $(seq 1 $NUM); do
		delete_client $ID
	done
}

get_bck_pid_f () {
	local HTTP_PID_F="$TMP_DIR/http_bck$1.pid"
	echo "$HTTP_PID_F"
}

get_bck_cfg_f () {
	local HTTP_PID_F="$TMP_DIR/http_bck$1.conf"
	echo "$HTTP_PID_F"
}

# deploy_tpl $TPL_PATH $DST_PATH
deploy_tpl () {
	# expand the template variables with env ones
	TMP_ENV="$TMP_DIR/env.vars"

	TPL=$1
	DST=$2
	if [[ "$TPL" == "" ]]; then error_param "template_path"; fi
	if [[ "$DST" == "" ]]; then error_param "destination_path"; fi

	set >$TMP_ENV
	cp $TPL $DST

	for VAR in `sed 's,#{,\n#{,g' $TPL | grep -E '#{[a-zA-Z0-9_-]+}' | sed -E 's/^.*#\{//g' | sed -E 's/\}.*($|#\{)//g'`; do
		VAL=$(grep -E "^$VAR=" $TMP_ENV | sed -E 's/.*=//')
		if [ "$VAL" != "" ]; then
			sed -Ei "s,#\{$VAR\},$VAL,g" $DST
		else
			error "The '$VAR' variable for the '$TPL' template is not defined"
		fi
	done
}

create_backend () {

	local ID="$1"
	if [[ "$ID" == "" ]]; then ID=1; fi
	local NS=$(get_bck_ns $ID)
	local IF=$(get_bck_if $ID)
	local IP=$(get_bck_ip $ID)
	local ROUTER_IP=$(get_bck_gw $ID)

	cmd "ip netns add $NS"
	cmd "ip link add $IF netns $PROXY_NS type veth peer name $IF netns $NS"
	cmd "ip -net $NS addr add $IP/16 dev $IF"
	cmd "ip -net $NS link set up dev $IF"

	# add GW info
	cmd "ip -net $PROXY_NS addr add $ROUTER_IP/16 dev $IF"
	cmd "ip -net $PROXY_NS link set up dev $IF"
	cmd "ip -net $PROXY_NS route add to $IP dev $IF via $ROUTER_IP"
	cmd "ip -net $NS route add default via $ROUTER_IP"

	start_httpd $ID

	add_etc_hosts "$IP	$ID.backend"
}

add_backends () {

	for ID in $(seq 1 $1); do
		create_backend $ID
	done
}

delete_backend () {

	local ID=$1
	if [[ "$ID" == "" ]]; then ID=1; fi
	local NS=$(get_bck_ns $ID)

	stop_httpd $ID
	cmd "ip netns del $NS"
}

delete_backends () {
	local NUM="$1"
	if [[ "$NUM" == "" ]]; then error_param "backend_number"; fi

	for ID in $(seq $NUM); do
		delete_backend $ID
	done
}

get_test_out_dir () {
	echo "test_$1_$2"
}

# variables used to form the curl command, these has to be cleaned beetwen tests
clean_test () {
	unset CMD CL METHOD SSL URL VHOST HEADERS BODY SSL REQUESTS RESOLV_VHOST FILE CONNS TIMEOUT THREADS BACKGROUND FILTER \
	NEXT_METHOD NEXT_BODY NEXT_FILE NEXT_VHOST NEXT_URL DISABLE TIMEOUT_100CONT PORT CMDPARAMS
}

replace_test_out ()
{
	for F in  `find $DIR/tests/ -name *.out.tmp`
	do
		BASE=$(echo $F | sed -E "s/.tmp$//")
		echo "Renaming $BASE"
		mv $F ${BASE}
	done
}

rm_test_out ()
{
	for F in  `find $DIR/tests/ -name *.tmp`
	do
		echo "Removed $F"
		rm $F
	done
}

## CLIENT ACTIONS
# the directory where the outputs are saved is passed as parameter
# in the directory will be created the following files:
#   - cmd, it is the command executed
#   - response_headers.out, they are the proxy response headers
#   - body.out, it is the response body returned by the proxy
exec_curl () {
	local DIR=$TMP_DIR
	local CLNS="$PROXY_NS"

	if [[ $1 != "" ]]; then DIR=$1; fi
	if [[ "$CL" != "" ]]; then CLNS=$(get_cl_ns $CL); fi
	if [[ "$URL" == "" ]]; then error_param "url"; fi
	if [[ "$METHOD" == "" ]]; then error_param "method"; fi

	local CMD_F="$DIR/cmd.out.tmp"
	local HEAD_F="$DIR/response_headers.out.tmp"
	local OUT_F="$DIR/body.out.tmp"
	local EXT_F="$DIR/extended.out.tmp"
	local ERR_F="$DIR/error.out.tmp"
	local FILTER_F="$DIR/filter.out.tmp"

	# create files to refresh files if become empty
	rm -f $CMD_F $HEAD_F $OUT_F $EXT_F $ERR_F $FILTER_F
	touch $CMD_F $HEAD_F $OUT_F $EXT_F $ERR_F

	local EXTENDED_OUT="-w 'HTTP_VERSION: %{http_version}\n'"
	local VHOST_LOC="$VHOST"
	local H=""
	local B=""
	local FG=""
	local F=""
	local P="${PROXY_VPORT}"

	local TIMEOUT100CONT_=""
	if [[ $TIMEOUT100CONT_ != "" ]]; then
		TIMEOUT100CONT_="--expect100-timeout $TIMEOUT_100CONT "
	fi
	HTTP="http"
	if [[ $SSL -eq 1 ]]; then
		HTTP="https"
		P="${PROXY_VPORT_SSL}"
	fi
	if [[ "$PORT" != "" ]]; then
		P="${PORT}"
	fi

	if [[ $FILTER != "" ]]; then
		F="-v"
	fi
	if [[ $HEADERS != "" ]]; then
		if [[ $HEADERS =~ ^@ ]]; then
			HEADERS=`cut -c2- <<< $HEADERS`
			while read -r IT
			do
				if [[ "$IT" == "" ]]; then continue; fi
				H="$H -H \"$IT\""
			done < <(cat $HEADERS)
		else
			while read -r IT
			do
				H="$H -H \"$IT\""
			done < <(echo "$HEADERS" | tr ";" "\n")
		fi
	fi
	if [[ $BODY != "" ]]; then
		B="--data-binary @$BODY"
	elif [[ $FILE != "" ]]; then
		B="-F 'file=@$FILE'"
	fi
	local SOCKET=""
	if [[ $SOCKET_UNIX -ne 0 ]]; then
		SOCKET="--unix-socket $PROXY_CTL_SOCKET"
		VHOST_LOC="${PROXY_SOCKET_IP}"
	elif [[ "$VHOST_LOC" == "" ]]; then
		VHOST_LOC="$PROXY_VIP:${P}"
	fi
	if [[ "$BACKGROUND" -eq 1 ]]; then
		FG="&"
		EXTENDED_OUT=""
		HEAD_F="/dev/null"
	fi

	local NEXT=""
	if [[ $NEXT_METHOD != "" ]]; then
		NEXT="-: -X $NEXT_METHOD"
		if [[ $NEXT_BODY != "" ]]; then
			NEXT="$NEXT --data-binary @$NEXT_BODY"
		elif [[ $NEXT_FILE != "" ]]; then
			NEXT="$NEXT -F 'file=@$NEXT_FILE'"
		fi

		if [[ $SSL -eq 1 ]]; then NEXT="$NEXT -k https";
		else NEXT="$NEXT http"; fi

		if [[ $NEXT_VHOST == "" ]]; then
			NEXT="$NEXT://${VHOST_LOC}$NEXT_URL"
		else
			NEXT="$NEXT://${NEXT_VHOST}$NEXT_URL"
		fi
		OUT_F="-"
	fi

	echo "ip netns exec $CLNS $CURL_BIN $CMDPARAMS -X $METHOD $B $H --connect-timeout $CLIENT_CONN_TIMEOUT \
${TIMEOUT100CONT_}-s -k --show-error $EXTENDED_OUT $F \
-D $HEAD_F -o $OUT_F $SOCKET \"$HTTP://${VHOST_LOC}$URL\" $NEXT >$EXT_F 2>$ERR_F $FG" > $CMD_F

	# mask tpl dir
	sed -i "s,$TEST_TPL,\$TEST_TPL,g" $CMD_F

	# cmd to debug
	echo "ip netns exec $CLNS $CURL_BIN $CMDPARAMS -X $METHOD $B $H --connect-timeout $CLIENT_CONN_TIMEOUT \
${TIMEOUT100CONT_}-s -k --show-error $SOCKET \"$HTTP://${VHOST_LOC}$URL\" $NEXT" > ${CMD_F}.dbg

	bash $CMD_F

	if [[ "$BACKGROUND" -ne 1 ]]; then
		mask_headers $HEAD_F
	fi

	if [[ $NEXT == "" ]]; then
		pretty_json $OUT_F
		mask_out $OUT_F
	fi

	if [[ "$FILTER" != "" ]]; then
		grep "$FILTER" $ERR_F > $FILTER_F
		rm $ERR_F
		touch $ERR_F
	else
		touch $FILTER_F
	fi
}

## CLIENT SSL ACTIONS
# the directory where the outputs are saved is passed as parameter
# in the directory will be created the following files:
#   - cmd, it is the command executed
#   - response_headers.out, they are the proxy response headers
#   - body.out, it is the response body returned by the proxy
exec_openssl () {
	local DIR=$TMP_DIR
	local CLNS="$PROXY_NS"

	if [[ $1 != "" ]]; then DIR=$1; fi
	if [[ "$CL" != "" ]]; then CLNS=$(get_cl_ns $CL); fi
	if [[ "$URL" == "" ]]; then error_param "url"; fi
	if [[ "$METHOD" == "" ]]; then error_param "method"; fi

	local CMD_F="$DIR/cmd.out.tmp"
	local HEAD_F="$DIR/response_headers.out.tmp"
	local OUT_F="$DIR/body.out.tmp"
	local EXT_F="$DIR/extended.out.tmp"
	local ERR_F="$DIR/error.out.tmp"
	local FILTER_F="$DIR/filter.out.tmp"

	# create files to refresh files if become empty
	rm -f $CMD_F $HEAD_F $OUT_F $EXT_F $ERR_F $FILTER_F
	touch $CMD_F $HEAD_F $OUT_F $EXT_F $ERR_F

	local HTTP_VERSION="HTTP/1.1"
	local VHOST_LOC="$VHOST"
	local H=""
	local B=""
	local FG=""
	local F=""
	local P="${PROXY_VPORT}"

	P="${PROXY_VPORT_SSL}"
	if [[ "$PORT" != "" ]]; then
		P="${PORT}"
	fi

	VHOST_LOC="$PROXY_VIP:${P}"

	echo "ip netns exec $CLNS timeout 1 $OPENSSL_BIN s_client \
-connect ${VHOST_LOC} $CMDPARAMS >$EXT_F 2>/dev/null $FG" > $CMD_F

	# mask tpl dir
	sed -i "s,$TEST_TPL,\$TEST_TPL,g" $CMD_F

	# cmd to debug
	echo "ip netns exec $CLNS echo -e \"$METHOD $URL $HTTP_VERSION\r\n\r\n\" | $OPENSSL_BIN s_client \
-connect ${VHOST_LOC} $CMDPARAMS" > ${CMD_F}.dbg

	bash $CMD_F

	if [[ "$FILTER" != "" ]]; then
		grep "$FILTER" $EXT_F > $FILTER_F
		mv $FILTER_F $EXT_F
	fi
}

pretty_json () {
	if [[ "$1" == "" ]]; then error_param "file"; fi

	local OUT_F=$1
	local TMP_JSON="$TMP_DIR/pretty.json"

	if [[ -s $OUT_F ]]; then
		cat $OUT_F | python3 -m json.tool > $TMP_JSON 2>/dev/null
		if [[ -s $TMP_JSON ]]; then
			mv $TMP_JSON $OUT_F
		fi
	fi
}

mask_headers () {
	if [[ "$1" == "" ]]; then error_param "file"; fi
	local FILE=$1
	sed -Ei 's/Server: zproxy\/.*$/Server: zproxy\/VERSION/g' $FILE
	sed -Ei 's/^Content-Length: [0-9]+/Content-Length: #######/g' $FILE
}

mask_out () {
	if [[ "$1" == "" ]]; then error_param "file"; fi
	local FILE=$1
	# remove bonding headers uploading files
	sed -Ei "s/--------------------------\w+/--------------------------################/g" $FILE
	sed -Ei "s/\"connect-time\"\s*:\s*([0-9\.]+|[1-9]+[-\.0-9e]+|-1.0),?/\"connect-time\": \"#######\",/g" $FILE
	sed -Ei "s/\"response-time\"\s*:\s*([0-9\.]+|[1-9]+[-\.0-9e]+|-1.0),?/\"response-time\": \"#######\",/g" $FILE
	sed -Ei 's/"last-seen":\s*0?[1-9][0-9]*.*$/"last-seen": #######/g' $FILE
	sed -Ei 's/^User-Agent: curl\/[0-9\.]+/User-Agent: curl\/VERSION/g' $FILE
	sed -Ei 's/zproxy\/[0-9]+\.[0-9]+\.[0-9]+ \([a-z0-9]+\)/zproxy\/VERSION/g' $FILE
	sed -Ei 's/zproxy\/[0-9]+\.[0-9]+\.[0-9]+/zproxy\/VERSION/g' $FILE
	if [[ $WRK_MASK == 1 ]]
	then
		sed -Ei 's/("[0-9]xx-code-hits":) [0-9]+/\1 #######/g' $FILE
		sed -Ei 's/("connections":) [0-9]+/\1 #######/g' $FILE
		sed -Ei 's/("pending-connections":) [0-9]+/\1 #######/g' $FILE
	fi
}

exec_average () {
	if [[ $1 == "" ]]; then error_param "directory"; fi
	if [[ "$CL" == "" ]]; then error_param "client_id"; fi
	if [[ "$REQUESTS" == "" ]]; then error_param "iterations"; fi

	local DIR=$TMP_DIR
	local TMP_AVG_PREF="$1/"
	rm -f "$TMP_AVG_PREF*.tmp"

	for IT in $(seq 1 $REQUESTS)
	do
		exec_curl $DIR
		for F in $AVERAGE_FILES_TMP; do
			cat "$DIR/$F" >> "${TMP_AVG_PREF}${F}.2"
		done
	done

	for F in $AVERAGE_FILES_TMP; do
		cat "${TMP_AVG_PREF}${F}.2" | grep -E '.' |  sort | uniq -c >"${TMP_AVG_PREF}${F}"
		rm "$DIR/$F" "${TMP_AVG_PREF}${F}.2"
	done
}

exec_stress_reload() {
	if [[ "$1" == "" ]]; then error_param "directory"; fi
	local DIR=$1
	if [[ "$TIMEOUT" == "" ]]; then error_param "timeout"; fi
	if [[ "$INTERVAL" == "" ]]; then error_param "interval"; fi

	URL="$PROXY_CTL_RELOAD_URL"
	METHOD="$PROXY_CTL_RELOAD_METHOD"

	PID=""
	if [[ VALGRIND -eq 0 ]] && [[ -f $PROXY_PID_F ]]
	then
		PID="$(cat $PROXY_PID_F)"
	else
		PID=$(ps aux | grep valgrind | grep -Ev grep | awk '{print $2}')
	fi

	while [ 1 ]
	do
		kill -SIGUSR1 $PID
		sleep $INTERVAL
	done &
	local LOOP_PID=$!

	sleep $TIMEOUT
	if kill -0 "$LOOP_PID";
	then
		kill "$LOOP_PID"
	fi
}

# exec_stress $CL $CONNS $TIMEOUT $THREADS
exec_wrk() {
	if [[ "$1" == "" ]]; then error_param "directory"; fi
	if [[ "$CL" == "" ]]; then error_param "client_id"; fi
	if [[ "$URL" == "" ]]; then error_param "url"; fi
	local DIR=$1
	local NS=$NS
	local BG=""
	if [[ "$NS" == "" ]]; then NS=$(get_cl_ns $CL); fi
	if [[ "$CONNS" == "" ]]; then error_param "connections"; fi
	if [[ "$TIMEOUT" == "" ]]; then error_param "timeout"; fi
	if [[ "$THREADS" == "" ]]; then error_param "threads"; fi
	if [[ "$BACKGROUND" == "1" ]]; then BG="&"; fi;
	local P="${PROXY_VPORT}"

	local TMP="$TMP_DIR/bm"
	local BM_OUT="$DIR/benchmark.out.tmp"
	local CMD_F="$DIR/cmd.out.tmp"

	HTTP="http"
	if [[ $SSL -eq 1 ]]; then
		HTTP="https"
		P="$PROXY_VPORT_SSL"
	fi
	if [[ "$PORT" != "" ]]; then
		P="${PORT}"
	fi

	if [[ "$VHOST" = "" ]]; then
		VHOST="$PROXY_VIP:${P}"
	fi

	local COMMAND="ip netns exec $NS $WRK_BIN -c $CONNS -d $TIMEOUT -t $THREADS $HTTP://${VHOST}$URL $BG"
	echo "$COMMAND" >$CMD_F

	bash $CMD_F >$TMP

	if [[ "$BACKGROUND" == "" ]]; then
		grep 'Requests/sec:' $TMP | sed -E 's/Requests\/sec:\s*//' >$BM_OUT
	fi

	WRK_MASK=1
}

exec_websocket() {
	if [[ "$1" == "" ]]; then error_param "directory"; fi
	if [[ "$CL" == "" ]]; then error_param "client_id"; fi
	if [[ "$URL" == "" ]]; then error_param "url"; fi
	if [[ $SSL -ne 1 ]]; then SSL=0; fi
	local DIR=$1
	local NS=$NS
	if [[ "$NS" == "" ]]; then NS=$(get_cl_ns $CL); fi
	if [[ "$PORT" == "" ]]
	then
		[[ $SSL -eq 1 ]] &&
			PORT=443 ||
			PORT=80
	fi
	if [[ "$VHOST" == "" ]]; then VHOST="$PROXY_VIP"; fi

	local WS_CL_SCRIPT="../../tpl/ws-cl-http.py"
	local TMP="$DIR/body.out.tmp"
	local ERR_TMP="$DIR/error.out.tmp"
	local CMD_F="$DIR/cmd.out.tmp"

	local COMMAND="ip netns exec $NS $WS_CL_SCRIPT $VHOST $PORT $SSL $URL"
	echo "$COMMAND" >$CMD_F

	bash $CMD_F >$TMP 2>$ERR_TMP
}

clean_wrk() {
	WRK_MASK=0
	pkill wrk
}

exec_benchmark() {
	if [[ $1 == "" ]]; then error_param "directory"; fi

	local DIR="$1"
	local TMP_F="$DIR/benchmark.out.tmp"
	local OUT_F="$DIR/benchmark.out"
	local NEW_F="$OUT_F.new"
	local BM_F="$OUT_F.bm"

	CONNS=$BENCH_CONNS
	TIMEOUT=$BENCH_DELAY
	THREADS=$BENCH_CL_THREADS

	rm -f $NEW_F

	exec_wrk $DIR

	# Get percentage
	NEW_BENCH=$(cat $TMP_F)
	echo $NEW_BENCH >$BM_F

	RESULT=$(perl -E "\$v=100*$NEW_BENCH/$BENCH_WITHOUT_PROXY;say int \$v;")
	echo "$RESULT" >$TMP_F

	if [[ ! -f $OUT_F ]]; then
		echo "Reference benchmark does not exist: $OUT_F"
		return 1
	fi

	OLD_BENCH=$(cat $OUT_F)
	ERR_EDGE=$(expr $OLD_BENCH - $BENCH_ERR_ACCEPTED)
	NEW_EDGE=$(expr $OLD_BENCH + $BENCH_ERR_ACCEPTED)
	echo "Benchmark: proxy-bench/client-bench = $NEW_BENCH/$BENCH_WITHOUT_PROXY = $RESULT%"
	if [[ $RESULT -lt $ERR_EDGE ]]; then
		echo "The new benchmark value '$RESULT%' is worse than the saved one '$OLD_BENCH+$BENCH_ERR_ACCEPTED%'"
		return 1
	elif [[ $RESULT -gt $NEW_EDGE ]]; then
		echo "The new benchmark value '$RESULT%' is better than the saved one '$OLD_BENCH~$BENCH_ERR_ACCEPTED%'"
		echo "Overwrite the file '$OUT_F' with the '$NEW_F' is you want to save it"
		mv $TMP_F "$OUT_F.new"
	else
		echo "The new benchmark is '$RESULT%' similar to the saved one '$OLD_BENCH~$BENCH_ERR_ACCEPTED%'"
	fi

	rm $TMP_F
	return 0
}

check_dependencies () {
	if [ $UID -ne 0 ]; then
		error "You must be root to run this test script"
	elif [[ ! -f "$CURL_BIN" ]]; then
		error "The 'curl' binary was not found, try to configure the variable 'CURL_BIN'"
	elif [[ ! -f "$WRK_BIN" ]]; then
		error "The 'wrk' binary was not found, try to configure the variable 'WRK_BIN'"
	elif [[ ! -f "$HTTPD_BIN" ]]; then
		error "The 'ngnix' binary was not found, try to configure the variable 'HTTPD_BIN'"
	elif [[ ! -f "$PROXY_BIN" ]]; then
		error "The 'zproxy' binary was not found, try to configure the variable 'PROXY_BIN'"
	elif [[ ! -f "$NGINX_MODULE_ECHO" ]]; then
		error "The 'echo' nginx module was not found, try to install the package 'libnginx-mod-http-echo'"
	elif [[ ! -f "$NGINX_MODULE_HEADER" ]]; then
		error "The 'headers-more' nginx module was not found, try to install the package 'libnginx-mod-http-headers-more-filter'"
	fi
}

print_report () {
	if [[ "$1" == "" ]]; then error_param "test_name"; fi
	if [[ "$2" == "" ]]; then error_param "command_name"; fi
	if [[ "$3" == "" ]]; then error_param "output_file"; fi

	# print stdout
	echo "### Error in test '$1/$2/'"
	echo ""
	if [[ -f $3 ]]; then
		cat $3
	else
		echo $3
	fi
	echo ""
	echo ""

	# print file
	echo "### Error in test '$1', command '$2'" >>$REPORT_F
	if [[ -f $3 ]]; then
		cat $3 >>$REPORT_F
	else
		echo $3 >>$REPORT_F
	fi
	echo "" >>$REPORT_F
	echo ""
	echo "##########################################################################################" >>$REPORT_F
	echo "" >>$REPORT_F
	echo "" >>$REPORT_F
}

create_benchmark_report () {
	rm -f $BENCH_REPORT
	local BM=$(find . -name "*.bm" | sort)
	if [[ "$BM" != "" ]]; then
		echo "Ideal throughput CLIENT - BACKEND: $BENCH_WITHOUT_PROXY" >$BENCH_REPORT
		echo "(The following percents are regarding the ideal throughput)" >>$BENCH_REPORT
		echo "" >>$BENCH_REPORT
		for B in $BM; do
			BM_NEW=`cat $B`
			F=`echo $B | sed 's/.bm//'`
			OLD=`cat $F`

			OK=1

			if [[ -f "${F}.new" ]]; then
				NEW=`cat $F.new`
			elif [[ -f "${F}.tmp" ]]; then
				NEW=`cat $F.tmp`
				OK=0
			else
				NEW="$OLD"
			fi

			BM_OLD=$(perl -E "\$v=$OLD*$BENCH_WITHOUT_PROXY/100;say int \$v;")

			echo "  passed=$OK $F: $BM_OLD ($OLD%) => $BM_NEW ($NEW%)" >>$BENCH_REPORT
		done

		echo "A benchmark report has been generated in '$BENCH_REPORT'"
	fi
}

find_diff_files () {

	local TMP=$(find . -name "*.tmp" | sort)
	if [[ $TMP != "" ]]; then
		msg "Error files: "
		echo "$TMP"
	fi

	local NEW=$(find . -name "*.new" | sort)
	if [[ $NEW != "" ]]; then
		msg "Benchmark improvements:"
		echo "$NEW"
	fi

	if [[ $NEW != "" || $TMP != "" ]]; then return 1; fi
}

find_diff_errors () {
	if [[ "$1" == "" ]]; then error_param "directory"; fi
	local DIR=$1

	for F in $OUTPUT_FILES; do
	    F="$DIR/$F"
		if [[ -f "$F.tmp" ]]; then
			diff -w $DIFF_OPT $F $F.tmp 2>&1
			if [[ $? -eq 0 ]]; then
				rm $F.tmp;
			else
				echo ""
			fi
		fi
	done
}

# /etc/hosts
create_etc_hosts () {
	grep "$ETC_TAG" $ETC_FILE >/dev/null
	if [[ $? -ne 0 ]]; then
		cp $ETC_FILE $ETC_FILE_SAVED
		echo "$ETC_TAG" >> $ETC_FILE
		deploy_tpl $ETC_TPL $ETC_TPL_TMP
		cat $ETC_TPL_TMP >> $ETC_FILE
		rm $ETC_TPL_TMP
	fi
}

add_etc_hosts () {
	if [[ "$1" == "" ]]; then error_param "host_entry"; fi
	echo "$1" >> $ETC_FILE
}

del_etc_hosts() {
	if [[ -f $ETC_FILE_SAVED ]]; then
		cp $ETC_FILE_SAVED $ETC_FILE
	fi
}

clean_valgrind() {
	rm -rf $VALGRIND_OUT_ALL
}

recollect_valgrind() {
	echo ">>>>>>>>> $1" >>$VALGRIND_OUT_ALL
	cat $VALGRIND_OUT >>$VALGRIND_OUT_ALL
	echo "" >>$VALGRIND_OUT_ALL
	echo "" >>$VALGRIND_OUT_ALL
	echo "" >>$VALGRIND_OUT_ALL

}

