feat(httputil): add HTTP request throttling for memory-constrained devices - #57
feat(httputil): add HTTP request throttling for memory-constrained devices#57deadprogram wants to merge 1 commit into
Conversation
…vices Introduce httputil.ThrottleHandler which uses a non-blocking channel semaphore to cap concurrent HTTP request processing. Excess requests are shed immediately with 503 rather than queued, keeping heap usage deterministic on ESP32-class hardware. - Add httputil package with ThrottleHandler and full test suite - Expose Esplink.TCPPoolSize() so examples can match the throttle limit to the actual TCP listener pool size - Update hello, webserver, and apwebserver examples to use throttling - Extract TCPPoolConfig into a local var in netlink so pool size is accessible after init - Document httputil in README with usage example Signed-off-by: deadprogram <[email protected]>
8a51509 to
b041512
Compare
| func ThrottleHandler(maxConn int, handler http.Handler) http.Handler { | ||
| if handler == nil { | ||
| handler = http.DefaultServeMux | ||
| } | ||
| if maxConn <= 0 { | ||
| maxConn = 1 | ||
| } | ||
| sem := make(chan struct{}, maxConn) | ||
| return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
| select { | ||
| case sem <- struct{}{}: | ||
| defer func() { <-sem }() | ||
| w.Header().Set("Connection", "close") | ||
| handler.ServeHTTP(w, r) | ||
| default: | ||
| w.Header().Set("Connection", "close") | ||
| http.Error(w, "busy", http.StatusServiceUnavailable) | ||
| } | ||
| }) | ||
| } |
There was a problem hiding this comment.
This package is too small and too non-espradio specific for it to live here. Provide it in the example for users.
There was a problem hiding this comment.
Good point, I will do that instead.
| poolCfg := xnet.TCPPoolConfig{ | ||
| PoolSize: 2, | ||
| QueueSize: 4, | ||
| TxBufSize: 4096, | ||
| RxBufSize: 1024, | ||
| EstablishedTimeout: 10 * time.Second, | ||
| ClosingTimeout: 5 * time.Second, | ||
| NewBackoff: func() lneto.BackoffStrategy { return pollBackoff }, | ||
| } |
There was a problem hiding this comment.
why did you move this around?
| ## httputil | ||
|
|
||
| The `httputil` package provides HTTP utilities for memory-constrained devices. The main utility is `ThrottleHandler`, which limits concurrent HTTP request processing to a fixed number of slots. When all slots are in use, excess requests are immediately rejected with `503 Service Unavailable` instead of being queued — this prevents unbounded goroutine/memory growth on ESP32-class hardware. | ||
|
|
||
| ```go | ||
| import "tinygo.org/x/espradio/httputil" | ||
|
|
||
| mux := http.NewServeMux() | ||
| mux.HandleFunc("/", handler) | ||
|
|
||
| // Allow at most 2 concurrent requests; excess get 503. | ||
| err := http.ListenAndServe(":80", httputil.ThrottleHandler(2, mux)) | ||
| ``` | ||
|
|
||
| All responses include `Connection: close` to discourage HTTP/1.1 keep-alive pipelining which consumes memory for idle connections. | ||
|
|
There was a problem hiding this comment.
yeah, I don't think we need a package, much less in espradio.
| - TCP and UDP Berkeley sockets API | ||
| - Raw Ethernet frame send/receive | ||
| - MQTT client support (uses [`natiu-mqtt`](https://github.com/soypat/natiu-mqtt)) | ||
| - HTTP concurrency throttling for memory-constrained devices (`httputil` package) |
There was a problem hiding this comment.
Memory constrained devices should not use net/http package.
|
I strongly encourage we stop acting like net/http is a goal. Using it as one would normally guarantees crashes on esp32 and raspberry pi pico. It's already hard enough to run an lneto server indefinetely. Let's stop wasting effort on squaring the circle. I'll provide a HTTP example using lneto's httphi soon as an alternative to stdlib |
Introduce httputil.ThrottleHandler which uses a non-blocking channel semaphore to cap concurrent HTTP request processing. Excess requests are shed immediately with 503 rather than queued, keeping heap usage deterministic on ESP32-class hardware.