Skip to main content

chumicro-http-server

A non-blocking HTTP/1.1 server with @route: serve requests while your LED keeps blinking.

Routing with @server.route (method dispatch, path parameters), bounded multi-connection, per-tick byte budgets, and a streaming request parser, all without blocking your main loop. Serves TLS on every supported board pair except CircuitPython on RP2040 (CYW43 substrate limitation; documented inline). Self-contained: no chumicro-requests dependency on the device.


Part of the ChuMicro family: small, focused Python libraries for microcontrollers and laptops. Browse all libraries.

Install

# CircuitPython (after `circup bundle-add ChuMicro/ChuMicro-Bundle-Experimental`)
circup install chumicro_http_server

# MicroPython
mpremote mip install github:ChuMicro/ChuMicro-Bundle-Experimental/chumicro_http_server

# CPython
pip install chumicro-http-server-experimental

For bundle setup, pre-compiled .mpy bundles, the experimental channel, and details on PyPI naming, see the chumicro INSTALL guide.

Quick example

from chumicro_http_server import HttpServer, build_response
from chumicro_sockets import listener
from chumicro_timing import ticks_ms

server = HttpServer(
    transport_factory=lambda: listener(host="0.0.0.0", port=8080),
)

@server.route("/")
def index(request):
    return build_response(200, html="<h1>Hello from a Pi Pico W</h1>")

@server.route("/sensor", methods=["POST"])
def sensor(request):
    payload = request.json()
    return build_response(201, json={"ok": True})

@server.route("/widgets/<id>")
def widget(request):
    return build_response(200, json={"id": request.path_params["id"]})

while True:
    if server.check(ticks_ms()):
        server.handle(ticks_ms())

What's included

Symbol Purpose
HttpServer Runner-shaped HTTP/1.1 server; check(now_ms) / handle(now_ms).
Request Per-request value object: method, path, query, headers, body, json(), text().
Response Outbound response: status_code, reason, headers, body.
build_response(status, *, body, json, text, html, headers) Convenience builder with sensible Content-Type defaults.
streaming.build_streaming_response(status, *, source, content_length, headers) Opt-in chumicro_http_server.streaming submodule. Serves a body larger than the heap from a fill-a-buffer source(buffer) -> int (Content-Length or chunked framing, fixed staging window).
RequestParser Streaming request parser (request line + headers + Content-Length body).
parse_query / split_target URL helpers.
ServerError + subclasses Typed exception hierarchy, independent of chumicro_requests so the server can ship without the client library.

Each request is served on a fresh accepted socket and Connection: close is added to every response. HTTP/1.1 keep-alive and connection pooling are not supported. Chunked request bodies are not supported either; use Content-Length.

Need to return a body bigger than the heap (a log dump, a file, a long export)? Return a streaming response from the opt-in chumicro_http_server.streaming submodule and the server drains it from a fill-a-buffer source one small window at a time, choosing Content-Length or chunked framing for you:

from chumicro_http_server.streaming import build_streaming_response, SOURCE_EOF

@server.route("/log")
def log_dump(request):
    def source(buffer):
        n = read_next_block_into(buffer)   # your storage read; 0 <= n <= len(buffer)
        return n if n else SOURCE_EOF      # -1 signals end of body
    return build_streaming_response(200, source=source)

See the user guide for the source contract, framing rules, fairness, and staging-window sizing.

Where this fits

Depends on chumicro-sockets (TCP listener) and chumicro-timing (ticks). Pairs with chumicro-websockets for combined HTTP + WS deployments. Self-contained otherwise: the shared HTTP/1.1 primitives (case-insensitive header dict, charset parsing) are inlined locally, so a server-only board never ships chumicro-requests.

Platform support

Works on CPython, MicroPython, and CircuitPython. Pure Python, no native extensions.

TLS server (HTTPS)

chumicro-http-server itself is transport-agnostic: pass a TLS-wrapped listener from chumicro_sockets.ssl_context_with_cert_and_key_paths into transport_factory and the same HttpServer runs HTTPS. Live verification across the supported board matrix:

