1877 Commits

Author SHA1 Message Date
Benoit Chesneau
b650332c70
Arbiter signal handling improvements (#3441)
* tests: Add tests for current signal handling behavior

Add tests for arbiter signal handling:
- TestSignalHandlerRegistration (4 tests): Verify signal handler
  registration, pipe creation, SIGCHLD separate handler, and
  expected signals list
- TestSignalQueue (4 tests): Test signal queueing, max queue size,
  wakeup writes to pipe, and sleep returns on pipe data
- TestReapWorkers (6 tests): Test worker reaping for normal exit,
  error exit codes, WORKER_BOOT_ERROR, APP_LOAD_ERROR, signal
  termination, and SIGKILL OOM hint

These tests establish baseline coverage before refactoring the
signal handling code for safety and reliability improvements.

* tests: Add tests for SIGHUP reload and worker lifecycle

Add tests for reload and worker management:
- TestSighupReload (3 tests): Verify reload spawns configured number
  of workers, calls manage_workers, and logs hang up message
- TestWorkerLifecycle (4 tests): Test spawn_worker adds to WORKERS
  dict, kill_worker sends correct signal, murder_workers sends
  SIGABRT first then SIGKILL on subsequent timeout

* arbiter: Fix waitpid status parsing using POSIX macros

Use os.WIFEXITED/WEXITSTATUS and os.WIFSIGNALED/WTERMSIG instead
of manual bit shifting for waitpid status interpretation. This
correctly distinguishes between normal exits and signal termination.

The previous code used 'status >> 8' which only worked for normal
exits, and used raw status values for signal detection which was
incorrect.

Fixes part of #3435 and #3056 (signal name display issues)

* arbiter: Change SIGTERM log level to warning

Log signal termination at warning level for expected signals
(SIGTERM, SIGQUIT) since these typically occur during normal
graceful shutdown. SIGKILL remains at error level with the
OOM hint since it indicates abnormal termination.

Fixes #3311, #3050 (SIGTERM logged as error)

* arbiter: Remove logging from SIGCHLD signal handler

Move reap_workers() call from signal handler context to main loop.
The signal handler (now signal_chld) only queues the signal and
wakes up the main loop. The actual reap_workers() is called from
handle_chld() in the main loop where logging is safe.

This fixes potential deadlocks caused by logging from signal
handler context when holding the logging lock.

Fixes #3198, #3004 (logging in signal handlers unsafe, deadlock)

* arbiter: Replace PIPE+select with queue.SimpleQueue

Use queue.SimpleQueue for signal handling instead of PIPE+select.
SimpleQueue is reentrant-safe and can be used from signal handlers.

Changes:
- Remove PIPE-based wakeup mechanism
- Add SIG_QUEUE as SimpleQueue instance
- Add WAKEUP_REQUEST sentinel for non-signal wakeups
- Replace sleep() with wait_for_signals() using queue.get()
- Simplify signal handler to just put_nowait()
- Update main loop to iterate over wait_for_signals()
- Add reap_workers() call in stop() to properly clean up workers
  since SIGCHLD is no longer processed during shutdown

This simplifies the code and removes the dependency on select().

Also adds integration tests for signal handling that verify:
- Basic request/response
- Graceful shutdown with SIGTERM/SIGINT
- SIGHUP reload
- Multiple concurrent requests

* arbiter: Wait for old workers on SIGHUP reload

After spawning new workers during reload, wait for old workers to
terminate before returning from reload(). This prevents the issue
where old workers could receive double SIGTERM - once from
manage_workers() and again from the arbiter loop.

The reload now tracks worker_age before spawning, then waits up to
graceful_timeout for workers older than that age to exit.

Fixes #3312, #3274 (SIGHUP can send double SIGTERM)

* arbiter: Log SIGCHLD at debug level

SIGCHLD is received frequently (whenever a worker exits) and doesn't
need to be logged at info level. Log it at debug level to reduce
noise in the logs while still making it available for debugging.

* tests: Fix lint warnings in test_arbiter.py
2026-01-22 11:56:23 +01:00
Benoit Chesneau
0186211400 gthread: Lock-free PollableMethodQueue refactoring
Replace RLock-based synchronization with a pipe-based method queue
for lock-free coordination between worker threads and main thread.

Key changes:
- Add PollableMethodQueue class using os.pipe() for wake-up signaling
- Non-blocking pipe (both ends) for BSD compatibility (FreeBSD, OpenBSD)
- Unified event loop using single poller.select() - no more futures.wait()
- Better graceful shutdown with connection draining within grace period
- Rename _keep to keepalived_conns, remove _lock entirely
- Add handle_exit() for SIGTERM, improve handle_quit() for SIGQUIT
- Add set_accept_enabled() for dynamic connection acceptance control
- Add wait_for_and_dispatch_events() with EINTR handling

Performance improvement: ~8% at high concurrency due to reduced
lock contention and non-blocking pipe operations.

Tests: 40 tests covering PollableMethodQueue, graceful shutdown,
keepalive management, error handling, and BSD compatibility.

Fixes #3146
Closes #3157
2026-01-22 09:32:48 +01:00
Benoit Chesneau
b43dc6d398 gthread: Improve reliability and fix edge cases
This commit addresses three issues with the gthread worker:

1. Request body handling on keepalive
   - Add finish_body() method to Parser to discard unread body bytes
   - Call it before returning connections to the poller
   - Prevents socket appearing readable due to leftover body
   Fixes #3301

2. Timeout reliability with monotonic clock
   - Replace time.time() with time.monotonic() in set_timeout()
   - Replace time.time() with time.monotonic() in murder_keepalived()
   - Prevents timeout issues caused by NTP adjustments

3. SSL error handling
   - Move conn.init() from enqueue_req() to handle()
   - SSL handshake now runs in worker thread, not main thread
   - ENOTCONN errors during ssl_wrap_socket are caught per-connection
   - Prevents entire worker crashes on SSL handshake failures

Also adds comprehensive unit tests for the gthread worker.

Closes #3303
Closes #3308
2026-01-22 09:14:19 +01:00
Benoit Chesneau
eb2f81dcf8
Merge pull request #3390 from adk-swisstopo/timeout-units
Specify the units for `graceful_timeout`.
2025-10-05 18:21:01 +02:00
Adrien Kunysz
dbc00fca76 Specify the units for graceful_timeout. 2025-04-16 15:58:49 +02:00
Adrien Kunysz
68f3545e5b
Fix typo in comment. 2025-04-08 15:17:38 +02:00
Mond WAN
d05b6c5425 Fix typo in the getgrnam
getgrnam is under grp module instead of pwd
2025-03-14 18:01:52 +00:00
Benoit Chesneau
bacbf8aa51
Merge pull request #2938 from Affirm/reuse-port-fix
Fix reuse-port to balance requests across Gunicorn workers
2024-10-25 11:36:15 +02:00
benoitc
7268a61099 fix util.set_owner_process
Python 2.x is not supported anymore, let's remove this extra feature.

fix #3212
2024-08-11 09:52:51 +02:00
Benoit Chesneau
334392e779
Merge pull request #2559 from laggardkernel/bugfix/reexec-env
Typo fix for reexec related environ variables
2024-08-10 11:07:03 +02:00
Benoit Chesneau
e75c3533e3
Merge pull request #3189 from pajod/patch-py36
chore: eat Python 2 leftovers
2024-08-10 10:40:40 +02:00
benoitc
9357b28dd8 keep document user in access_log_format setting 2024-08-10 10:19:36 +02:00
benoitc
79fdef0822 bump to 23.0.0 2024-08-10 10:06:36 +02:00
Benoit Chesneau
3f56d76548
Merge pull request #3192 from pajod/patch-allowed-script-name
22.0.0 regression: We need a better default treatment of SCRIPT_NAME
2024-08-09 09:05:57 +02:00
Paul J. Dorn
687b78d20c config defaults: PATH_NAME and ::1 for proxy
* PATH_NAME is used like SCRIPT_NAME: include both
* replicate changed forwarded-allow-ips default to proxy_allow_ips
2024-08-08 18:15:42 +02:00
Paul J. Dorn
5bbf373e18 undocumented allow-all for forwarder-headers 2024-08-07 20:15:17 +02:00
Paul J. Dorn
3e042e8269 Configurable list of forwarder headers 2024-08-07 20:15:13 +02:00
Paul J. Dorn
01bcdb1d12 Exempt SCRIPT_NAME from newly introduced --header-map treatment 2024-08-07 20:10:47 +02:00
Paul J. Dorn
2bc931e7d9 whitespace handling in header field values
Strip whitespace also *after* header field value.
Simply refuse obsolete header folding (a default-off
option to revert is temporarily provided).
While we are at it, explicitly handle recently
introduced http error classes with intended status code.
2024-08-07 19:42:16 +02:00
Benoit Chesneau
77b65a0934
Merge pull request #3191 from pajod/patch-severity
logging: swap error/debug severity on socket creation failure
2024-08-07 19:25:31 +02:00
Paul J. Dorn
ce039ca71a Swap error/debug severity
debug message: will now sleep(1)
error message: this is what went wrong
2024-08-07 19:02:37 +02:00
Paul J. Dorn
6c3296e177 update docs
re-apply typo fix from 628a0bcb61ef3a211d67dfd68ad1ba161cccb3b8
reflect removal of setting from 555d2fa27f2d891f23bd03890e4a826b5018c6b4
2024-08-07 18:21:36 +02:00
Paul J. Dorn
6245e2c03b docs: unresolved reference (the reference is not named after the class name) 2024-08-07 18:16:20 +02:00
Benoit Chesneau
ad7c1de132
Merge pull request #3080 from odyfatouros/Fix-#3079-worker_class-parameter-accepts-class
Fix for issue #3079, worker_class parameter accepts a class
2024-08-07 08:47:20 +02:00
Benoit Chesneau
52234e7b32
Merge pull request #2688 from StarfishStorage/fix_2684
Fix problem that may happen after interrupted chunk-encoding request
2024-08-07 00:24:46 +02:00
benoitc
555d2fa27f don't tolerate wrong te headers
changes:

- Just follow the new TE specification (https://datatracker.ietf.org/doc/html/rfc9112#name-transfer-encoding)
 here and accept to introduce a breaking change.
- gandle multiple TE on one line

** breaking changes ** : invalid  headers and position will now return
an error.
2024-08-06 23:47:01 +02:00
Benoit Chesneau
9a96e75808
Merge pull request #3253 from pajod/patch-rfc9110-section5.5
Refuse requests with invalid and dangerous CR/LF/NUL in header field value, as demanded by rfc9110 section 5.5
2024-08-06 22:25:12 +02:00
Benoit Chesneau
5c0e1571dc
Merge pull request #3255 from pajod/patch-refuse-empty-request-target
refuse empty request-target in HTTP request
2024-08-06 18:26:47 +02:00
Benoit Chesneau
26c22af2e7
Merge pull request #2336 from elendiastarman/gevent-statsd-fix
Fixed two bugs related to gevent + gunicorn + statsd.
2024-08-06 18:08:18 +02:00
Paul J. Dorn
a3d130ae51 gracefully handle chunked encoding missing size
Treat it the same as invalid characters where size should be.
2024-07-31 18:32:02 +02:00
Paul J. Dorn
9ca4f1fdfc refuse empty request-target in HTTP request
A single slash is valid, but nothing at all can be safely refused.

Python stdlib explicitly tells us it will not perform validation.
https://docs.python.org/3/library/urllib.parse.html#url-parsing-security
There are *four* `request-target` forms in rfc9112, none of them can be empty.
2024-07-31 02:14:35 +02:00
Paul J. Dorn
eda9d456d3 forbid lone CR/LF and NUL in headers
New parser rule: refuse HTTP requests where a header field value
contains characters that
a) should never appear there in the first place,
b) might have lead to incorrect treatment in a proxy in front, and
c) might lead to unintended behaviour in applications.

From RFC 9110 section 5.5:
"Field values containing CR, LF, or NUL characters are invalid and
dangerous, due to the varying ways that implementations might parse
and interpret those characters; a recipient of CR, LF, or NUL within
a field value MUST either reject the message or replace each of those
characters with SP before further processing or forwarding of that
message."
2024-07-31 01:28:30 +02:00
Vaclav Rehak
97f87ec13e Fix InvalidHTTPVersion exception str method
Fixes: #3195
2024-04-26 13:58:10 +02:00
Paul J. Dorn
4f7766585d Python 3.3+: replace deprecated aliases
since 3.3: EnvironmentError, IOError, socket.error and select.error are merged into IOError.
They may now return a more specific subclass - which this commit does not utilize yet.
2024-04-22 03:33:31 +02:00
Paul J. Dorn
ec85b32d3b Pythn 2.7+: open arg defaults to r 2024-04-22 03:33:31 +02:00
Paul J. Dorn
78ef73b63e Python3: super() args not typically needed 2024-04-22 03:33:31 +02:00
Paul J. Dorn
422b18acea class Name(object): -> class Name: 2024-04-22 03:33:30 +02:00
Paul J. Dorn
4323027b1e drop long-default - coding: utf-8 2024-04-22 03:33:14 +02:00
benoitc
f63d59e4d7 bump to 22.0 2024-04-17 00:44:14 +02:00
Eisuke Kawashima
628a0bcb61
chore: fix typos 2024-03-25 08:31:59 +09:00
Randall Leeds
660fd8d850 Fix references to non-existent 20.2 version in configuration settings
Close #3043.
2023-12-28 19:57:14 -08:00
Randall Leeds
f9e61b11c7
Merge pull request #3108 from pajod/patch-githubactions
restore, and from now on CI-test for entry point
2023-12-28 18:54:52 -08:00
Randall Leeds
fd809184c3
Merge branch 'master' into master 2023-12-27 16:16:21 -08:00
Paul J. Dorn
e710393d14 HTTP parser: stricter chunk-ext OBS handling
chunk extensions are silently ignored before and after this change;
its just the whitespace handling for the case without extensions that matters
applying same strip(WS)->rstrip(BWS) replacement as already done in related cases

