Skip to content

feat(httputil): add HTTP request throttling for memory-constrained devices - #57

Closed
deadprogram wants to merge 1 commit into
mainfrom
net-http-throttle
Closed

feat(httputil): add HTTP request throttling for memory-constrained devices#57
deadprogram wants to merge 1 commit into
mainfrom
net-http-throttle

Conversation

@deadprogram

Copy link
Copy Markdown
Member

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

…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]>
Comment thread httputil/httputil.go
Comment on lines +29 to +48
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)
}
})
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This package is too small and too non-espradio specific for it to live here. Provide it in the example for users.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I will do that instead.

Comment thread netlink/ap.go
Comment on lines +148 to +156
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 },
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did you move this around?

Comment thread README.md
Comment on lines +368 to +383
## 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I don't think we need a package, much less in espradio.

Comment thread README.md
- 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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Memory constrained devices should not use net/http package.

@soypat

soypat commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants