Add docker integration tests for simple ASGI (HTTP protocol)

Tests the ASGI worker with direct HTTP requests without uWSGI protocol.
Includes tests for GET, POST, query strings, path handling, keepalive,
large bodies, and custom headers.
This commit is contained in:
Benoit Chesneau 2026-01-25 15:03:12 +01:00
parent 47b1cb82b8
commit 6575d86251
4 changed files with 193 additions and 0 deletions

View File

@ -0,0 +1,18 @@
FROM python:3.11-slim
WORKDIR /build
# Copy gunicorn source
COPY . /build/
# Install gunicorn from source
RUN pip install --no-cache-dir -e .
# Copy test app
WORKDIR /app
COPY tests/docker/asgi/app.py /app/
# Expose HTTP port
EXPOSE 8000
CMD ["gunicorn", "--worker-class", "asgi", "--bind", "0.0.0.0:8000", "app:app"]

45
tests/docker/asgi/app.py Normal file
View File

@ -0,0 +1,45 @@
"""Simple ASGI test application for HTTP protocol testing."""
async def app(scope, receive, send):
"""Simple ASGI application that echoes request info."""
if scope["type"] == "lifespan":
while True:
message = await receive()
if message["type"] == "lifespan.startup":
await send({"type": "lifespan.startup.complete"})
elif message["type"] == "lifespan.shutdown":
await send({"type": "lifespan.shutdown.complete"})
return
if scope["type"] != "http":
return
# Read body
body = b""
while True:
message = await receive()
body += message.get("body", b"")
if not message.get("more_body", False):
break
# Build response
method = scope["method"]
path = scope["path"]
query = scope.get("query_string", b"").decode("utf-8")
response_body = f"Method: {method}\nPath: {path}\nQuery: {query}\nBody: {body.decode('utf-8')}\n"
response_bytes = response_body.encode("utf-8")
await send({
"type": "http.response.start",
"status": 200,
"headers": [
[b"content-type", b"text/plain"],
[b"content-length", str(len(response_bytes)).encode()],
],
})
await send({
"type": "http.response.body",
"body": response_bytes,
})

View File

@ -0,0 +1,14 @@
services:
gunicorn:
build:
context: ../../..
dockerfile: tests/docker/asgi/Dockerfile
command: >
gunicorn
--worker-class asgi
--bind 0.0.0.0:8000
--workers 1
--log-level debug
app:app
ports:
- "8080:8000"

116
tests/docker/asgi/test_asgi.sh Executable file
View File

@ -0,0 +1,116 @@
#!/bin/bash
# Integration test for ASGI HTTP protocol support
#
# This script tests that gunicorn's ASGI worker correctly handles
# HTTP requests directly (without uWSGI protocol).
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
# Use IPv4 explicitly to avoid Docker IPv6 issues
BASE_URL="http://127.0.0.1:8080"
cleanup() {
echo "Cleaning up..."
docker compose down -v 2>/dev/null || true
}
trap cleanup EXIT
echo "=== Building and starting containers ==="
docker compose up -d --build
echo "=== Waiting for services to be ready ==="
sleep 5
echo "=== Running tests ==="
# Test 1: Simple GET request
echo "Test 1: Simple GET request"
RESPONSE=$(curl -s "$BASE_URL/")
if echo "$RESPONSE" | grep -q "Method: GET"; then
echo " PASS: GET request works"
else
echo " FAIL: GET request failed"
echo " Response: $RESPONSE"
exit 1
fi
# Test 2: GET with query string
echo "Test 2: GET with query string"
RESPONSE=$(curl -s "$BASE_URL/search?q=test&page=1")
if echo "$RESPONSE" | grep -q "Query: q=test&page=1"; then
echo " PASS: Query string works"
else
echo " FAIL: Query string failed"
echo " Response: $RESPONSE"
exit 1
fi
# Test 3: POST with body
echo "Test 3: POST with body"
RESPONSE=$(curl -s -X POST -d "hello=world" "$BASE_URL/submit")
if echo "$RESPONSE" | grep -q "Method: POST" && echo "$RESPONSE" | grep -q "Body: hello=world"; then
echo " PASS: POST with body works"
else
echo " FAIL: POST with body failed"
echo " Response: $RESPONSE"
exit 1
fi
# Test 4: Path handling
echo "Test 4: Path handling"
RESPONSE=$(curl -s "$BASE_URL/api/v1/users")
if echo "$RESPONSE" | grep -q "Path: /api/v1/users"; then
echo " PASS: Path handling works"
else
echo " FAIL: Path handling failed"
echo " Response: $RESPONSE"
exit 1
fi
# Test 5: Multiple requests (keepalive)
echo "Test 5: Multiple requests (keepalive)"
for i in 1 2 3; do
RESPONSE=$(curl -s "$BASE_URL/request/$i")
if ! echo "$RESPONSE" | grep -q "Path: /request/$i"; then
echo " FAIL: Request $i failed"
exit 1
fi
done
echo " PASS: Multiple requests work"
# Test 6: Large POST body
echo "Test 6: Large POST body"
LARGE_BODY=$(python3 -c "print('x' * 10000)")
RESPONSE=$(curl -s -X POST -d "$LARGE_BODY" "$BASE_URL/large")
if echo "$RESPONSE" | grep -q "Method: POST" && echo "$RESPONSE" | grep -c "x" | grep -q "10000"; then
echo " PASS: Large POST body works"
else
# Verify body length in response
BODY_LINE=$(echo "$RESPONSE" | grep "Body:")
BODY_LEN=${#BODY_LINE}
if [ "$BODY_LEN" -gt 10000 ]; then
echo " PASS: Large POST body works"
else
echo " FAIL: Large POST body failed"
echo " Response length: $BODY_LEN"
exit 1
fi
fi
# Test 7: HTTP headers
echo "Test 7: Custom headers"
RESPONSE=$(curl -s -H "X-Custom-Header: test-value" "$BASE_URL/headers")
if echo "$RESPONSE" | grep -q "Method: GET"; then
echo " PASS: Custom headers work"
else
echo " FAIL: Custom headers failed"
echo " Response: $RESPONSE"
exit 1
fi
echo ""
echo "=== All tests passed! ==="