chumicro-http-server-experimental 0.18.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- chumicro_http_server_experimental-0.18.0/.gitignore +99 -0
- chumicro_http_server_experimental-0.18.0/PKG-INFO +187 -0
- chumicro_http_server_experimental-0.18.0/README.md +155 -0
- chumicro_http_server_experimental-0.18.0/VERSION +1 -0
- chumicro_http_server_experimental-0.18.0/docs/api.md +25 -0
- chumicro_http_server_experimental-0.18.0/docs/guide.md +307 -0
- chumicro_http_server_experimental-0.18.0/docs/img/favicon.png +0 -0
- chumicro_http_server_experimental-0.18.0/docs/index.md +50 -0
- chumicro_http_server_experimental-0.18.0/docs/stylesheets/extra.css +151 -0
- chumicro_http_server_experimental-0.18.0/examples/_generated/example_runtime_config_http_server_simple_server.msgpack +1 -0
- chumicro_http_server_experimental-0.18.0/examples/helpers.py +340 -0
- chumicro_http_server_experimental-0.18.0/examples/simple_server.py +110 -0
- chumicro_http_server_experimental-0.18.0/pyproject.toml +89 -0
- chumicro_http_server_experimental-0.18.0/src/chumicro_http_server/__init__.py +87 -0
- chumicro_http_server_experimental-0.18.0/src/chumicro_http_server/_wire.py +811 -0
- chumicro_http_server_experimental-0.18.0/src/chumicro_http_server/server.py +1166 -0
- chumicro_http_server_experimental-0.18.0/src/chumicro_http_server/sockets_factory.py +53 -0
- chumicro_http_server_experimental-0.18.0/src/chumicro_http_server/streaming.py +461 -0
- chumicro_http_server_experimental-0.18.0/src/chumicro_http_server/testing.py +71 -0
- chumicro_http_server_experimental-0.18.0/tests/conftest.py +1 -0
- chumicro_http_server_experimental-0.18.0/tests/test_http_caseinsensitive_dict.py +105 -0
- chumicro_http_server_experimental-0.18.0/tests/test_http_from_config.py +255 -0
- chumicro_http_server_experimental-0.18.0/tests/test_http_server_e2e.py +399 -0
- chumicro_http_server_experimental-0.18.0/tests/test_http_server_misc.py +259 -0
- chumicro_http_server_experimental-0.18.0/tests/test_http_server_request_routing.py +380 -0
- chumicro_http_server_experimental-0.18.0/tests/test_http_streaming_e2e.py +426 -0
- chumicro_http_server_experimental-0.18.0/tests/test_http_streaming_wire.py +268 -0
- chumicro_http_server_experimental-0.18.0/tests/test_http_wire_parse.py +327 -0
- chumicro_http_server_experimental-0.18.0/tests/test_http_wire_response.py +202 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
.DS_Store
|
|
2
|
+
.idea/*
|
|
3
|
+
!.idea/modules.xml
|
|
4
|
+
!.idea/chumicro.iml
|
|
5
|
+
!.idea/runConfigurations/
|
|
6
|
+
!.idea/inspectionProfiles/
|
|
7
|
+
.venv/
|
|
8
|
+
.tools/
|
|
9
|
+
__pycache__/
|
|
10
|
+
*.py[cod]
|
|
11
|
+
.pytest_cache/
|
|
12
|
+
.ruff_cache/
|
|
13
|
+
.coverage
|
|
14
|
+
coverage.xml
|
|
15
|
+
htmlcov/
|
|
16
|
+
build/
|
|
17
|
+
dist/
|
|
18
|
+
site/
|
|
19
|
+
.site/
|
|
20
|
+
*.egg-info/
|
|
21
|
+
|
|
22
|
+
# Generated by docs build from support/docs/ and zensical
|
|
23
|
+
libraries/*/docs/stylesheets/
|
|
24
|
+
libraries/*/docs/img/
|
|
25
|
+
libraries/*/docs/assets/
|
|
26
|
+
workbench/*/docs/stylesheets/
|
|
27
|
+
workbench/*/docs/img/
|
|
28
|
+
workbench/*/docs/assets/
|
|
29
|
+
|
|
30
|
+
.mypy_cache/
|
|
31
|
+
.scratch/
|
|
32
|
+
|
|
33
|
+
# Generated runtime-config msgpack from `chumicro-workspace deploy-example`
|
|
34
|
+
# — the `_generated/` build-artifact convention, never committed beside
|
|
35
|
+
# the tracked example source.
|
|
36
|
+
libraries/*/examples/_generated/
|
|
37
|
+
|
|
38
|
+
# Pre-upgrade backups of edited curated library trees, written by
|
|
39
|
+
# `chumicro-workspace library update` before it replaces a tree —
|
|
40
|
+
# tool-managed, never committed; recover local edits from here.
|
|
41
|
+
_library-backups/
|
|
42
|
+
|
|
43
|
+
# Agent-tooling scratch dir (worktrees, per-user settings). Track only:
|
|
44
|
+
# * the `skills` symlink so Claude Code auto-discovers the canonical skills
|
|
45
|
+
# tree at `.github/skills/` from the location it expects (`.claude/skills/`).
|
|
46
|
+
# * `settings.json` — team-shared agent permissions for chumicro dev tooling
|
|
47
|
+
# (chumicro-workspace / chumicro-deploy / chumicro-repl). `.local.json`
|
|
48
|
+
# stays ignored for per-user overrides.
|
|
49
|
+
.claude/*
|
|
50
|
+
!.claude/agents
|
|
51
|
+
!.claude/skills
|
|
52
|
+
!.claude/settings.json
|
|
53
|
+
|
|
54
|
+
# .agents/ — install tree the add-skill tool writes when pulling downloaded
|
|
55
|
+
# marketplace skills from `skills-lock.json`. The canonical, tracked copy of
|
|
56
|
+
# every downloaded skill lives under `.github/skills/`; this is a redundant
|
|
57
|
+
# per-checkout install tree, so track the manifest plus `.github/skills/` and
|
|
58
|
+
# ignore this duplicate.
|
|
59
|
+
.agents/
|
|
60
|
+
|
|
61
|
+
devices.yml
|
|
62
|
+
|
|
63
|
+
# Legacy: chumicro-dev-config.toml. Retired in Phase 4 of the unification
|
|
64
|
+
# workstream — networking-library functional-test conftests now read from
|
|
65
|
+
# workspace.yml via `chumicro_workspace.compose_runtime_config`. Kept
|
|
66
|
+
# gitignored for one cycle so contributors with a left-over copy from the
|
|
67
|
+
# pre-Phase-4 era don't accidentally commit it.
|
|
68
|
+
chumicro-dev-config.toml
|
|
69
|
+
|
|
70
|
+
# workspace.yml — workspace machinery (library_sources, deploy_targets,
|
|
71
|
+
# quality, environments). Materialised by ``python scripts/run.py setup``
|
|
72
|
+
# from `_workspace_template/workspace.yml` (this repo's specific opinions
|
|
73
|
+
# — tracked, ships as the starter) or chumicro-workspace's workbench-owned
|
|
74
|
+
# starter. Gitignored so the auto-populated ``library_sources:`` block
|
|
75
|
+
# (paths into a sibling chumicro/ checkout) doesn't reach git. Root-
|
|
76
|
+
# anchored so `_workspace_template/workspace.yml` stays trackable.
|
|
77
|
+
/workspace.yml
|
|
78
|
+
|
|
79
|
+
# secrets.toml — workspace-wide credentials + device defaults that flow
|
|
80
|
+
# into runtime_config.msgpack at deploy time. Materialised on first
|
|
81
|
+
# ``setup`` from `_workspace_template/secrets.toml` (this repo's specific
|
|
82
|
+
# opinions — tracked, ships as the starter) or the workbench-owned
|
|
83
|
+
# starter. Never committed — contains real wifi passwords / broker auth.
|
|
84
|
+
# Root-anchored so `_workspace_template/secrets.toml` stays trackable.
|
|
85
|
+
/secrets.toml
|
|
86
|
+
|
|
87
|
+
# .envrc — direnv hook for per-checkout shell setup (e.g. auto-sourcing
|
|
88
|
+
# the venv on cd). Per-user / per-machine; not part of the canonical
|
|
89
|
+
# workflow.
|
|
90
|
+
.envrc
|
|
91
|
+
|
|
92
|
+
# Firmware blobs. `chumicro-deploy flash-firmware` and friends default
|
|
93
|
+
# to `./firmware.bin` for the downloaded image; the file is build /
|
|
94
|
+
# board artifact, not source. Use `.scratch/` for one-off testing or
|
|
95
|
+
# pass an explicit destination to keep blobs out of the working tree.
|
|
96
|
+
/firmware.bin
|
|
97
|
+
firmware-*.bin
|
|
98
|
+
*.uf2
|
|
99
|
+
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: chumicro-http-server-experimental
|
|
3
|
+
Version: 0.18.0
|
|
4
|
+
Summary: Non-blocking HTTP/1.1 server for CircuitPython, MicroPython, and CPython.
|
|
5
|
+
Project-URL: Homepage, https://github.com/ChuMicro/ChuMicro
|
|
6
|
+
Project-URL: Documentation, https://chumicro.github.io/ChuMicro/http_server/experimental/
|
|
7
|
+
Project-URL: Source, https://github.com/ChuMicro/ChuMicro/tree/main/libraries/http_server
|
|
8
|
+
Project-URL: Issues, https://github.com/ChuMicro/ChuMicro/issues
|
|
9
|
+
Project-URL: Bundle, https://github.com/ChuMicro/ChuMicro-Bundle-Experimental
|
|
10
|
+
Author: ChuMicro
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
Keywords: circuitpython,embedded,esp32,http,microcontroller,micropython,rp2040
|
|
13
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Topic :: Software Development :: Embedded Systems
|
|
19
|
+
Classifier: Topic :: System :: Hardware
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Requires-Dist: chumicro-config-experimental
|
|
22
|
+
Requires-Dist: chumicro-sockets-experimental
|
|
23
|
+
Requires-Dist: chumicro-timing-experimental
|
|
24
|
+
Provides-Extra: test
|
|
25
|
+
Requires-Dist: chumicro-pytest-device; extra == 'test'
|
|
26
|
+
Requires-Dist: chumicro-requests; extra == 'test'
|
|
27
|
+
Requires-Dist: chumicro-test-harness; extra == 'test'
|
|
28
|
+
Requires-Dist: chumicro-wifi; extra == 'test'
|
|
29
|
+
Requires-Dist: chumicro-workspace; extra == 'test'
|
|
30
|
+
Requires-Dist: pytest; extra == 'test'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# chumicro-http-server
|
|
34
|
+
|
|
35
|
+
<img src="https://raw.githubusercontent.com/ChuMicro/ChuMicro/main/support/docs/chumicro_tip.png"
|
|
36
|
+
align="left" width="64" style="margin-right: 16px; margin-bottom: 8px;">
|
|
37
|
+
|
|
38
|
+
**A non-blocking HTTP/1.1 server with `@route` — serve requests while your LED keeps blinking.**
|
|
39
|
+
|
|
40
|
+
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.
|
|
41
|
+
|
|
42
|
+
<br clear="left">
|
|
43
|
+
|
|
44
|
+
> Part of the [ChuMicro](https://github.com/ChuMicro/ChuMicro) family — small, focused Python libraries for microcontrollers and laptops. [Browse all libraries.](https://github.com/ChuMicro/ChuMicro/tree/main/libraries)
|
|
45
|
+
|
|
46
|
+
## Install
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# CircuitPython (after `circup bundle-add ChuMicro/ChuMicro-Bundle`)
|
|
50
|
+
circup install chumicro-http-server
|
|
51
|
+
|
|
52
|
+
# MicroPython
|
|
53
|
+
mpremote mip install github:ChuMicro/ChuMicro-Bundle/chumicro_http_server
|
|
54
|
+
|
|
55
|
+
# CPython
|
|
56
|
+
pip install chumicro-http-server
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
For bundle setup, pre-compiled `.mpy` bundles, the experimental channel, and details on PyPI naming, see the [chumicro INSTALL guide](https://github.com/ChuMicro/ChuMicro/blob/main/INSTALL.md).
|
|
60
|
+
|
|
61
|
+
## Quick example
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from chumicro_http_server import HttpServer, build_response
|
|
65
|
+
from chumicro_sockets import listener
|
|
66
|
+
from chumicro_timing import ticks_ms
|
|
67
|
+
|
|
68
|
+
server = HttpServer(
|
|
69
|
+
transport_factory=lambda: listener(host="0.0.0.0", port=8080),
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
@server.route("/")
|
|
73
|
+
def index(request):
|
|
74
|
+
return build_response(200, html="<h1>Hello from a Pi Pico W</h1>")
|
|
75
|
+
|
|
76
|
+
@server.route("/sensor", methods=["POST"])
|
|
77
|
+
def sensor(request):
|
|
78
|
+
payload = request.json()
|
|
79
|
+
return build_response(201, json={"ok": True})
|
|
80
|
+
|
|
81
|
+
@server.route("/widgets/<id>")
|
|
82
|
+
def widget(request):
|
|
83
|
+
return build_response(200, json={"id": request.path_params["id"]})
|
|
84
|
+
|
|
85
|
+
while True:
|
|
86
|
+
if server.check(ticks_ms()):
|
|
87
|
+
server.handle(ticks_ms())
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## What's included
|
|
91
|
+
|
|
92
|
+
| Symbol | Purpose |
|
|
93
|
+
|---|---|
|
|
94
|
+
| `HttpServer` | Runner-shaped HTTP/1.1 server; `check(now_ms)` / `handle(now_ms)`. |
|
|
95
|
+
| `Request` | Per-request value object: `method`, `path`, `query`, `headers`, `body`, `json()`, `text()`. |
|
|
96
|
+
| `Response` | Outbound response: `status_code`, `reason`, `headers`, `body`. |
|
|
97
|
+
| `build_response(status, *, body, json, text, html, headers)` | Convenience builder with sensible Content-Type defaults. |
|
|
98
|
+
| `streaming.build_streaming_response(status, *, source, content_length, headers)` | Opt-in `chumicro_http_server.streaming` submodule — serve a body larger than the heap from a fill-a-buffer `source(buffer) -> int` (Content-Length or chunked framing, fixed staging window). |
|
|
99
|
+
| `RequestParser` | Streaming request parser (request line + headers + Content-Length body). |
|
|
100
|
+
| `parse_query` / `split_target` | URL helpers. |
|
|
101
|
+
| `ServerError` + subclasses | Typed exception hierarchy, independent of `chumicro_requests` so the server can ship without the client library. |
|
|
102
|
+
|
|
103
|
+
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`.
|
|
104
|
+
|
|
105
|
+
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:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from chumicro_http_server.streaming import build_streaming_response, SOURCE_EOF
|
|
109
|
+
|
|
110
|
+
@server.route("/log")
|
|
111
|
+
def log_dump(request):
|
|
112
|
+
def source(buffer):
|
|
113
|
+
n = read_next_block_into(buffer) # your storage read; 0 <= n <= len(buffer)
|
|
114
|
+
return n if n else SOURCE_EOF # -1 signals end of body
|
|
115
|
+
return build_streaming_response(200, source=source)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
See the [user guide](https://chumicro.github.io/ChuMicro/http_server/stable/guide/#streaming-large-response-bodies) for the source contract, framing rules, fairness, and staging-window sizing.
|
|
119
|
+
|
|
120
|
+
## Where this fits
|
|
121
|
+
|
|
122
|
+
Depends on [`chumicro-sockets`](../sockets/) (TCP listener) and [`chumicro-timing`](../timing/) (ticks). Pairs with [`chumicro-websockets`](../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`](../requests/).
|
|
123
|
+
|
|
124
|
+
## Platform support
|
|
125
|
+
|
|
126
|
+
Works on CPython, MicroPython, and CircuitPython. Pure Python — no native extensions.
|
|
127
|
+
|
|
128
|
+
### TLS server (HTTPS)
|
|
129
|
+
|
|
130
|
+
`chumicro-http-server` itself is transport-agnostic — pass a TLS-wrapped
|
|
131
|
+
listener from
|
|
132
|
+
[`chumicro_sockets.ssl_context_with_cert_and_key_paths`](https://github.com/ChuMicro/ChuMicro/tree/main/libraries/sockets)
|
|
133
|
+
into `transport_factory` and the same `HttpServer` runs HTTPS. Live
|
|
134
|
+
verification across the supported board matrix:
|
|
135
|
+
|
|
136
|
+
| Runtime + board | TLS server status | Notes |
|
|
137
|
+
|---|---|---|
|
|
138
|
+
| CircuitPython on ESP32-S2 (Lolin S2) | ✅ Works | Bench-tested ~5 KB context (RSA-2048); each connection adds tens of KB during handshake — leave headroom. |
|
|
139
|
+
| 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. |
|
|
140
|
+
| MicroPython on ESP32-S2 | ✅ Works | Hardware-accelerated handshake; ~1 KB heap. |
|
|
141
|
+
| MicroPython on rp2 (Pi Pico W) | ✅ Works (RSA-2048 only) | DER-encoded key; ~25 KB handshake heap; ECC keys fail at context build. |
|
|
142
|
+
|
|
143
|
+
> **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.
|
|
144
|
+
|
|
145
|
+
The TLS handshake is synchronous inside `wrap_socket(..., server_side=True)`:
|
|
146
|
+
the listener stalls until it completes — single-digit to tens of
|
|
147
|
+
milliseconds on the supported board class with a local TLS client, longer
|
|
148
|
+
on a slow uplink as TLS rounds-trip. Once the handshake completes, the
|
|
149
|
+
per-connection state machine resumes its runner-shaped,
|
|
150
|
+
LED-blink-friendly progression.
|
|
151
|
+
|
|
152
|
+
## Examples
|
|
153
|
+
|
|
154
|
+
| Example | What it shows |
|
|
155
|
+
|---|---|
|
|
156
|
+
| `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) — runtime marker on the file gates hardware-only deploys. For a two-physical-board demo see the workspace template's `two_board_handshake/` example. |
|
|
157
|
+
|
|
158
|
+
## Wiring wifi credentials for examples and functional tests
|
|
159
|
+
|
|
160
|
+
The hardware-prefixed examples + real-network suites in `functional_tests/test_real_*.py` need wifi credentials. See [`docs/wiring-wifi-credentials.md`](https://github.com/ChuMicro/ChuMicro/blob/main/docs/wiring-wifi-credentials.md) for the workspace-based and raw single-file paths. The library itself never reads TOML — it takes a `transport_factory` and goes; config wiring is application-layer.
|
|
161
|
+
|
|
162
|
+
## Contributing
|
|
163
|
+
|
|
164
|
+
Working on `chumicro-http-server` itself? Clone the [mono-repo](https://github.com/ChuMicro/ChuMicro) if you haven't already — the rest of the workflow assumes you're inside that workspace.
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
pip install -e .[test]
|
|
168
|
+
pytest tests/ # host-side tests
|
|
169
|
+
pytest functional_tests/ # on-device tests (needs a board registered in devices.yml)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Register a board before running functional tests: `chumicro-workspace add-device <id> --address <port>`.
|
|
173
|
+
|
|
174
|
+
## Docs
|
|
175
|
+
|
|
176
|
+
📖 **[Stable docs](https://chumicro.github.io/ChuMicro/http-server/stable/)** · **[Experimental docs](https://chumicro.github.io/ChuMicro/http-server/experimental/)**
|
|
177
|
+
|
|
178
|
+
## Find this library
|
|
179
|
+
|
|
180
|
+
- **PyPI:** [chumicro-http-server](https://pypi.org/project/chumicro-http-server/)
|
|
181
|
+
- **Bundle:** [ChuMicro-Bundle](https://github.com/ChuMicro/ChuMicro-Bundle/tree/main/chumicro_http_server) (CircuitPython & MicroPython)
|
|
182
|
+
- **Experimental bundle:** [ChuMicro-Bundle-Experimental](https://github.com/ChuMicro/ChuMicro-Bundle-Experimental/tree/main/chumicro_http_server)
|
|
183
|
+
- **Source:** [libraries/http_server](https://github.com/ChuMicro/ChuMicro/tree/main/libraries/http_server)
|
|
184
|
+
|
|
185
|
+
## License
|
|
186
|
+
|
|
187
|
+
[MIT](https://github.com/ChuMicro/ChuMicro/blob/main/LICENSE)
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# chumicro-http-server
|
|
2
|
+
|
|
3
|
+
<img src="https://raw.githubusercontent.com/ChuMicro/ChuMicro/main/support/docs/chumicro_tip.png"
|
|
4
|
+
align="left" width="64" style="margin-right: 16px; margin-bottom: 8px;">
|
|
5
|
+
|
|
6
|
+
**A non-blocking HTTP/1.1 server with `@route` — serve requests while your LED keeps blinking.**
|
|
7
|
+
|
|
8
|
+
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.
|
|
9
|
+
|
|
10
|
+
<br clear="left">
|
|
11
|
+
|
|
12
|
+
> Part of the [ChuMicro](https://github.com/ChuMicro/ChuMicro) family — small, focused Python libraries for microcontrollers and laptops. [Browse all libraries.](https://github.com/ChuMicro/ChuMicro/tree/main/libraries)
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# CircuitPython (after `circup bundle-add ChuMicro/ChuMicro-Bundle`)
|
|
18
|
+
circup install chumicro-http-server
|
|
19
|
+
|
|
20
|
+
# MicroPython
|
|
21
|
+
mpremote mip install github:ChuMicro/ChuMicro-Bundle/chumicro_http_server
|
|
22
|
+
|
|
23
|
+
# CPython
|
|
24
|
+
pip install chumicro-http-server
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
For bundle setup, pre-compiled `.mpy` bundles, the experimental channel, and details on PyPI naming, see the [chumicro INSTALL guide](https://github.com/ChuMicro/ChuMicro/blob/main/INSTALL.md).
|
|
28
|
+
|
|
29
|
+
## Quick example
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from chumicro_http_server import HttpServer, build_response
|
|
33
|
+
from chumicro_sockets import listener
|
|
34
|
+
from chumicro_timing import ticks_ms
|
|
35
|
+
|
|
36
|
+
server = HttpServer(
|
|
37
|
+
transport_factory=lambda: listener(host="0.0.0.0", port=8080),
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
@server.route("/")
|
|
41
|
+
def index(request):
|
|
42
|
+
return build_response(200, html="<h1>Hello from a Pi Pico W</h1>")
|
|
43
|
+
|
|
44
|
+
@server.route("/sensor", methods=["POST"])
|
|
45
|
+
def sensor(request):
|
|
46
|
+
payload = request.json()
|
|
47
|
+
return build_response(201, json={"ok": True})
|
|
48
|
+
|
|
49
|
+
@server.route("/widgets/<id>")
|
|
50
|
+
def widget(request):
|
|
51
|
+
return build_response(200, json={"id": request.path_params["id"]})
|
|
52
|
+
|
|
53
|
+
while True:
|
|
54
|
+
if server.check(ticks_ms()):
|
|
55
|
+
server.handle(ticks_ms())
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## What's included
|
|
59
|
+
|
|
60
|
+
| Symbol | Purpose |
|
|
61
|
+
|---|---|
|
|
62
|
+
| `HttpServer` | Runner-shaped HTTP/1.1 server; `check(now_ms)` / `handle(now_ms)`. |
|
|
63
|
+
| `Request` | Per-request value object: `method`, `path`, `query`, `headers`, `body`, `json()`, `text()`. |
|
|
64
|
+
| `Response` | Outbound response: `status_code`, `reason`, `headers`, `body`. |
|
|
65
|
+
| `build_response(status, *, body, json, text, html, headers)` | Convenience builder with sensible Content-Type defaults. |
|
|
66
|
+
| `streaming.build_streaming_response(status, *, source, content_length, headers)` | Opt-in `chumicro_http_server.streaming` submodule — serve a body larger than the heap from a fill-a-buffer `source(buffer) -> int` (Content-Length or chunked framing, fixed staging window). |
|
|
67
|
+
| `RequestParser` | Streaming request parser (request line + headers + Content-Length body). |
|
|
68
|
+
| `parse_query` / `split_target` | URL helpers. |
|
|
69
|
+
| `ServerError` + subclasses | Typed exception hierarchy, independent of `chumicro_requests` so the server can ship without the client library. |
|
|
70
|
+
|
|
71
|
+
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`.
|
|
72
|
+
|
|
73
|
+
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:
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from chumicro_http_server.streaming import build_streaming_response, SOURCE_EOF
|
|
77
|
+
|
|
78
|
+
@server.route("/log")
|
|
79
|
+
def log_dump(request):
|
|
80
|
+
def source(buffer):
|
|
81
|
+
n = read_next_block_into(buffer) # your storage read; 0 <= n <= len(buffer)
|
|
82
|
+
return n if n else SOURCE_EOF # -1 signals end of body
|
|
83
|
+
return build_streaming_response(200, source=source)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
See the [user guide](https://chumicro.github.io/ChuMicro/http_server/stable/guide/#streaming-large-response-bodies) for the source contract, framing rules, fairness, and staging-window sizing.
|
|
87
|
+
|
|
88
|
+
## Where this fits
|
|
89
|
+
|
|
90
|
+
Depends on [`chumicro-sockets`](../sockets/) (TCP listener) and [`chumicro-timing`](../timing/) (ticks). Pairs with [`chumicro-websockets`](../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`](../requests/).
|
|
91
|
+
|
|
92
|
+
## Platform support
|
|
93
|
+
|
|
94
|
+
Works on CPython, MicroPython, and CircuitPython. Pure Python — no native extensions.
|
|
95
|
+
|
|
96
|
+
### TLS server (HTTPS)
|
|
97
|
+
|
|
98
|
+
`chumicro-http-server` itself is transport-agnostic — pass a TLS-wrapped
|
|
99
|
+
listener from
|
|
100
|
+
[`chumicro_sockets.ssl_context_with_cert_and_key_paths`](https://github.com/ChuMicro/ChuMicro/tree/main/libraries/sockets)
|
|
101
|
+
into `transport_factory` and the same `HttpServer` runs HTTPS. Live
|
|
102
|
+
verification across the supported board matrix:
|
|
103
|
+
|
|
104
|
+
| Runtime + board | TLS server status | Notes |
|
|
105
|
+
|---|---|---|
|
|
106
|
+
| CircuitPython on ESP32-S2 (Lolin S2) | ✅ Works | Bench-tested ~5 KB context (RSA-2048); each connection adds tens of KB during handshake — leave headroom. |
|
|
107
|
+
| 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. |
|
|
108
|
+
| MicroPython on ESP32-S2 | ✅ Works | Hardware-accelerated handshake; ~1 KB heap. |
|
|
109
|
+
| MicroPython on rp2 (Pi Pico W) | ✅ Works (RSA-2048 only) | DER-encoded key; ~25 KB handshake heap; ECC keys fail at context build. |
|
|
110
|
+
|
|
111
|
+
> **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.
|
|
112
|
+
|
|
113
|
+
The TLS handshake is synchronous inside `wrap_socket(..., server_side=True)`:
|
|
114
|
+
the listener stalls until it completes — single-digit to tens of
|
|
115
|
+
milliseconds on the supported board class with a local TLS client, longer
|
|
116
|
+
on a slow uplink as TLS rounds-trip. Once the handshake completes, the
|
|
117
|
+
per-connection state machine resumes its runner-shaped,
|
|
118
|
+
LED-blink-friendly progression.
|
|
119
|
+
|
|
120
|
+
## Examples
|
|
121
|
+
|
|
122
|
+
| Example | What it shows |
|
|
123
|
+
|---|---|
|
|
124
|
+
| `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) — runtime marker on the file gates hardware-only deploys. For a two-physical-board demo see the workspace template's `two_board_handshake/` example. |
|
|
125
|
+
|
|
126
|
+
## Wiring wifi credentials for examples and functional tests
|
|
127
|
+
|
|
128
|
+
The hardware-prefixed examples + real-network suites in `functional_tests/test_real_*.py` need wifi credentials. See [`docs/wiring-wifi-credentials.md`](https://github.com/ChuMicro/ChuMicro/blob/main/docs/wiring-wifi-credentials.md) for the workspace-based and raw single-file paths. The library itself never reads TOML — it takes a `transport_factory` and goes; config wiring is application-layer.
|
|
129
|
+
|
|
130
|
+
## Contributing
|
|
131
|
+
|
|
132
|
+
Working on `chumicro-http-server` itself? Clone the [mono-repo](https://github.com/ChuMicro/ChuMicro) if you haven't already — the rest of the workflow assumes you're inside that workspace.
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
pip install -e .[test]
|
|
136
|
+
pytest tests/ # host-side tests
|
|
137
|
+
pytest functional_tests/ # on-device tests (needs a board registered in devices.yml)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Register a board before running functional tests: `chumicro-workspace add-device <id> --address <port>`.
|
|
141
|
+
|
|
142
|
+
## Docs
|
|
143
|
+
|
|
144
|
+
📖 **[Stable docs](https://chumicro.github.io/ChuMicro/http-server/stable/)** · **[Experimental docs](https://chumicro.github.io/ChuMicro/http-server/experimental/)**
|
|
145
|
+
|
|
146
|
+
## Find this library
|
|
147
|
+
|
|
148
|
+
- **PyPI:** [chumicro-http-server](https://pypi.org/project/chumicro-http-server/)
|
|
149
|
+
- **Bundle:** [ChuMicro-Bundle](https://github.com/ChuMicro/ChuMicro-Bundle/tree/main/chumicro_http_server) (CircuitPython & MicroPython)
|
|
150
|
+
- **Experimental bundle:** [ChuMicro-Bundle-Experimental](https://github.com/ChuMicro/ChuMicro-Bundle-Experimental/tree/main/chumicro_http_server)
|
|
151
|
+
- **Source:** [libraries/http_server](https://github.com/ChuMicro/ChuMicro/tree/main/libraries/http_server)
|
|
152
|
+
|
|
153
|
+
## License
|
|
154
|
+
|
|
155
|
+
[MIT](https://github.com/ChuMicro/ChuMicro/blob/main/LICENSE)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.18.0
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
## `chumicro_http_server`
|
|
4
|
+
|
|
5
|
+
::: chumicro_http_server
|
|
6
|
+
|
|
7
|
+
## `chumicro_http_server.streaming`
|
|
8
|
+
|
|
9
|
+
Opt-in submodule for streamed response bodies — import it explicitly.
|
|
10
|
+
A server that never streams never loads it.
|
|
11
|
+
|
|
12
|
+
::: chumicro_http_server.streaming
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
<div class="chumicro-footer" markdown>
|
|
17
|
+
|
|
18
|
+
[← Home](index.md)
|
|
19
|
+
|
|
20
|
+
[Source](https://github.com/ChuMicro/ChuMicro/tree/main/libraries/http_server) · \
|
|
21
|
+
[PyPI](https://pypi.org/project/chumicro-http_server/) · \
|
|
22
|
+
[Bundle](https://github.com/ChuMicro/ChuMicro-Bundle) · \
|
|
23
|
+
[Experimental Bundle](https://github.com/ChuMicro/ChuMicro-Bundle-Experimental)
|
|
24
|
+
|
|
25
|
+
</div>
|