From 65ba40b243886ee969d4458dbeab4240547487fd Mon Sep 17 00:00:00 2001 From: Benoit Chesneau Date: Fri, 3 Apr 2026 21:49:47 +0200 Subject: [PATCH] Update Docker setup to install gunicorn from local source This allows testing local changes to gunicorn in the E2E test suite. Previously containers were installing from GitHub master branch. Also updates compatibility grid with latest test results (417/444, 93%). --- gunicorn/asgi/protocol.py | 8 ++++++- .../asgi_framework_compat/docker-compose.yml | 24 +++++++++---------- .../frameworks/blacksheep_app/Dockerfile | 15 ++++++++---- .../blacksheep_app/requirements.txt | 2 +- .../frameworks/django_app/Dockerfile | 20 ++++++++++++---- .../frameworks/django_app/requirements.txt | 2 +- .../frameworks/fastapi_app/Dockerfile | 15 ++++++++---- .../frameworks/fastapi_app/requirements.txt | 2 +- .../frameworks/litestar_app/Dockerfile | 15 ++++++++---- .../frameworks/litestar_app/requirements.txt | 2 +- .../frameworks/quart_app/Dockerfile | 15 ++++++++---- .../frameworks/quart_app/requirements.txt | 2 +- .../frameworks/starlette_app/Dockerfile | 15 ++++++++---- .../frameworks/starlette_app/requirements.txt | 2 +- .../results/compatibility_grid.json | 14 +++++------ .../results/compatibility_grid.md | 8 +++---- 16 files changed, 107 insertions(+), 54 deletions(-) diff --git a/gunicorn/asgi/protocol.py b/gunicorn/asgi/protocol.py index 8bdafc70..365ed59a 100644 --- a/gunicorn/asgi/protocol.py +++ b/gunicorn/asgi/protocol.py @@ -922,7 +922,13 @@ class ASGIProtocol(asyncio.Protocol): # Skip for 1xx informational responses (RFC 9110) # Skip if Transfer-Encoding already set by framework is_informational = 100 <= response_status < 200 - if not has_content_length and not has_transfer_encoding and request.version >= (1, 1) and not is_informational: + needs_chunked = ( + not has_content_length + and not has_transfer_encoding + and request.version >= (1, 1) + and not is_informational + ) + if needs_chunked: use_chunked = True response_headers = list(response_headers) + [(b"transfer-encoding", b"chunked")] diff --git a/tests/docker/asgi_framework_compat/docker-compose.yml b/tests/docker/asgi_framework_compat/docker-compose.yml index 92653f2b..cf70f530 100644 --- a/tests/docker/asgi_framework_compat/docker-compose.yml +++ b/tests/docker/asgi_framework_compat/docker-compose.yml @@ -17,8 +17,8 @@ x-healthcheck: &healthcheck services: django: build: - context: ./frameworks/django_app - dockerfile: Dockerfile + context: ../../.. + dockerfile: tests/docker/asgi_framework_compat/frameworks/django_app/Dockerfile ports: - "8001:8000" command: ["gunicorn", "asgi:application", "-k", "asgi", "-b", "0.0.0.0:8000", "--workers", "1", "--worker-connections", "100", "--asgi-loop", "${ASGI_LOOP:-auto}"] @@ -28,8 +28,8 @@ services: fastapi: build: - context: ./frameworks/fastapi_app - dockerfile: Dockerfile + context: ../../.. + dockerfile: tests/docker/asgi_framework_compat/frameworks/fastapi_app/Dockerfile ports: - "8002:8000" command: ["gunicorn", "app:app", "-k", "asgi", "-b", "0.0.0.0:8000", "--workers", "1", "--worker-connections", "100", "--asgi-loop", "${ASGI_LOOP:-auto}"] @@ -39,8 +39,8 @@ services: starlette: build: - context: ./frameworks/starlette_app - dockerfile: Dockerfile + context: ../../.. + dockerfile: tests/docker/asgi_framework_compat/frameworks/starlette_app/Dockerfile ports: - "8003:8000" command: ["gunicorn", "app:app", "-k", "asgi", "-b", "0.0.0.0:8000", "--workers", "1", "--worker-connections", "100", "--asgi-loop", "${ASGI_LOOP:-auto}"] @@ -50,8 +50,8 @@ services: quart: build: - context: ./frameworks/quart_app - dockerfile: Dockerfile + context: ../../.. + dockerfile: tests/docker/asgi_framework_compat/frameworks/quart_app/Dockerfile ports: - "8004:8000" command: ["gunicorn", "app:app", "-k", "asgi", "-b", "0.0.0.0:8000", "--workers", "1", "--worker-connections", "100", "--asgi-loop", "${ASGI_LOOP:-auto}"] @@ -61,8 +61,8 @@ services: litestar: build: - context: ./frameworks/litestar_app - dockerfile: Dockerfile + context: ../../.. + dockerfile: tests/docker/asgi_framework_compat/frameworks/litestar_app/Dockerfile ports: - "8005:8000" command: ["gunicorn", "app:app", "-k", "asgi", "-b", "0.0.0.0:8000", "--workers", "1", "--worker-connections", "100", "--asgi-loop", "${ASGI_LOOP:-auto}"] @@ -72,8 +72,8 @@ services: blacksheep: build: - context: ./frameworks/blacksheep_app - dockerfile: Dockerfile + context: ../../.. + dockerfile: tests/docker/asgi_framework_compat/frameworks/blacksheep_app/Dockerfile ports: - "8006:8000" command: ["gunicorn", "app:app", "-k", "asgi", "-b", "0.0.0.0:8000", "--workers", "1", "--worker-connections", "100", "--asgi-loop", "${ASGI_LOOP:-auto}"] diff --git a/tests/docker/asgi_framework_compat/frameworks/blacksheep_app/Dockerfile b/tests/docker/asgi_framework_compat/frameworks/blacksheep_app/Dockerfile index a5e7a1f0..73265ba8 100644 --- a/tests/docker/asgi_framework_compat/frameworks/blacksheep_app/Dockerfile +++ b/tests/docker/asgi_framework_compat/frameworks/blacksheep_app/Dockerfile @@ -2,13 +2,20 @@ FROM python:3.12-slim WORKDIR /app -# Install curl for healthcheck and git for pip install from git -RUN apt-get update && apt-get install -y curl git && rm -rf /var/lib/apt/lists/* +# Install curl for healthcheck +RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* -COPY requirements.txt . +# Copy gunicorn source and install from local +COPY gunicorn /gunicorn-src/gunicorn +COPY pyproject.toml /gunicorn-src/ +COPY README.md /gunicorn-src/ +RUN pip install --no-cache-dir /gunicorn-src + +# Install other requirements +COPY tests/docker/asgi_framework_compat/frameworks/blacksheep_app/requirements.txt . RUN pip install --no-cache-dir -r requirements.txt -COPY app.py . +COPY tests/docker/asgi_framework_compat/frameworks/blacksheep_app/app.py . EXPOSE 8000 diff --git a/tests/docker/asgi_framework_compat/frameworks/blacksheep_app/requirements.txt b/tests/docker/asgi_framework_compat/frameworks/blacksheep_app/requirements.txt index 5561732a..d974c849 100644 --- a/tests/docker/asgi_framework_compat/frameworks/blacksheep_app/requirements.txt +++ b/tests/docker/asgi_framework_compat/frameworks/blacksheep_app/requirements.txt @@ -1,4 +1,4 @@ -gunicorn @ git+https://github.com/benoitc/gunicorn.git@master +# gunicorn is installed from local source in Dockerfile blacksheep>=2.0.0 uvloop>=0.19.0 websockets>=12.0 diff --git a/tests/docker/asgi_framework_compat/frameworks/django_app/Dockerfile b/tests/docker/asgi_framework_compat/frameworks/django_app/Dockerfile index fd603b79..3c767b01 100644 --- a/tests/docker/asgi_framework_compat/frameworks/django_app/Dockerfile +++ b/tests/docker/asgi_framework_compat/frameworks/django_app/Dockerfile @@ -2,13 +2,25 @@ FROM python:3.12-slim WORKDIR /app -# Install curl for healthcheck and git for pip install from git -RUN apt-get update && apt-get install -y curl git && rm -rf /var/lib/apt/lists/* +# Install curl for healthcheck +RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* -COPY requirements.txt . +# Copy gunicorn source and install from local +COPY gunicorn /gunicorn-src/gunicorn +COPY pyproject.toml /gunicorn-src/ +COPY README.md /gunicorn-src/ +RUN pip install --no-cache-dir /gunicorn-src + +# Install other requirements +COPY tests/docker/asgi_framework_compat/frameworks/django_app/requirements.txt . RUN pip install --no-cache-dir -r requirements.txt -COPY . . +COPY tests/docker/asgi_framework_compat/frameworks/django_app/asgi.py . +COPY tests/docker/asgi_framework_compat/frameworks/django_app/settings.py . +COPY tests/docker/asgi_framework_compat/frameworks/django_app/urls.py . +COPY tests/docker/asgi_framework_compat/frameworks/django_app/views.py . +COPY tests/docker/asgi_framework_compat/frameworks/django_app/consumers.py . +COPY tests/docker/asgi_framework_compat/frameworks/django_app/routing.py . ENV DJANGO_SETTINGS_MODULE=settings ENV PYTHONPATH=/app diff --git a/tests/docker/asgi_framework_compat/frameworks/django_app/requirements.txt b/tests/docker/asgi_framework_compat/frameworks/django_app/requirements.txt index 7c75f91b..226191c6 100644 --- a/tests/docker/asgi_framework_compat/frameworks/django_app/requirements.txt +++ b/tests/docker/asgi_framework_compat/frameworks/django_app/requirements.txt @@ -1,4 +1,4 @@ -gunicorn @ git+https://github.com/benoitc/gunicorn.git@master +# gunicorn is installed from local source in Dockerfile Django>=5.0 channels>=4.0.0 uvloop>=0.19.0 diff --git a/tests/docker/asgi_framework_compat/frameworks/fastapi_app/Dockerfile b/tests/docker/asgi_framework_compat/frameworks/fastapi_app/Dockerfile index a5e7a1f0..42b80cd2 100644 --- a/tests/docker/asgi_framework_compat/frameworks/fastapi_app/Dockerfile +++ b/tests/docker/asgi_framework_compat/frameworks/fastapi_app/Dockerfile @@ -2,13 +2,20 @@ FROM python:3.12-slim WORKDIR /app -# Install curl for healthcheck and git for pip install from git -RUN apt-get update && apt-get install -y curl git && rm -rf /var/lib/apt/lists/* +# Install curl for healthcheck +RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* -COPY requirements.txt . +# Copy gunicorn source and install from local +COPY gunicorn /gunicorn-src/gunicorn +COPY pyproject.toml /gunicorn-src/ +COPY README.md /gunicorn-src/ +RUN pip install --no-cache-dir /gunicorn-src + +# Install other requirements +COPY tests/docker/asgi_framework_compat/frameworks/fastapi_app/requirements.txt . RUN pip install --no-cache-dir -r requirements.txt -COPY app.py . +COPY tests/docker/asgi_framework_compat/frameworks/fastapi_app/app.py . EXPOSE 8000 diff --git a/tests/docker/asgi_framework_compat/frameworks/fastapi_app/requirements.txt b/tests/docker/asgi_framework_compat/frameworks/fastapi_app/requirements.txt index 7f526a7f..c1bc195c 100644 --- a/tests/docker/asgi_framework_compat/frameworks/fastapi_app/requirements.txt +++ b/tests/docker/asgi_framework_compat/frameworks/fastapi_app/requirements.txt @@ -1,4 +1,4 @@ -gunicorn @ git+https://github.com/benoitc/gunicorn.git@master +# gunicorn is installed from local source in Dockerfile fastapi>=0.110.0 uvloop>=0.19.0 websockets>=12.0 diff --git a/tests/docker/asgi_framework_compat/frameworks/litestar_app/Dockerfile b/tests/docker/asgi_framework_compat/frameworks/litestar_app/Dockerfile index a5e7a1f0..2b512598 100644 --- a/tests/docker/asgi_framework_compat/frameworks/litestar_app/Dockerfile +++ b/tests/docker/asgi_framework_compat/frameworks/litestar_app/Dockerfile @@ -2,13 +2,20 @@ FROM python:3.12-slim WORKDIR /app -# Install curl for healthcheck and git for pip install from git -RUN apt-get update && apt-get install -y curl git && rm -rf /var/lib/apt/lists/* +# Install curl for healthcheck +RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* -COPY requirements.txt . +# Copy gunicorn source and install from local +COPY gunicorn /gunicorn-src/gunicorn +COPY pyproject.toml /gunicorn-src/ +COPY README.md /gunicorn-src/ +RUN pip install --no-cache-dir /gunicorn-src + +# Install other requirements +COPY tests/docker/asgi_framework_compat/frameworks/litestar_app/requirements.txt . RUN pip install --no-cache-dir -r requirements.txt -COPY app.py . +COPY tests/docker/asgi_framework_compat/frameworks/litestar_app/app.py . EXPOSE 8000 diff --git a/tests/docker/asgi_framework_compat/frameworks/litestar_app/requirements.txt b/tests/docker/asgi_framework_compat/frameworks/litestar_app/requirements.txt index f07b970a..4c027cab 100644 --- a/tests/docker/asgi_framework_compat/frameworks/litestar_app/requirements.txt +++ b/tests/docker/asgi_framework_compat/frameworks/litestar_app/requirements.txt @@ -1,4 +1,4 @@ -gunicorn @ git+https://github.com/benoitc/gunicorn.git@master +# gunicorn is installed from local source in Dockerfile litestar>=2.7.0 uvloop>=0.19.0 websockets>=12.0 diff --git a/tests/docker/asgi_framework_compat/frameworks/quart_app/Dockerfile b/tests/docker/asgi_framework_compat/frameworks/quart_app/Dockerfile index a5e7a1f0..07d6dcf5 100644 --- a/tests/docker/asgi_framework_compat/frameworks/quart_app/Dockerfile +++ b/tests/docker/asgi_framework_compat/frameworks/quart_app/Dockerfile @@ -2,13 +2,20 @@ FROM python:3.12-slim WORKDIR /app -# Install curl for healthcheck and git for pip install from git -RUN apt-get update && apt-get install -y curl git && rm -rf /var/lib/apt/lists/* +# Install curl for healthcheck +RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* -COPY requirements.txt . +# Copy gunicorn source and install from local +COPY gunicorn /gunicorn-src/gunicorn +COPY pyproject.toml /gunicorn-src/ +COPY README.md /gunicorn-src/ +RUN pip install --no-cache-dir /gunicorn-src + +# Install other requirements +COPY tests/docker/asgi_framework_compat/frameworks/quart_app/requirements.txt . RUN pip install --no-cache-dir -r requirements.txt -COPY app.py . +COPY tests/docker/asgi_framework_compat/frameworks/quart_app/app.py . EXPOSE 8000 diff --git a/tests/docker/asgi_framework_compat/frameworks/quart_app/requirements.txt b/tests/docker/asgi_framework_compat/frameworks/quart_app/requirements.txt index 1f50a34f..28385937 100644 --- a/tests/docker/asgi_framework_compat/frameworks/quart_app/requirements.txt +++ b/tests/docker/asgi_framework_compat/frameworks/quart_app/requirements.txt @@ -1,4 +1,4 @@ -gunicorn @ git+https://github.com/benoitc/gunicorn.git@master +# gunicorn is installed from local source in Dockerfile quart>=0.19.0 uvloop>=0.19.0 websockets>=12.0 diff --git a/tests/docker/asgi_framework_compat/frameworks/starlette_app/Dockerfile b/tests/docker/asgi_framework_compat/frameworks/starlette_app/Dockerfile index a5e7a1f0..16ce4d9f 100644 --- a/tests/docker/asgi_framework_compat/frameworks/starlette_app/Dockerfile +++ b/tests/docker/asgi_framework_compat/frameworks/starlette_app/Dockerfile @@ -2,13 +2,20 @@ FROM python:3.12-slim WORKDIR /app -# Install curl for healthcheck and git for pip install from git -RUN apt-get update && apt-get install -y curl git && rm -rf /var/lib/apt/lists/* +# Install curl for healthcheck +RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* -COPY requirements.txt . +# Copy gunicorn source and install from local +COPY gunicorn /gunicorn-src/gunicorn +COPY pyproject.toml /gunicorn-src/ +COPY README.md /gunicorn-src/ +RUN pip install --no-cache-dir /gunicorn-src + +# Install other requirements +COPY tests/docker/asgi_framework_compat/frameworks/starlette_app/requirements.txt . RUN pip install --no-cache-dir -r requirements.txt -COPY app.py . +COPY tests/docker/asgi_framework_compat/frameworks/starlette_app/app.py . EXPOSE 8000 diff --git a/tests/docker/asgi_framework_compat/frameworks/starlette_app/requirements.txt b/tests/docker/asgi_framework_compat/frameworks/starlette_app/requirements.txt index 6bf1eecc..a4db846d 100644 --- a/tests/docker/asgi_framework_compat/frameworks/starlette_app/requirements.txt +++ b/tests/docker/asgi_framework_compat/frameworks/starlette_app/requirements.txt @@ -1,4 +1,4 @@ -gunicorn @ git+https://github.com/benoitc/gunicorn.git@master +# gunicorn is installed from local source in Dockerfile starlette>=0.37.0 uvloop>=0.19.0 websockets>=12.0 diff --git a/tests/docker/asgi_framework_compat/results/compatibility_grid.json b/tests/docker/asgi_framework_compat/results/compatibility_grid.json index 796c50c0..d90f3b26 100644 --- a/tests/docker/asgi_framework_compat/results/compatibility_grid.json +++ b/tests/docker/asgi_framework_compat/results/compatibility_grid.json @@ -1,5 +1,5 @@ { - "generated": "2026-04-03T11:06:45.300191", + "generated": "2026-04-03T21:49:07.476946", "worker": "gunicorn.workers.gasgi.ASGIWorker", "frameworks": { "django": { @@ -16,8 +16,8 @@ "total": 19 }, "websocket": { - "passed": 13, - "failed": 6, + "passed": 19, + "failed": 0, "total": 19 }, "lifespan": { @@ -31,7 +31,7 @@ "total": 9 } }, - "total_passed": 66, + "total_passed": 72, "total_tests": 74 }, "fastapi": { @@ -186,12 +186,12 @@ "total": 8 }, "streaming": { - "passed": 1, - "failed": 8, + "passed": 9, + "failed": 0, "total": 9 } }, - "total_passed": 65, + "total_passed": 73, "total_tests": 74 } } diff --git a/tests/docker/asgi_framework_compat/results/compatibility_grid.md b/tests/docker/asgi_framework_compat/results/compatibility_grid.md index 31bf8860..50c3778f 100644 --- a/tests/docker/asgi_framework_compat/results/compatibility_grid.md +++ b/tests/docker/asgi_framework_compat/results/compatibility_grid.md @@ -1,6 +1,6 @@ # ASGI Framework Compatibility Grid -**Generated:** 2026-04-03 11:06:45 +**Generated:** 2026-04-03 21:49:07 **Worker:** gunicorn ASGI worker (`-k asgi`) **Event Loop:** auto (uvloop if available) @@ -8,13 +8,13 @@ | Framework | HTTP Scope | HTTP Messages | WebSocket | Lifespan | Streaming | Total | |-----------|---------|---------|---------|---------|---------|-------| -| Django + Channels | 19/19 | **18/19** | **13/19** | **7/8** | 9/9 | **66/74** | +| Django + Channels | 19/19 | **18/19** | 19/19 | **7/8** | 9/9 | **72/74** | | FastAPI | 19/19 | **18/19** | 19/19 | 8/8 | 9/9 | **73/74** | | Starlette | 19/19 | **18/19** | 19/19 | 8/8 | 9/9 | **73/74** | | Quart | **18/19** | **17/19** | **11/19** | 8/8 | 9/9 | **63/74** | | Litestar | **18/19** | **11/19** | **17/19** | 8/8 | 9/9 | **63/74** | -| BlackSheep | 19/19 | **18/19** | 19/19 | 8/8 | **1/9** | **65/74** | +| BlackSheep | 19/19 | **18/19** | 19/19 | 8/8 | 9/9 | **73/74** | *Bold indicates failures* -**Overall:** 403/444 tests passed (90%) +**Overall:** 417/444 tests passed (93%)