August 5, 2026·4 min read·engineeringpythonhttpreliability

Python 3.14.7 bounds HTTP response processing

A socket timeout did not stop an endless stream of HTTP trailers or interim responses. Python 3.14.7 adds count limits and fixes other cases where processing could continue indefinitely.

A server could keep Python's HTTP client busy indefinitely while a socket timeout was configured. The Python 3.14.7 changelog describes the mechanism. A malicious or broken server could send an endless sequence of chunked-response trailer lines or interim 100 Continue responses. The client kept processing the stream instead of completing the request.

Python 3.14.7, released on August 5, limits both cases. The client reads at most one hundred trailer lines and skips at most one hundred interim responses. These are limits on protocol processing work. They address behavior that a timeout on an individual blocking operation does not necessarily bound.

Python's library coverage is a major reason I keep using it. That places practical importance on details such as whether a network operation can finish. An interface can accept a timeout and still need additional limits inside its protocol implementation. This patch gives a concrete example of the distinction.

A timeout describes a particular wait

The versioned HTTPConnection documentation says its timeout applies to blocking operations, including connection attempts. It does not describe the parameter as a total deadline for all work involved in consuming a response. The reported bug is therefore consistent with a narrower timeout doing its job while the overall request remains incomplete.

The important question is what happens between waits. If a peer keeps supplying material that the parser accepts as another item to process, the implementation can repeatedly continue. Time spent waiting for the next read and the number of accepted protocol items are separate quantities. A limit on the former cannot establish a finite value for the latter.

The new count limits constrain two specific loops in response handling. After a finite number of interim responses or trailer lines, the implementation cannot continue accepting more of that sequence. This does not make the count a universal HTTP request budget. It removes the unbounded continuation described in the security entry.

The peer need not be deliberately hostile for the distinction to matter. The changelog explicitly includes broken servers. A client library used across many applications must define behavior for malformed or excessive responses regardless of whether the remote system intended an attack. The local application still needs a failure it can observe.

Parsing work needs bounds too

Other security fixes in the same release concern the amount of work performed on input. Incremental HTML parsing had quadratic complexity for long unfinished constructs, including tags and comments. That could permit a denial of service. The patch corrects this behavior in html.parser.HTMLParser.

The shared concern is that input can keep a component occupied without producing the result its caller requested. In the HTTP case, the input supplies more protocol items. In the HTML case, a long unfinished construct causes excessive parsing work. They need different implementation fixes even though both affect the application's ability to complete useful work.

ElementTree also avoids quadratic behavior for certain XPath index predicates on documents with many siblings sharing a tag. The changelog names [1], [last()], and [last()-N]. These details matter because the problematic work is tied to a specific document shape and query, not to XML processing in general.

The library section records both exponential and quadratic cases in csv.Sniffer.sniff(). Samples containing many quote characters or quoted fields could trigger excessive work. Inferring a format is convenient for callers, but the inference algorithm still needs acceptable behavior for difficult samples. The release corrects those cases without requiring applications to implement their own CSV inference.

That is a useful reason to examine a maintenance changelog by mechanism. Several unrelated modules received corrections to how much work particular input can induce. A patch upgrade can change the behavior of an application's data ingestion path even when it adds no new public method.

Completion has more than one prerequisite

The release also fixes an asyncio subprocess wait that could hang after the process exited. A child of that process could inherit a pipe and keep it open, preventing end-of-file on the parent's stream. The corrected wait() returns when the process exits regardless of the pipes' state.

This has a different cause from the HTTP issue. Process exit and pipe closure are distinct events. Waiting for the wrong combination can leave the caller blocked after the event it actually requested has happened. The changelog makes the new contract explicit instead of treating every unfinished wait as a missing timeout.

My preference for a library is that these distinctions have implementations and documented failure behavior. Application code should not need to rediscover them while diagnosing an apparently stuck operation. Python's maintenance work matters because the standard library is where many applications delegate that responsibility.

For users of http.client, the immediate change is a finite allowance for the two response sequences identified in the fix. A configured socket timeout continues to bound its documented blocking operations. The parser now has additional count limits for cases where the peer keeps sending data and never lets response processing finish.