half-way fix: could probably reject all BWS cases, rejecting only misplaced ones
2023-12-17 17:46:56 +01:00
Paul J. Dorn
7ebe442d08 strict HTTP version validation
Note: This is unrelated to a reverse proxy potentially talking HTTP/3 to clients.
This is about the HTTP protocol version spoken to Gunicorn, which is HTTP/1.0 or HTTP/1.1.

Little legitimate need for processing HTTP 1 requests with ambiguous version numbers.
Broadly refuse.

Co-authored-by: Ben Kallus <benjamin.p.kallus.gr@dartmouth.edu>
2023-12-15 13:33:31 +01:00
Paul J. Dorn
f5501111a2 strict HTTP header field name validation
Do the validation on the original, not the result from unicode case folding.

Background:
latin-1 0xDF is traditionally uppercased 0x53+0x53 which puts it back in ASCII
2023-12-15 13:33:31 +01:00
Paul J. Dorn
fd67112f40 Ignore secure_scheme_headers in Trailer section
In common configuration unlikely a big security problem in itself
you are just fooling the remote about https.
However, it is offers an oracle for otherwise invisible proxy request headers,
so it might help exploiting other vulnerabilities.
2023-12-15 13:33:31 +01:00
Paul J. Dorn
ac29c9b0a7 fail-safe on unsupported request framing
If we promise wsgi.input_terminated, we better get it right - or not at all.
* chunked encoding on HTTP <= 1.1
* chunked not last transfer coding
* multiple chinked codings
* any unknown codings (yes, this too! because we do not detect unusual syntax that is still chunked)
* empty coding (plausibly harmless, but not see in real life anyway - refused, for the moment)
2023-12-15 13:33:31 +01:00
Paul J. Dorn
0b10cbab1d unconditionally log request error
Somehow exception logging was conditional on successful request uri parsing.
Add it back for the other branch.
2023-12-15 13:33:31 +01:00
Paul J. Dorn
72b8970dbf silently drop or refuse header names w/ underscore
Ambiguous mappings open a bottomless pit of "what is user input and what is proxy input" confusion.
Default to what everyone else has been doing for years now, silently drop.

see also https://nginx.org/r/underscores_in_headers
2023-12-15 13:33:31 +01:00