#!/bin/sh
#
# Copyright (C) 2026 Nikos Mavrogiannopoulos
#
# This file is part of ocserv.
#
# ocserv is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at
# your option) any later version.
#
# ocserv is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GnuTLS; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

SERV="${SERV:-../src/ocserv}"
srcdir=${srcdir:-.}
NO_NEED_ROOT=1

. `dirname $0`/common.sh

eval "${GETPORT}"

echo "Testing HTTP body and header size limits..."

update_config test-user-cert.config
launch_simple_sr_server -d 1 -f -c ${CONFIG}
PID=$!
wait_server $PID

BODY_LIMIT=$((256 * 1024))
HDR_LIMIT=$((16 * 1024))
HDRFILE=test-http-limits.$$.tmp
CURL_OUT=test-http-limits.$$.curl

# Run a POST via socket_wrapper; print HTTP status code to stdout.
# Returns curl's exit code (28 = timeout, 52 = empty reply, 0 = got response).
curl_post() {
	LD_PRELOAD=libsocket_wrapper.so \
	curl --silent --insecure --max-time 10 --user-agent "AnyConnect" \
		--write-out "%{http_code}" \
		"$@" -o "$CURL_OUT"
}

echo -n "Testing body within limit (${BODY_LIMIT} bytes)... "
http_code=$(head -c ${BODY_LIMIT} /dev/zero | \
	curl_post -X POST https://$ADDRESS:$PORT/auth --data-binary @-)
rc=$?
if [ $rc -ne 0 ] || [ "$http_code" = "000" ]; then
	echo "FAILED (curl exit=$rc, HTTP=$http_code)"
	cat "$CURL_OUT"
	fail $PID "Body within limit was rejected"
fi
echo "ok (HTTP $http_code)"

echo -n "Testing body over limit ($((BODY_LIMIT + 1)) bytes)... "
http_code=$(head -c $((BODY_LIMIT + 1)) /dev/zero | \
	curl_post -X POST https://$ADDRESS:$PORT/auth --data-binary @-)
rc=$?
if [ $rc -eq 0 ] && [ "$http_code" != "413" ] && [ "$http_code" != "431" ]; then
	echo "FAILED (curl exit=$rc, HTTP=$http_code)"
	cat "$CURL_OUT"
	fail $PID "Body over limit was not rejected (HTTP $http_code)"
fi
echo "ok (HTTP $http_code)"

# Generate N headers of 1 KB each into $HDRFILE for curl's -H @file.
# With a 16 KB limit: 3 headers (3 KB) is within; 20 headers (20 KB) is over.
HDR_CHUNK=$((1 * 1024))
gen_headers() {
	n=$1; rm -f "$HDRFILE"; i=0
	while [ $i -lt $n ]; do
		{ printf 'X-Test-%d: ' $i
		  head -c ${HDR_CHUNK} /dev/zero | tr '\0' 'A'
		  printf '\n'; } >> "$HDRFILE"
		i=$((i+1))
	done
}

echo -n "Testing headers within limit ($((3 * HDR_CHUNK)) bytes)... "
gen_headers 3
# Explicit Content-Length: 0 is required: older curl (e.g. 7.76.1 on CentOS 9)
# omits it for empty bodies, leaving the server unable to determine
# message_complete and causing a timeout.
http_code=$(curl_post -X POST -H "Content-Length: 0" -H @"$HDRFILE" \
	https://$ADDRESS:$PORT/auth)
rc=$?
if [ $rc -ne 0 ] || [ "$http_code" = "000" ]; then
	echo "FAILED (curl exit=$rc, HTTP=$http_code)"
	cat "$CURL_OUT"
	fail $PID "Headers within limit were rejected"
fi
echo "ok (HTTP $http_code)"

echo -n "Testing headers over limit ($((20 * HDR_CHUNK)) bytes)... "
gen_headers 20
http_code=$(curl_post -X POST -H "Content-Length: 0" -H @"$HDRFILE" \
	https://$ADDRESS:$PORT/auth)
rc=$?
if [ $rc -eq 0 ] && [ "$http_code" != "413" ] && [ "$http_code" != "431" ]; then
	echo "FAILED (curl exit=$rc, HTTP=$http_code)"
	cat "$CURL_OUT"
	fail $PID "Headers over limit were not rejected (HTTP $http_code)"
fi
echo "ok (HTTP $http_code)"

rm -f "$HDRFILE" "$CURL_OUT"
cleanup
exit 0
