Skip to content

keel serve: request handler has no socket timeout; a refused POST that declares a body it never sends holds a thread forever #981

Open

Description

@eaitbrahim

Found by the PR #977 security review (round 1). Predates #977.

Problem

KeelHandler (keel/web/server.py) sets no timeout. Two consequences:

  • _refuse drains the declared body BEFORE answering, so an unauthenticated client that sends Content-Length: 100 and no body gets no response and holds one ThreadingHTTPServer thread indefinitely. Reproduced in the review: a refused halt with CL 100 and no body never answered.
  • Idle keep-alive connections block forever in readline.

On a deployment with a declared remote origin (keel serve --external-host), anyone who can reach the port can exhaust threads. The halt itself is not delayed per connection (each connection has its own thread, and an admitted halt answers before draining since #977), but enough held threads would starve every connection, the halt included.

Fix sketch

Set timeout on the handler class (e.g. 30 s), so rfile reads raise socket.timeout and the connection closes. Test: a refused POST declaring a body it never sends is closed within the timeout (patch it short in the test); a halt still answers 200 immediately.

Related, smaller

gates._perform opens with the read-write connect(), which CREATES a missing database: a halt that races a deleted keel.db would leave an empty one behind. A mode=rw URI open (as db.connect_read_only does since #976, without query_only) would refuse instead.

Activity

  1. self-assigned this
    on Oct 12, 2026
  2. eaitbrahim commented on Oct 12, 2026

    @eaitbrahim
    ContributorAuthor

    Also from the PR #977 round-2 review (pre-existing): a GET's request body is never drained on the success path, so a GET that sends a body poisons the next request on a kept-alive socket, the same shape as #955 for POSTs. Worth covering together with the socket timeout.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions