{"id":254,"library":"h11","title":"h11","description":"h11 is a pure-Python, bring-your-own-I/O implementation of HTTP/1.1, heavily inspired by hyper-h2. It contains no networking code whatsoever — you supply the bytes in and out — making it usable with any I/O model: synchronous, threaded, asyncio, trio, or custom. It models the HTTP exchange as a strict state machine emitting typed event objects (Request, Response, Data, EndOfMessage, etc.) and enforces spec conformance on both incoming and outgoing messages. Current version is 0.16.0, released in 2025 as a security fix; releases are infrequent and focused on correctness. It has no dependencies outside the Python standard library.","status":"active","version":"0.16.0","language":"python","source_language":"en","source_url":"https://github.com/python-hyper/h11","tags":["http","http1.1","networking","protocol","sans-io","asyncio","trio","parser"],"install":[{"cmd":"pip install h11","lang":"bash","label":"pip"}],"dependencies":[],"imports":[{"note":"All public symbols live directly on the h11 namespace. Do not import from h11._connection or other private submodules.","symbol":"Connection","correct":"import h11; conn = h11.Connection(our_role=h11.CLIENT)"},{"note":"All event classes are top-level on h11. Importing from h11._events is private API and may break across patch releases.","symbol":"Request / Response / Data / EndOfMessage / InformationalResponse / ConnectionClosed","correct":"import h11  # then use h11.Request, h11.Response, etc."},{"note":"These are re-exported at the top-level h11 namespace; importing from h11._util is private and unsupported.","wrong":"from h11._util import LocalProtocolError","symbol":"LocalProtocolError / RemoteProtocolError / ProtocolError","correct":"import h11  # catch h11.LocalProtocolError, h11.RemoteProtocolError"},{"note":"Sentinel values are identity-compared with 'is', not '=='. Use type(event) dispatch or identity checks.","symbol":"NEED_DATA / PAUSED / CLIENT / SERVER","correct":"import h11  # use h11.NEED_DATA, h11.PAUSED, h11.CLIENT, h11.SERVER"}],"quickstart":{"code":"import socket\nimport ssl\nimport h11\n\nHOST = \"httpbin.org\"\nPORT = 443\n\nctx = ssl.create_default_context()\nsock = ctx.wrap_socket(\n    socket.create_connection((HOST, PORT)),\n    server_hostname=HOST,\n)\n\nconn = h11.Connection(our_role=h11.CLIENT)\n\n# Send request\nsock.sendall(conn.send(h11.Request(\n    method=\"GET\",\n    target=\"/get\",\n    headers=[(\"Host\", HOST), (\"Connection\", \"close\")],\n)))\nsock.sendall(conn.send(h11.EndOfMessage()))\n\n# Receive events\nwhile True:\n    event = conn.next_event()\n    if event is h11.NEED_DATA:\n        data = sock.recv(4096)\n        conn.receive_data(data)\n        continue\n    if isinstance(event, h11.Response):\n        print(\"Status:\", event.status_code)\n    elif isinstance(event, h11.Data):\n        print(\"Body chunk:\", event.data[:80])\n    elif isinstance(event, (h11.EndOfMessage, h11.ConnectionClosed)):\n        break\n\nsock.close()\n","lang":"python","description":"Minimal synchronous HTTP/1.1 client using a raw socket. Demonstrates Connection setup, sending a Request + EndOfMessage, and draining events in a loop."},"warnings":[{"fix":"Catch h11.LocalProtocolError for errors you caused and h11.RemoteProtocolError for peer misbehavior. Both inherit from h11.ProtocolError if you need a catch-all.","message":"ProtocolError was split into LocalProtocolError and RemoteProtocolError in v0.10. Catching the old bare ProtocolError still works (it is now an abstract base class), but code that previously caught ProtocolError to distinguish client vs server errors must be updated.","severity":"breaking","affected_versions":"<0.10"},{"fix":"Upgrade to Python >= 3.8. Pin h11 <= 0.11.x only if you must keep Python 2 support.","message":"Python 2 support was dropped in v0.12; Python 3.6 support dropped in v0.14. h11 now requires Python >= 3.8 (as of v0.16).","severity":"breaking","affected_versions":"<0.12 (Py2), <0.14 (Py3.6)"},{"fix":"Strip whitespace from header values before passing them to h11 event constructors, or fix the upstream source producing invalid header values.","message":"Outgoing header validation became strict in v0.10+: headers with illegal characters or leading/trailing whitespace in values now raise LocalProtocolError. Previously, whitespace was silently stripped.","severity":"breaking","affected_versions":"<0.10"},{"fix":"Upgrade to h11 >= 0.16.0. If you are behind a reverse proxy, also ensure the proxy correctly validates chunked encoding to prevent request smuggling.","message":"v0.16.0 rejects previously-accepted malformed Transfer-Encoding: chunked bodies (CVE / GHSA-vqfr-h8mv-ghfj). Code or test suites that construct intentionally malformed chunked bodies will now get a RemoteProtocolError.","severity":"breaking","affected_versions":"<0.16.0"},{"fix":"Close the socket and create a brand-new h11.Connection for the next request. Do not attempt to call start_next_cycle() after an error.","message":"Protocol errors are unrecoverable. Once LocalProtocolError or RemoteProtocolError is raised, the Connection state becomes ERROR and all subsequent send()/next_event() calls also raise. You cannot reset or reuse the connection.","severity":"gotcha","affected_versions":"all"},{"fix":"Verify conn.our_state is h11.DONE and conn.their_state is h11.DONE before calling conn.start_next_cycle(). Always fully drain response events (including EndOfMessage) before reusing.","message":"start_next_cycle() for keep-alive reuse raises LocalProtocolError('not in a reusable state') if called before both sides have reached DONE state. Forgetting to send or fully consume EndOfMessage is a common cause.","severity":"gotcha","affected_versions":"all"},{"fix":"Accumulate all Data event payloads in a list/bytearray and concatenate only after receiving EndOfMessage.","message":"Response bodies may be delivered across multiple Data events because h11 buffers as little as possible. Assuming a single Data event contains the full body will silently truncate data.","severity":"gotcha","affected_versions":"all"}],"env_vars":null,"last_verified":"2026-05-12T12:23:25.627Z","next_check":"2026-06-25T00:00:00.000Z","problems":[{"fix":"Upgrade `h11` to the latest version (`pip install --upgrade h11`) and also upgrade any libraries that depend on `h11` (e.g., `pip install --upgrade httpcore httpx`). If `Event` is being directly referenced, check the `h11` documentation for the correct event class (e.g., `h11.Request`, `h11.Response`) as a generic `h11.Event` is not a top-level class.","cause":"This error typically arises when a dependent library (like `httpx` or `gradio`) expects a specific `Event` object or attribute from `h11` that is either not exposed directly, or due to version incompatibility where `h11` or its dependents are outdated.","error":"AttributeError: module 'h11' has no attribute 'Event'"},{"fix":"Reinstall or upgrade `h11` (`pip install --upgrade h11`) and its direct dependencies (e.g., `pip install --upgrade httpcore httpx`). Using a virtual environment and ensuring all packages are installed fresh can help resolve dependency conflicts.","cause":"This error occurs when a Python package, often one that indirectly depends on `h11` (like the OpenAI Python client), attempts to import an internal submodule `h11._util` that cannot be found. This usually indicates a broken `h11` installation, conflicting dependencies, or an incompatibility after an upgrade of `h11` or a dependent library.","error":"ModuleNotFoundError: No module named 'h11._util'"},{"fix":"Ensure that the `Content-Length` header in your HTTP request or response accurately reflects the actual byte length of the body being sent. If using chunked transfer encoding, do not set `Content-Length`.","cause":"`h11` raises this `LocalProtocolError` when your application, acting as either a client or server, sends a message body that is larger than the `Content-Length` header it declared, violating the HTTP/1.1 protocol.","error":"h11._util.LocalProtocolError: Too much data for declared Content-Length"},{"fix":"This error indicates a problem with the remote peer's HTTP message. If you control the peer, fix its HTTP implementation. If you are a server receiving this, it suggests a client is sending invalid HTTP. If you are a client, the server might be sending an invalid initial response. You may need to inspect the raw bytes received to diagnose the specific non-compliance.","cause":"`h11` raises this `RemoteProtocolError` when it receives an HTTP request or response line from a remote peer that does not conform to the HTTP/1.1 specification (e.g., malformed syntax, incorrect method/target, or unsupported HTTP version).","error":"h11._util.RemoteProtocolError: illegal request line"}],"ecosystem":"pypi","meta_description":null,"install_score":100,"install_tag":"verified","quickstart_score":80,"quickstart_tag":"verified","pypi_latest":null,"install_checks":{"last_tested":"2026-05-12","tag":"verified","tag_description":"installs cleanly on critical runtimes, fast import, recently tested","results":[{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.05,"mem_mb":1.8,"disk_size":"18.0M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.03,"mem_mb":1.8,"disk_size":"18M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.09,"mem_mb":2.1,"disk_size":"19.9M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.07,"mem_mb":2.1,"disk_size":"20M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.08,"mem_mb":1.8,"disk_size":"11.7M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.08,"mem_mb":1.8,"disk_size":"12M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.07,"mem_mb":2.1,"disk_size":"11.4M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.07,"mem_mb":1.9,"disk_size":"12M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.04,"mem_mb":1.8,"disk_size":"17.5M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.04,"mem_mb":1.8,"disk_size":"18M"}]},"quickstart_checks":{"last_tested":"2026-04-23","tag":"verified","tag_description":"quickstart runs on critical runtimes, recently tested","results":[{"runtime":"python:3.10-alpine","exit_code":0},{"runtime":"python:3.10-slim","exit_code":0},{"runtime":"python:3.11-alpine","exit_code":0},{"runtime":"python:3.11-slim","exit_code":0},{"runtime":"python:3.12-alpine","exit_code":0},{"runtime":"python:3.12-slim","exit_code":0},{"runtime":"python:3.13-alpine","exit_code":0},{"runtime":"python:3.13-slim","exit_code":0},{"runtime":"python:3.9-alpine","exit_code":0},{"runtime":"python:3.9-slim","exit_code":0}]}}