29#include "freertos/FreeRTOS.h"
30#include "freertos/queue.h"
31#include "freertos/task.h"
49#if DETWS_ENABLE_OBSERVABILITY
58 DetConnEventCb conn_event_cb =
nullptr;
59 std::atomic<uint32_t> ctr[8];
63void det_conn_on_event(DetConnEventCb cb)
65 s_obs.conn_event_cb = cb;
68DetConnCounters det_conn_counters()
71 c.accepts = s_obs.ctr[0].load(std::memory_order_relaxed);
72 c.closes_remote = s_obs.ctr[1].load(std::memory_order_relaxed);
73 c.closes_local = s_obs.ctr[2].load(std::memory_order_relaxed);
74 c.closes_error = s_obs.ctr[3].load(std::memory_order_relaxed);
75 c.closes_timeout = s_obs.ctr[4].load(std::memory_order_relaxed);
76 c.closes_abort = s_obs.ctr[5].load(std::memory_order_relaxed);
77 c.backpressure = s_obs.ctr[6].load(std::memory_order_relaxed);
78 c.defer_drops = s_obs.ctr[7].load(std::memory_order_relaxed);
87void det_conn_counters_reset()
89 for (
int i = 0; i < 8; i++)
90 s_obs.ctr[i].store(0, std::memory_order_relaxed);
93static void obs_bump(DetConnReason reason)
98 case DetConnReason::DET_CONN_R_ACCEPT:
101 case DetConnReason::DET_CONN_R_CLOSE_REMOTE:
104 case DetConnReason::DET_CONN_R_CLOSE_LOCAL:
107 case DetConnReason::DET_CONN_R_ERROR:
110 case DetConnReason::DET_CONN_R_TIMEOUT:
113 case DetConnReason::DET_CONN_R_ABORT:
116 case DetConnReason::DET_CONN_R_BACKPRESSURE:
119 case DetConnReason::DET_CONN_R_DEFER_DROP:
122 case DetConnReason::DET_CONN_R_DRAINED:
127 s_obs.ctr[idx].fetch_add(1, std::memory_order_relaxed);
134void detws_obs_transition(uint8_t slot,
ConnState olds,
ConnState news, DetConnReason reason)
137 if (s_obs.conn_event_cb)
138 s_obs.conn_event_cb(slot, olds, news, reason);
142void detws_obs_notice(uint8_t slot,
ConnState st, DetConnReason reason)
145 if (s_obs.conn_event_cb)
146 s_obs.conn_event_cb(slot, st, st, reason);
168static void closing_check(uint8_t slot,
struct tcp_pcb *pcb);
171#include "lwip/priv/tcpip_priv.h"
201static inline bool on_tcpip_thread()
203#if defined(LWIP_TCPIP_CORE_LOCKING) && LWIP_TCPIP_CORE_LOCKING
211 return sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER);
222 struct tcpip_api_call_data
base;
240static bool pcb_still_bound(
const struct tcp_pcb *pcb)
253static err_t det_tcp_do(
struct tcpip_api_call_data *c)
258 s_tp.
tcpip_task = xTaskGetCurrentTaskHandle();
265 if (!pcb_still_bound(k->
pcb))
304 det_tls_conn_end(k->
slot);
306 if (tcp_close(k->
pcb) != ERR_OK)
313 tcp_arg(k->
pcb,
nullptr);
316 closing_check(k->
slot, k->
pcb);
319 tcp_recved(k->
pcb, k->
len);
325static inline err_t det_tcp_marshal(
DetTcpOp op, uint8_t slot,
struct tcp_pcb *pcb,
const void *data, u16_t len,
329 memset(&k, 0,
sizeof(k));
339 if (on_tcpip_thread())
342 tcpip_api_call(det_tcp_do, &k.
base);
372 return det_tls_write(slot, data, len) >= 0;
374 return tcp_write(
conn_pool[slot].pcb, data, len, TCP_WRITE_FLAG_COPY) == ERR_OK;
389 return det_tls_write(slot, data, len) >= 0;
391 if (tcp_write(
conn_pool[slot].pcb, data, len, TCP_WRITE_FLAG_COPY) != ERR_OK)
403 u16_t avail = tcp_sndbuf(pcb);
408 avail = (avail > 64) ? (u16_t)(avail - 64) : 0;
445 tcp_recved(c->
pcb, (u16_t)consumed);
460 err_t e = tcp_write(pcb, data, len, TCP_WRITE_FLAG_COPY);
472 struct tcp_pcb *pcb = c->
pcb;
489 det_tls_conn_end(slot);
491 if (tcp_close(pcb) != ERR_OK)
501 struct tcp_pcb *pcb = c->
pcb;
507 det_tls_conn_free(slot);
523 tcp_arg(pcb,
nullptr);
544static void closing_finalize(uint8_t slot,
struct tcp_pcb *pcb)
549 det_tls_conn_end(slot);
555 tcp_arg(pcb,
nullptr);
556 if (tcp_close(pcb) != ERR_OK)
564static void closing_check(uint8_t slot,
struct tcp_pcb *pcb)
568 if (!pcb || pcb->snd_queuelen == 0)
569 closing_finalize(slot, pcb);
579 struct tcp_pcb *pcb = c->
pcb;
583 DetConnReason::DET_CONN_R_CLOSE_LOCAL);
590 closing_check(slot_id, pcb);
616 static const TcpConn blank = {};
662 return ip4_addr_get_u32(ip_2_ip4(&conn->
pcb->remote_ip));
679 memcpy(b, ip_2_ip6(ra)->addr, 16);
686 uint32_t be = ip4_addr_get_u32(ip_2_ip4(ra));
687 *out =
det_ip_from_v4_octets((uint8_t)be, (uint8_t)(be >> 8), (uint8_t)(be >> 16), (uint8_t)(be >> 24));
733 if (slot->
owner != worker_id)
744 struct tcp_pcb *cpcb = slot->
pcb;
753 DetConnReason::DET_CONN_R_DRAINED);
762 struct tcp_pcb *pcb = slot->
pcb;
776 DetConnReason::DET_CONN_R_TIMEOUT);
809 tcp_recved(tpcb, p->tot_len);
827 tcp_arg(tpcb,
nullptr);
828 if (tcp_close(tpcb) != ERR_OK)
831 DetConnReason::DET_CONN_R_CLOSE_REMOTE);
868 for (
struct pbuf *q = p; q !=
nullptr; q = q->next)
869 head = det_ring_write_span(slot->
rx_buffer,
RX_BUF_SIZE, head, (
const uint8_t *)q->payload, q->len);
871 size_t bytes_copied = p->tot_len;
880 if (bytes_copied > 0)
903 closing_check(slot->
id, tpcb);
943 DetConnReason::DET_CONN_R_DRAINED);
#define CONN_TIMEOUT_MS
Compile-time default for connection idle timeout in milliseconds.
#define DETWS_CLOSING_TIMEOUT_MS
Upper bound (ms) a slot may dwell in ConnState::CONN_CLOSING after a graceful close before the idle s...
#define RX_BUF_SIZE
Ring-buffer capacity in bytes per connection slot.
#define MAX_CONNS
Maximum simultaneous TCP connections (fixed static pool; ~3.95 KB of internal RAM per slot).
static void check_timeouts(int worker_id=0)
Scan the pool and force-close connections idle for > conn_timeout_ms.
static void pool_init(const WebServerConfig *cfg=nullptr)
Initialize the connection pool and store the runtime config.
static uint32_t conn_timeout_ms
Runtime connection-idle timeout in milliseconds.
static void stop()
Abort all active connections and reset the pool to ConnState::CONN_FREE.
Pluggable monotonic clock for all library timing.
uint32_t detws_millis(void)
The library's monotonic time at 1000 Hz (milliseconds).
DetIp det_ip_from_v6_bytes(const uint8_t bytes[16])
Build a v6 DetIp from 16 address bytes in network (big-endian) order.
DetIp det_ip_from_v4_octets(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
Build a v4 DetIp from four octets (a.b.c.d).
@ DET_IP_NONE
empty / unparsed
bool listener_enqueue(uint8_t listener_id, const TcpEvt *evt)
Post evt to the queue owned by listener listener_id.
A v4 or v6 address in network (big-endian) byte order.
DetIpFamily family
address family tag
err_t result
outcome of the op (DetTcpOp::DET_OP_SEND: whether the write was queued)
bool flush
DetTcpOp::DET_OP_SEND: also tcp_output() after a successful write (coalesced write+flush)
struct tcpip_api_call_data base
A single TCP connection context.
struct tcp_pcb * pcb
lwIP PCB; null when slot is free.
uint32_t last_activity_ms
millis() timestamp of last TX/RX event.
uint8_t id
Fixed slot index (0 … MAX_CONNS-1).
uint8_t rx_buffer[RX_BUF_SIZE]
Ring buffer storage.
uint8_t owner
Worker that owns this slot (round-robin at accept). Always 0 at N=1.
uint8_t listener_id
Index into listener_pool[]; set at accept time.
DetAtomic< size_t > rx_tail
Consumer read index (worker context).
DetAtomic< size_t > rx_head
Producer write index (lwIP/tcpip context).
DetAtomic< ConnState > state
Lifecycle state; acquire/release for inter-task visibility.
uint8_t tls
Non-zero when this connection is TLS (set at accept time).
Event record posted from lwIP callbacks to the session layer.
Runtime-tunable server parameters.
uint32_t det_conn_remote_ip(uint8_t slot)
Raw source IPv4 of the connection in slot, or 0 if the slot has no active pcb (or on host builds)....
bool det_conn_send(uint8_t slot, const void *data, u16_t len)
Send len bytes on connection slot (copies data; TLS-aware).
void det_conn_flush(uint8_t slot)
Flush queued bytes / finish the send on slot (TLS-aware).
void det_conn_begin_close(uint8_t slot_id)
Begin a graceful close that dwells in ConnState::CONN_CLOSING until the peer ACKs.
uint32_t detws_ap_ip
softAP IPv4 address (network byte order) for STA/AP interface tagging.
u16_t det_conn_sndbuf(uint8_t slot)
Bytes that can currently be queued for sending on slot.
void det_conn_close(uint8_t slot)
Close connection slot gracefully (tcp_close), aborting if the FIN cannot be queued....
void det_conn_ack_consumed(uint8_t slot)
Reopen the TCP receive window by however much slot has drained.
TcpConn conn_pool[CONN_POOL_SLOTS]
Static pool of connection contexts. Defined in tcp.cpp. Sized CONN_POOL_SLOTS: MAX_CONNS TCP slots pl...
bool det_conn_send_flush(uint8_t slot, const void *data, u16_t len)
Send len bytes on slot and flush in a single tcpip_thread round-trip.
err_t lowlevel_recv_cb(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
lwIP receive callback - fires when data arrives on a connection.
void det_lwip_to_detip(const ip_addr_t *ra, DetIp *out)
A stable per-peer 32-bit identity key for slot (the v4 address, or an FNV-1a hash of a v6 address)....
bool det_conn_raw_send(struct tcp_pcb *pcb, const void *data, u16_t len)
Write raw bytes straight to pcb (no TLS), context-safe.
err_t lowlevel_sent_cb(void *arg, struct tcp_pcb *tpcb, u16_t len)
lwIP sent callback - fires after the stack acknowledges sent bytes.
void lowlevel_err_cb(void *arg, err_t err)
lwIP error callback - fires when the stack detects a fatal error.
bool det_conn_remote_addr(uint8_t slot, DetIp *out)
The connected peer's address as a family-tagged DetIp (IPv4 or IPv6).
void det_conn_detach(struct tcp_pcb *pcb)
Detach pcb from its slot's lwIP callbacks before the slot is freed.
void det_conn_touch_active(uint8_t slot_id)
Refresh slot's idle-timeout timestamp while a response body is in flight.
void det_conn_abort_slot(uint8_t slot)
Hard-abort connection slot (RST) for a fatal condition. The transport owns the teardown order: free t...
void det_conn_abort(struct tcp_pcb *pcb)
Hard-abort pcb (RST) for a fatal condition; no graceful FIN.
uint8_t det_conn_active_count()
Number of server connection slots currently in the CONN_ACTIVE state.
Layer 4 (Transport) - TCP connection pool, ring buffers, and lwIP integration.
TcpConn conn_pool[CONN_POOL_SLOTS]
Static pool of connection contexts. Defined in tcp.cpp. Sized CONN_POOL_SLOTS: MAX_CONNS TCP slots pl...
void det_conn_detach(struct tcp_pcb *pcb)
Detach pcb from its slot's lwIP callbacks before the slot is freed.
#define DETWS_OBS_NOTICE(slot, st, reason)
@ EVT_DATA
Data received; bytes are already in the ring buffer.
@ EVT_DISCONNECT
Remote peer closed the connection gracefully.
@ EVT_ERROR
lwIP reported an error (PCB may already be freed).
#define DETWS_OBS_TRANSITION(slot, olds, news, reason)
ConnState
Lifecycle state of a connection pool slot.
@ CONN_CLOSING
FIN sent; waiting for final ACK (reserved).
@ CONN_ACTIVE
Live connection; PCB is valid.
@ CONN_FREE
Slot is available; no PCB is attached.
void det_conn_abort(struct tcp_pcb *pcb)
Hard-abort pcb (RST) for a fatal condition; no graceful FIN.
Deterministic TLS engine: mbedTLS over a static memory pool (DETWS_ENABLE_TLS).
void detws_worker_wake(int worker_id)
Wake worker worker_id so it services a freshly-queued event now.
Layer 5 (Session) - server worker identity.