Runtime + board TLS server status Notes
CircuitPython on ESP32-S2 (Lolin S2) ✅ Works Bench-tested ~5 KB context (RSA-2048); each connection adds tens of KB during handshake, so leave headroom.
CircuitPython on rp2 (Pi Pico W / Pi Pico 2 W) ❌ Refused (UnsupportedSSLConfigError) chumicro_sockets.listener(tls=True) raises up-front; the underlying CYW43 TLS path raises OSError(32) mid-handshake AND wedges the chip's station-mode state. Use ESP32-family or MicroPython on rp2.
MicroPython on ESP32-S2 ✅ Works Hardware-accelerated handshake; ~1 KB heap.
MicroPython on rp2 (Pi Pico W) ✅ Works (RSA-2048 only) DER-encoded key; ~25 KB handshake heap; ECC keys fail at context build.

Why the CP-on-rp2 row? The CYW43 stack's TLS server path raises OSError(32) mid-handshake and wedges the chip's station state until a USB power-cycle. No upstream fix is in flight; for HTTPS server work on rp2, use MicroPython.

The TLS handshake is synchronous inside wrap_socket(..., server_side=True): the listener stalls until it completes (single-digit to tens of milliseconds on the supported board class with a local TLS client, longer on a slow uplink as TLS rounds-trip). Once the handshake completes, the per-connection state machine resumes its runner-shaped, LED-blink-friendly progression.

Examples

Example What it shows
simple_server.py Single-board HTTP server with GET /, GET /api/uptime, POST /api/echo routes. Drive it with curl from your laptop. Cross-runtime (CP + MP); the runtime marker on the file gates hardware-only deploys. For a two-physical-board demo see the workspace template's two_board_handshake/ example.

Wiring wifi credentials

The hardware examples need wifi credentials to join your network. The library itself never reads TOML. It takes a transport_factory and goes, so bringing wifi up and handing the server a connected transport is application-layer work.

Contributing

Issues, bug reports, and pull requests are welcome, and so is "I ran it on this board and here's what happened", some of the most useful feedback a hardware project can get. Development happens in the ChuMicro repository, whose contributing guide covers setup and the test workflow.

Docs

📖 Stable docs · Experimental docs

Find this library

License

MIT

Release files for chumicro-http-server-experimental 0.19.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for chumicro-http-server-experimental 0.19.1
File Size Uploaded
chumicro_http_server_experimental-0.19.1.tar.gz 55.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for chumicro-http-server-experimental 0.19.1
File Interpreter ABI Platform
chumicro_http_server_experimental-0.19.1-py3-none-any.whl Python 3 none any Details

Total release size: 78.8 kB

Release files / chumicro_http_server_experimental-0.19.1.tar.gz

Download URL chumicro_http_server_experimental-0.19.1.tar.gz
Size 55.0 kB
Tags Source
SHA-256 checksum
How to use checksums
f6b926305d98afcc7660bbd6239cd109e4fc17b05b46721746f9cbe5fb7d6796
BLAKE2b-256 checksum
How to use checksums
ea320eedb1622ba677ae645010b66af21489ae82fb43cf8100a7e40b5baa382b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 7, 2026.

Transparency log

Release files / chumicro_http_server_experimental-0.19.1-py3-none-any.whl

Download URL chumicro_http_server_experimental-0.19.1-py3-none-any.whl
Size 23.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1ef7bf8aeedc7e61a6fa774cbe91427888c8bb87207eacf8b5ee4c8b6b8866ab
BLAKE2b-256 checksum
How to use checksums
203c50ca504a1fb20165307cedf9242b38cf9912d7e5b9dec15ac0b3824a42bb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 7, 2026.

Transparency log

Release history Release notifications | RSS feed

0.21.3

2 release files

0.21.2

2 release files

0.21.1

2 release files

This release

0.19.1 This release

2 release files

0.19.0

2 release files

0.18.2

2 release files

0.18.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page