pub struct Server<const P: usize = DEFAULT_HANDLER_TASKS_COUNT, const B: usize = DEFAULT_BUF_SIZE, const N: usize = DEFAULT_MAX_HEADERS_COUNT>(/* private fields */);Expand description
An HTTP server that can handle multiple requests concurrently.
The server needs an implementation of edge_nal::TcpAccept to accept incoming connections.
Implementations§
Source§impl<const P: usize, const B: usize, const N: usize> Server<P, B, N>
impl<const P: usize, const B: usize, const N: usize> Server<P, B, N>
Sourcepub async fn run<A, H>(
&mut self,
keepalive_timeout_ms: Option<u32>,
acceptor: A,
handler: H,
) -> Result<(), Error<A::Error>>
pub async fn run<A, H>( &mut self, keepalive_timeout_ms: Option<u32>, acceptor: A, handler: H, ) -> Result<(), Error<A::Error>>
Run the server with the specified acceptor and handler
A note on timeouts:
- The function does NOT - by default - establish any timeouts on the IO operations except
an optional timeout on idle connections, so that they can be closed.
It is up to the caller to wrap the acceptor type with
edge_nal::WithTimeoutto establish timeouts on the socket produced by the acceptor. - Similarly, the function does NOT establish any timeouts on the complete request-response cycle.
It is up to the caller to wrap their complete or partial handling logic with
edge_nal::with_timeout, or its whole handler withedge_nal::WithTimeout, so as to establish a global or semi-global request-response timeout.
A note on concurrent connection acceptance:
- All handler tasks concurrently call
accept()to maximize connection acceptance capacity. - This is critical for TCP stacks without accept queues (e.g., smoltcp/embassy-net), where
incoming connections can only be accepted if at least one task is actively waiting in
accept(). - When a task is busy handling a request, other tasks remain available to accept new connections.
- Threading safety: The acceptor must be safe to use from multiple async tasks.
For single-threaded async executors (e.g., embassy-executor on embedded platforms),
acceptors using non-Sync types like
Cellfor internal state are safe. For multi-threaded executors, the acceptor’s internal state must be properly synchronized (e.g., using atomics or locks).
Consider using run_with_socket_queue() instead for better connection handling
with TCP stacks that lack accept queues (e.g., smoltcp/embassy-net). The socket queue architecture
decouples connection acceptance from HTTP processing, allowing connections to be accepted even when
all worker tasks are busy.
Parameters:
keepalive_timeout_ms: An optional timeout in milliseconds for detecting an idle keepalive connection that should be closed. If not provided, the function will not close idle connections and the connection - in the absence of other timeouts - will remain active forever.acceptor: An implementation ofedge_nal::TcpAcceptto accept incoming connectionshandler: An implementation ofHandlerto handle incoming requests If not provided, a default timeout of 50 seconds is used.
Sourcepub async fn run_with_socket_queue<A, H, const Q: usize>(
&mut self,
keepalive_timeout_ms: Option<u32>,
acceptor: A,
handler: H,
) -> Result<(), Error<A::Error>>
pub async fn run_with_socket_queue<A, H, const Q: usize>( &mut self, keepalive_timeout_ms: Option<u32>, acceptor: A, handler: H, ) -> Result<(), Error<A::Error>>
Run the server with a socket queue architecture (recommended for smoltcp/embassy-net)
This method addresses the limitation of TCP stacks without accept queues (e.g., smoltcp/embassy-net) by using a signal-based coordination mechanism between acceptor and worker tasks. This ensures that the number of sockets in use never exceeds the available socket pool size, preventing resource exhaustion.
§Type Parameters
Q: Number of acceptor tasks and maximum sockets that can be allocated simultaneously (default: 8)
§Important Constraints
CRITICAL: The Q parameter must satisfy these constraints or the function will panic:
Qmust be less than or equal to the number of sockets in your smoltcp/embassy-net socket pool- If
Qexceeds the socket pool size, accept() calls will fail and cause panics
- If
Qshould be greater than or equal toPfor the architecture to provide benefits- If
Q < P, you lose the advantage of decoupling acceptance from processing - Recommended:
Q >= P(e.g., Q=8 with P=4)
- If
§Timeout Configuration
The function does NOT establish timeouts by default (except optional keepalive timeout):
- Wrap the acceptor with
edge_nal::WithTimeoutto set socket-level timeouts - Wrap handler logic with
edge_nal::with_timeoutfor request-response timeouts
§Parameters
keepalive_timeout_ms: Optional timeout in milliseconds for idle keepalive connectionsacceptor: An implementation ofedge_nal::TcpAcceptto accept incoming connectionshandler: An implementation ofHandlerto handle incoming requests