Changed repo organisation
This commit is contained in:
parent
50891b21f2
commit
df45cf75db
32 changed files with 0 additions and 0 deletions
11
tests/config.txt
Normal file
11
tests/config.txt
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
[global]
|
||||
log = true
|
||||
pid_file = /tmp/HTTPd.pid
|
||||
daemon = start
|
||||
|
||||
[[vhosts]]
|
||||
server_name = my_server
|
||||
ip = 127.0.0.1
|
||||
port = 6996
|
||||
root_dir = .
|
||||
default_file='/dev/null'
|
||||
86
tests/config_reader.sh
Executable file
86
tests/config_reader.sh
Executable file
|
|
@ -0,0 +1,86 @@
|
|||
#!/bin/sh
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 --path-config <file> [--path-bin <bin>] [--daemon <options>]"
|
||||
echo
|
||||
echo "Options:"
|
||||
echo " --path-config <fichier> (mandatory) path to the config file"
|
||||
echo " --path-bin <chemin-binaire> (optional) path to the httpd binary, default is ./httpd"
|
||||
echo " --daemon <options> (optional) option passed to httpd"
|
||||
echo " -h, --help prints this help page"
|
||||
exit 1
|
||||
}
|
||||
|
||||
OPTIONS=$(getopt -o h --long path-config:,path-bin:,daemon:,help -- "$@")
|
||||
if [ $? -ne 0 ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
eval set -- "$OPTIONS"
|
||||
|
||||
PATH_CONFIG=""
|
||||
PATH_BIN="./httpd"
|
||||
DAEMON_OPTS=""
|
||||
|
||||
# Lecture des arguments
|
||||
while true; do
|
||||
case "$1" in
|
||||
--path-config)
|
||||
PATH_CONFIG="$2"
|
||||
shift 2
|
||||
;;
|
||||
--path-bin)
|
||||
PATH_BIN="$2"
|
||||
shift 2
|
||||
;;
|
||||
--daemon)
|
||||
DAEMON_OPTS="--daemon $2"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# verify mandatory option
|
||||
if [ -z "$PATH_CONFIG" ]; then
|
||||
echo "error : --path-config is mandatory"
|
||||
echo
|
||||
usage
|
||||
fi
|
||||
|
||||
! [ -f "$PATH_CONFIG" ] && (echo "config file: $PATH_CONFIG is not a file"; exit 1;)
|
||||
PATH_BIN_TO_RUN="$PATH_BIN"
|
||||
|
||||
if [[ "$PATH_BIN_TO_RUN" != */* ]]; then
|
||||
PATH_BIN_TO_RUN="./$PATH_BIN_TO_RUN"
|
||||
fi
|
||||
|
||||
[ -x "$PATH_BIN_TO_RUN" ] || (echo "error: binary file: '$PATH_BIN_TO_RUN' is not executable or does not exist."; exit 1;)
|
||||
|
||||
PATH_BIN="$PATH_BIN_TO_RUN"
|
||||
|
||||
# main
|
||||
ARGS=""
|
||||
regex="([^ ]+) ?= ?([^ ]+)"
|
||||
|
||||
while IFS= read -r line || [[ -n "$line" ]];
|
||||
do
|
||||
[ "$line" = "[global]" ] || [ "$line" = "[[vhosts]]" ] && continue;
|
||||
|
||||
if [[ $line =~ $regex ]]; then
|
||||
ARGS="$ARGS --${BASH_REMATCH[1]} ${BASH_REMATCH[2]}"
|
||||
fi
|
||||
done < "$PATH_CONFIG"
|
||||
|
||||
# launch binary with correct args
|
||||
echo "command run is: $PATH_BIN $DAEMON_OPTS ${ARGS:1}"
|
||||
$PATH_BIN $DAEMON_OPTS ${ARGS:1}
|
||||
1
tests/test_root_dir/index.html
Normal file
1
tests/test_root_dir/index.html
Normal file
|
|
@ -0,0 +1 @@
|
|||
<h1> YEAAH </h1>
|
||||
178
tests/test_suite.py
Normal file
178
tests/test_suite.py
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
import subprocess as sp
|
||||
import http
|
||||
import requests
|
||||
import socket
|
||||
import pytest
|
||||
import time
|
||||
|
||||
host = "127.0.0.1"
|
||||
port = "6994"
|
||||
|
||||
executable = "./httpd"
|
||||
|
||||
def spawn_httpd(stdout_filename, args=[]):
|
||||
with open(stdout_filename,"w") as f:
|
||||
httpd_proc = sp.Popen([executable,"--pid_file","/tmp/HTTPd.pid","--ip",host,"--port", port, "--root_dir","test_root_dir/","--server_name","httpd"] if args == [] else [executable] + args, stdout=f,stderr=sp.PIPE,bufsize=0)
|
||||
time.sleep(0.2)
|
||||
|
||||
return httpd_proc
|
||||
|
||||
def kill_httpd(proc):
|
||||
#proc.send_signal(sp.SIGINT)
|
||||
proc.kill()
|
||||
|
||||
# @pytest.mark.timeout(2)
|
||||
def test_bad_config():
|
||||
proc = spawn_httpd("out.log", ["hello","world"])
|
||||
proc.wait(1)
|
||||
try:
|
||||
assert proc.returncode == 2
|
||||
finally:
|
||||
kill_httpd(proc)
|
||||
|
||||
# @pytest.mark.timeout(2)
|
||||
def test_get_index():
|
||||
proc = spawn_httpd("out.log")
|
||||
req = requests.get(f"http://{host}:{port}/index.html")
|
||||
assert req.status_code == 200
|
||||
with open("./test_root_dir/index.html","r") as f:
|
||||
try:
|
||||
assert f.read() == req.text
|
||||
finally:
|
||||
kill_httpd(proc)
|
||||
|
||||
# @pytest.mark.timeout(2)
|
||||
def test_get_default():
|
||||
proc = spawn_httpd("out.log")
|
||||
req = requests.get(f"http://{host}:{port}/")
|
||||
assert req.status_code == 200
|
||||
with open("./test_root_dir/index.html","r") as f:
|
||||
try:
|
||||
assert f.read() == req.text
|
||||
finally:
|
||||
kill_httpd(proc)
|
||||
|
||||
# @pytest.mark.timeout(2)
|
||||
def test_no_file():
|
||||
proc = spawn_httpd("out.log")
|
||||
req = requests.get(f"http://{host}:{port}/notindex.html")
|
||||
assert req.status_code == 404
|
||||
|
||||
# @pytest.mark.timeout(2)
|
||||
def test_bad_request():
|
||||
proc = spawn_httpd("out.log")
|
||||
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
||||
sock.connect((host,int(port)))
|
||||
|
||||
request = f"GET /index.html FTP/1.1\r\nHOST: {host}:{port}\r\nConnection: close\r\n\r\n"
|
||||
|
||||
sock.sendall(request.encode())
|
||||
|
||||
resp = sock.recv(1024)
|
||||
resp_decoded = resp.decode()
|
||||
|
||||
try:
|
||||
assert "400 Bad Request" in resp_decoded
|
||||
finally:
|
||||
kill_httpd(proc)
|
||||
|
||||
# @pytest.mark.timeout(2)
|
||||
def test_invalid_method():
|
||||
proc = spawn_httpd("out.log")
|
||||
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
||||
sock.connect((host,int(port)))
|
||||
|
||||
request = f"PUT /index.html HTTP/1.1\r\nHOST: {host}:{port}\r\nConnection: close\r\n\r\n"
|
||||
|
||||
sock.sendall(request.encode())
|
||||
|
||||
response = http.client.HTTPResponse(sock)
|
||||
response.begin()
|
||||
|
||||
try:
|
||||
assert response.status == 405
|
||||
finally:
|
||||
kill_httpd(proc)
|
||||
|
||||
|
||||
# @pytest.mark.timeout(2)
|
||||
def test_invalid_version():
|
||||
proc = spawn_httpd("out.log")
|
||||
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
||||
sock.connect((host,int(port)))
|
||||
|
||||
request = f"GET /index.html HTTP/1.2\r\nHOST: {host}:{port}\r\nConnection: close\r\n\r\n"
|
||||
|
||||
sock.sendall(request.encode())
|
||||
|
||||
response = http.client.HTTPResponse(sock)
|
||||
response.begin()
|
||||
|
||||
try:
|
||||
assert response.status == 505
|
||||
finally:
|
||||
kill_httpd(proc)
|
||||
|
||||
# @pytest.mark.timeout(2)
|
||||
def test_bad_request():
|
||||
proc = spawn_httpd("out.log")
|
||||
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
||||
sock.connect((host,int(port)))
|
||||
|
||||
request = f"GET /index.html FTP/1.1\r\nHOST: {host}:{port}\r\nConnection: close\r\n\r\n"
|
||||
|
||||
sock.sendall(request.encode())
|
||||
|
||||
response = http.client.HTTPResponse(sock)
|
||||
response.begin()
|
||||
|
||||
try:
|
||||
assert response.status == 400
|
||||
finally:
|
||||
kill_httpd(proc)
|
||||
|
||||
@pytest.mark.timeout(2)
|
||||
def test_head_index():
|
||||
proc = spawn_httpd("out.log")
|
||||
try:
|
||||
req = requests.head(f"http://{host}:{port}/index.html")
|
||||
assert req.status_code == 200
|
||||
assert req.text == ""
|
||||
finally:
|
||||
kill_httpd(proc)
|
||||
|
||||
@pytest.mark.timeout(2)
|
||||
def test_missing_host():
|
||||
proc = spawn_httpd("out.log")
|
||||
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
||||
sock.connect((host,int(port)))
|
||||
|
||||
request = f"GET /index.html HTTP/1.1\r\nConnection: close\r\n\r\n"
|
||||
|
||||
sock.sendall(request.encode())
|
||||
|
||||
response = http.client.HTTPResponse(sock)
|
||||
response.begin()
|
||||
|
||||
try:
|
||||
assert response.status == 400
|
||||
finally:
|
||||
kill_httpd(proc)
|
||||
|
||||
@pytest.mark.timeout(2)
|
||||
def test_directory_traversal():
|
||||
proc = spawn_httpd("out.log")
|
||||
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
||||
sock.connect((host,int(port)))
|
||||
|
||||
request = f"GET /../test_suite.py HTTP/1.1\r\nHOST: {host}:{port}\r\nConnection: close\r\n\r\n"
|
||||
|
||||
sock.sendall(request.encode())
|
||||
|
||||
response = http.client.HTTPResponse(sock)
|
||||
response.begin()
|
||||
|
||||
try:
|
||||
assert response.status in [400, 403, 404]
|
||||
finally:
|
||||
kill_httpd(proc)
|
||||
40
tests/tests_mieux.sh
Executable file
40
tests/tests_mieux.sh
Executable file
|
|
@ -0,0 +1,40 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Simple test script for HTTP/1.1 Host header compliance
|
||||
# Usage: ./test_host_compliance.sh [IP] [PORT]
|
||||
|
||||
IP=${1:-"127.0.0.1"}
|
||||
PORT=${2:-"6996"}
|
||||
|
||||
echo "Targeting server at $IP:$PORT"
|
||||
|
||||
test_req() {
|
||||
NAME="$1"
|
||||
PAYLOAD="$2"
|
||||
EXPECTED="$3"
|
||||
|
||||
echo -n "Test: $NAME ... "
|
||||
# Send payload, wait max 1s for response
|
||||
RESP=$(printf "$PAYLOAD" | nc -w 1 $IP $PORT 2>/dev/null | head -n 1)
|
||||
|
||||
if echo "$RESP" | grep -q "$EXPECTED"; then
|
||||
echo "PASS"
|
||||
else
|
||||
echo "FAIL (Expected '$EXPECTED', got '$RESP')"
|
||||
fi
|
||||
}
|
||||
|
||||
# 1. Valid Request
|
||||
test_req "Valid Request" "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n" "200 OK"
|
||||
|
||||
# 2. Missing Host Header
|
||||
test_req "Missing Host" "GET / HTTP/1.1\r\n\r\n" "400 Bad Request"
|
||||
|
||||
# 3. Empty Host Header
|
||||
test_req "Empty Host" "GET / HTTP/1.1\r\nHost:\r\n\r\n" "400 Bad Request"
|
||||
|
||||
# 4. Multiple Host Headers
|
||||
test_req "Multiple Hosts" "GET / HTTP/1.1\r\nHost: a\r\nHost: b\r\n\r\n" "400 Bad Request"
|
||||
|
||||
# 5. Bad Protocol Version (Should be 505 now with the fix)
|
||||
test_req "Bad Protocol (HTTP/1.0)" "GET / HTTP/1.0\r\nHost: localhost\r\n\r\n" "505"
|
||||
Loading…
Add table
Add a link
Reference in a new issue