History of changes¶
H11 MyPyc 0.17.0 (2026-08-25)¶
First release of the fork. Everything below is relative to upstream h11 0.16.0; the protocol behaviour is unchanged, and the whole upstream test suite passes against both the interpreted and the compiled build.
Compiled builds¶
- The package is compiled with mypyc. On the benchmark suite this is worth about 1.9x (27,700 → 52,900 requests/sec on the reference machine).
- Both a compiled wheel and a pure-Python
py3-none-anywheel are published. Installers pick the compiled build when a wheel matches the interpreter and fall back to the pure one otherwise, so PyPy and platforms without a wheel keep working. No configuration is needed on the consuming side. - Building from an sdist produces the pure-Python build unless
H11_MYPYC=1is set, so a missing compiler never breaks an install.
Backwards-incompatible changes¶
- The import name is
h11_mypyc, noth11. The distribution deliberately does not ship anh11/directory: two distributions writing to the same directory overwrite each other silently, and uninstalling either one takes the other's files with it. Under the new name the fork and upstream h11 can be installed side by side. PRODUCT_IDis nowpython-h11-mypyc/<version>rather thanpython-h11/<version>, so the fork does not report itself as upstream inUser-Agent:andServer:headers.- The sentinels (
NEED_DATA,PAUSED,IDLE, ...) are no longer instances of themselves:type(h11_mypyc.NEED_DATA) is h11_mypyc.NEED_DATAis now false. That property came from a custom metaclass, which mypyc cannot compile.iscomparisons, dict keys andType[Sentinel]annotations are unaffected. Headersno longer inherits fromcollections.abc.Sequence; it is registered as a virtual subclass instead, soisinstance(headers, Sequence)still holds. A compiled class inheriting an ABC shares the base'sisinstancecache, which silently corruptsisinstancein both directions across the process.Eventno longer inherits fromabc.ABC, and the event classes use@dataclass(slots=True)rather than a hand-written__slots__. The events are still frozen, still without__dict__, and still work withdataclasses.fields()anddataclasses.replace().- Several annotations were widened to match what the code has always accepted at
runtime, because mypyc turns annotations into runtime checks:
Data.dataandResponse.status_codeare nowAny(to keep the documented sendfile pass-through and theLocalProtocolErrorfor a non-integer status code), and the event constructors accept any bytes-like object. validate()no longer returns the match groups; usematch_or_raise()orvalidate_and_group()for those.- Requires Python 3.10 or newer.
Performance¶
Most of the work below helps the pure-Python build too, which is about 17% faster than upstream on the same benchmark:
- Header names and values are checked with byte-class tests instead of regexes. The equivalence to the ABNF patterns is pinned by a differential test.
_obsolete_line_fold()no longer runs a regex on every header line; obs-fold continuations are detected from the first byte.- Header parsing no longer builds a dict per header line just to read two fields out of it.
- Sentinel and event classes are
@final, which lets mypyc call their methods directly rather than through a vtable.
Miscellaneous internal changes¶
- Packaging metadata moved from
setup.pytopyproject.toml;setup.pyremains only to declare the mypyc extension modules. - Added a benchmark suite (
tests/test_benchmarks.py, run withmake bench-py) covering header parsing, the state machine and whole request/response cycles.
H11 0.16.0 (2025-04-23)¶
Security fix¶
Reject certain malformed Transfer-Encoding: chunked bodies that were previously accepted. These could have enabled request-smuggling attacks when an h11-based HTTP server was placed behind a load balancer with a matching bug in its chunked handling.
Advisory with more details: https://github.com/python-hyper/h11/security/advisories/GHSA-vqfr-h8mv-ghfj
Reported by: Jeppe Bonde Weikop
H11 0.15.0 (2025-04-23)¶
Bugfixes¶
- Reject Content-Lengths >= 1 zettabyte (1 billion terabytes) early, without attempting to parse the integer (#181)
Miscellaneous internal changes¶
- Remove the
testsfolder from wheel files. This reduces the zipped file size by 20KB (about 30%). (#158)
H11 0.14.0 (2022-09-25)¶
Features¶
- Allow additional trailing whitespace in chunk headers for additional compatibility with existing servers. (#133)
- Improve the type hints for Sentinel types, which should make it easier to type hint h11 usage. (#151 & #144))
Deprecations and Removals¶
- Python 3.6 support is removed. h11 now requires Python>=3.7
including PyPy 3. Users running
pip install h11on Python 2 will automatically get the last Python 2-compatible version. (#138)
v0.13.0 (2022-01-19)¶
Features¶
- Clarify that the Headers class is a Sequence and inherit from the collections Sequence abstract base class to also indicate this (and gain the mixin methods). See also #104. (#112)
- Switch event classes to dataclasses for easier typing and slightly improved performance. (#124)
- Shorten traceback of protocol errors for easier readability (#132).
- Add typing including a PEP 561 marker for usage by type checkers (#135).
- Expand the allowed status codes to [0, 999] from [0, 600] (#134).
Backwards incompatible changes¶
- Ensure request method is a valid token (#141).
v0.12.0 (2021-01-01)¶
Features¶
- Added support for servers with broken line endings.
After this change h11 accepts both \r\n and \n as a headers
delimiter. (#7)
- Add early detection of invalid http data when request line starts
with binary (#122)
Deprecations and Removals¶
- Python 2.7 and PyPy 2 support is removed. h11 now requires
Python>=3.6 including PyPy 3. Users running
pip install h11on Python 2 will automatically get the last Python 2-compatible version. (#114)
v0.11.0 (2020-10-05)¶
New features:
- h11 now stores and makes available the raw header name as received. In addition h11 will write out header names with the same casing as passed to it. This allows compatibility with systems that expect titlecased header names. See #31.
- Multiple content length headers are now merged into a single header if all the values are equal, if any are unequal a LocalProtocol error is raised (as before). See #92.
Backwards incompatible changes:
- Headers added by h11, rather than passed to it, now have titlecased names. Whilst this should help compatibility it replaces the previous lowercased header names.
v0.10.0 (2020-08-14)¶
Other changes:
- Drop support for Python 3.4.
- Support Python 3.8.
- Make error messages returned by match failures less ambiguous (#98).
v0.9.0 (2019-05-15)¶
Bug fixes:
- Allow a broader range of characters in header values. This violates the RFC, but is apparently required for compatibility with real-world code, like Google Analytics cookies (#57, #58).
- Validate incoming and outgoing request paths for invalid characters. This prevents a variety of potential security issues that have affected other HTTP clients. (#69).
- Force status codes to be integers, thereby allowing stdlib HTTPStatus IntEnums to be used when constructing responses (#72).
Other changes:
- Make all sentinel values inspectable by IDEs, and split
SEND_BODY_DONEintoSEND_BODY, andDONE(#75). - Drop support for Python 3.3.
- LocalProtocolError raised in start_next_cycle now shows states for more informative errors (#80).
v0.8.1 (2018-04-14)¶
Bug fixes:
- Always return headers as
bytesobjects (#60)
Other changes:
- Added proper license notices to the Javascript used in our documentation (#61)
v0.8.0 (2018-03-20)¶
Backwards incompatible changes:
- h11 now performs stricter validation on outgoing header names and header values: illegal characters are now rejected (example: you can't put a newline into an HTTP header), and header values with leading/trailing whitespace are also rejected (previously h11 would silently discard the whitespace). All these checks were already performed on incoming headers; this just extends that to outgoing headers.
New features:
- New method
Connection.send_failed(), to notify aConnectionobject when data returned fromConnection.send()was not sent.
Bug fixes:
-
Make sure that when computing the framing headers for HEAD responses, we produce the same results as we would for the corresponding GET.
-
Error out if a request has multiple Host: headers.
-
Send the Host: header first, as recommended by RFC 7230.
-
The Expect: header is case-insensitive, so use case-insensitive matching when looking for 100-continue.
Other changes:
-
Better error messages in several cases.
-
Provide correct
error_status_hintin exception raised when encountering an invalidTransfer-Encodingheader. -
For better compatibility with broken servers, h11 now tolerates responses where the reason phrase is missing (not just empty).
-
Various optimizations and documentation improvements.
v0.7.0 (2016-11-25)¶
New features (backwards compatible):
-
Made it so that sentinels are instances of themselves, to enable certain dispatch tricks on the return value of
Connection.next_event()(see issue #8 for discussion). -
Added
Data.chunk_startandData.chunk_endproperties to theDataevent. These provide the user information about where chunk delimiters are in the data stream from the remote peer when chunked transfer encoding is in use. You probably shouldn't use these, but sometimes there's no alternative (see issue #19 for discussion). -
Expose
Response.reasonattribute, making it possible to read or set the textual "reason phrase" on responses (issue #13).
Bug fixes:
-
Fix the error message given when a call to an event constructor is missing a required keyword argument (issue #14).
-
Fixed encoding of empty
Dataevents (Data(data=b"")) when using chunked encoding (issue #21).
v0.6.0 (2016-10-24)¶
This is the first release since we started using h11 to write non-trivial server code, and this experience triggered a number of substantial API changes.
Backwards incompatible changes:
-
Split the old
receive_data()into the newConnection.receive_data()andConnection.next_event(), and replaced the oldPausedpseudo-event with the newNEED_DATAandPAUSEDsentinels. -
Simplified the API by replacing the old
Connection.state_of(),Connection.client_state,Connection.server_statewith the newConnection.states. -
Renamed the old
prepare_to_reuse()to the newConnection.start_next_cycle(). -
Removed the
Pausedpseudo-event.
Backwards compatible changes:
-
State machine: added a
DONE->MUST_CLOSEtransition triggered by our peer being in theERRORstate. -
Split
ProtocolErrorintoLocalProtocolErrorandRemoteProtocolError(see Error handling). Use case: HTTP servers want to be able to distinguish between an error that originates locally (which produce a 500 status code) versus errors caused by remote misbehavior (which produce a 4xx status code). -
Changed the
PRODUCT_IDfromh11/<verson>topython-h11/<version>. (This is similar to what requests uses, and much more searchable than plain h11.)
Other changes:
- Added a minimal benchmark suite, and used it to make a few small optimizations (maybe ~20% speedup?).
v0.5.0 (2016-05-14)¶
- Initial release.