24#if PC_ENABLE_METRICS || PC_ENABLE_STATS
31static void num_field(
char *dst,
size_t cap, uint32_t v)
33 pc_sb b = {dst, cap, 0,
true};
55static void tmpl_take_placeholder(uint8_t slot,
const char *&p,
TemplateVar resolver,
bool emit,
size_t &total)
57 const char *end = strstr(p + 2,
"}}");
58 size_t nlen = end ? (size_t)(end - (p + 2)) : 0;
59 if (!end || nlen > 32)
71 memcpy(name, p + 2, nlen);
73 const char *val = resolver ? resolver(name) : nullptr;
78 size_t vlen = strnlen(val, 0xFFFF);
88static size_t tmpl_walk(uint8_t slot,
const char *tmpl,
TemplateVar resolver,
bool emit)
94 if (p[0] ==
'{' && p[1] ==
'{')
96 tmpl_take_placeholder(slot, p, resolver, emit, total);
102 while (*p && !(p[0] ==
'{' && p[1] ==
'{'))
106 size_t rlen = (size_t)(p - run);
126 if (!pc_conn_active(slot_id))
133 size_t body_len = tmpl_walk(slot_id, tmpl, resolver,
false);
136 const char *cl = pc_resp_conn_hdr(slot_id, &keep);
156 if (!head && body_len > 0)
158 tmpl_walk(slot_id, tmpl, resolver,
true);
161 pc_resp_end(slot_id, code, (
int)body_len, keep);
182 if (!pc_conn_active(slot_id))
189 const char *cl = pc_resp_conn_hdr(slot_id, &keep);
202 cl =
"Connection: close\r\n";
214 pc_sb_put(&hb2, raw ?
"\r\n" :
"\r\nTransfer-Encoding: chunked\r\n");
223 pc_resp_end(slot_id, code, 0, keep);
235 chunk_send_pump(slot_id);
240void PC::chunk_send_pump(uint8_t slot_id)
252 if (!pc_conn_active(slot_id))
267 static const u16_t CHUNK_HDR_RESERVE = 8;
268 const u16_t FRAME = s.
raw ? 0 : 12;
278 size_t cap = (size_t)(avail - FRAME);
284 uint8_t *body = framed + CHUNK_HDR_RESERVE;
315 uint8_t *start = body - sn;
316 memcpy(start, digits, nd);
318 start[nd + 1] =
'\n';
338 if (slot_id >=
MAX_CONNS || name ==
nullptr || value ==
nullptr)
343 char *buf = _extra_hdr[slot_id];
346 pc_sb hb3 = {buf + used, room, 0,
true};
359void PC::set_cookie(uint8_t slot_id,
const char *name,
const char *value,
const char *attrs)
361 if (slot_id >=
MAX_CONNS || name ==
nullptr || value ==
nullptr)
366 char *buf = _extra_hdr[slot_id];
369 pc_sb cb = {buf + used, room, 0,
true};
374 if (attrs !=
nullptr && attrs[0] !=
'\0')
392 _extra_hdr[slot_id][0] =
'\0';
403 return PC_MIME_OCTET_STREAM;
407 const char *dot =
nullptr;
408 for (
const char *p = path; *p; p++)
419 if (!dot || dot[1] ==
'\0')
421 return PC_MIME_OCTET_STREAM;
423 const char *ext = dot + 1;
431 {
"html", PC_MIME_TEXT_HTML}, {
"htm", PC_MIME_TEXT_HTML}, {
"css",
"text/css"},
432 {
"js", PC_MIME_JAVASCRIPT}, {
"mjs", PC_MIME_JAVASCRIPT}, {
"json", PC_MIME_JSON},
433 {
"xml",
"application/xml"}, {
"txt", PC_MIME_TEXT_PLAIN}, {
"csv",
"text/csv"},
434 {
"svg",
"image/svg+xml"}, {
"png",
"image/png"}, {
"jpg",
"image/jpeg"},
435 {
"jpeg",
"image/jpeg"}, {
"gif",
"image/gif"}, {
"ico",
"image/x-icon"},
436 {
"webp",
"image/webp"}, {
"wasm",
"application/wasm"}, {
"woff",
"font/woff"},
437 {
"woff2",
"font/woff2"}, {
"ttf",
"font/ttf"}, {
"pdf",
"application/pdf"},
438 {
"gz",
"application/gzip"},
440 for (
size_t i = 0; i <
sizeof(table) /
sizeof(table[0]); i++)
443 const char *b = table[i].ext;
447 char ca = (*a >=
'A' && *a <=
'Z') ? (
char)(*a + 32) : *a;
457 if (eq && *a ==
'\0' && *b ==
'\0')
459 return table[i].type;
462 return PC_MIME_OCTET_STREAM;
484static StatsCtx s_stats;
486static const char *stats_var(
const char *name)
488 if (!strcmp(name,
"uptime_ms"))
490 return s_stats.uptime;
492 if (!strcmp(name,
"requests"))
494 return s_stats.requests;
496 if (!strcmp(name,
"http_2xx"))
500 if (!strcmp(name,
"http_4xx"))
504 if (!strcmp(name,
"http_5xx"))
508 if (!strcmp(name,
"active_conns"))
510 return s_stats.active;
515 if (!strcmp(name,
"free_heap"))
522void PC::stats(uint8_t slot_id)
526 unsigned long up = millis();
528 uint32_t heap = ESP.getFreeHeap();
534 num_field(s_stats.uptime,
sizeof(s_stats.uptime), (uint32_t)up);
535 num_field(s_stats.requests,
sizeof(s_stats.requests), (uint32_t)_stat_requests);
536 num_field(s_stats.n2xx,
sizeof(s_stats.n2xx), (uint32_t)_stat_2xx);
537 num_field(s_stats.n4xx,
sizeof(s_stats.n4xx), (uint32_t)_stat_4xx);
538 num_field(s_stats.n5xx,
sizeof(s_stats.n5xx), (uint32_t)_stat_5xx);
539 num_field(s_stats.active,
sizeof(s_stats.active), (uint32_t)(active < 0 ? 0 : active));
540 num_field(s_stats.heap,
sizeof(s_stats.heap), heap);
566static MetricsCtx s_metrics;
568static const char *metrics_var(
const char *name)
570 if (!strcmp(name,
"uptime_seconds"))
572 return s_metrics.uptime;
574 if (!strcmp(name,
"requests_total"))
576 return s_metrics.requests;
578 if (!strcmp(name,
"resp_2xx"))
580 return s_metrics.n2xx;
582 if (!strcmp(name,
"resp_4xx"))
584 return s_metrics.n4xx;
586 if (!strcmp(name,
"resp_5xx"))
588 return s_metrics.n5xx;
590 if (!strcmp(name,
"active_conns"))
592 return s_metrics.active;
594 if (!strcmp(name,
"max_conns"))
596 return s_metrics.max;
598 if (!strcmp(name,
"free_heap"))
600 return s_metrics.heap;
602 if (!strcmp(name,
"min_free_heap"))
604 return s_metrics.minheap;
606 if (!strcmp(name,
"heap_size"))
608 return s_metrics.heapsize;
615 if (!strcmp(name,
"max_alloc_heap"))
617 return s_metrics.maxalloc;
622void PC::metrics(uint8_t slot_id)
626 unsigned long up = millis();
628 uint32_t heap = ESP.getFreeHeap();
629 uint32_t min_heap = ESP.getMinFreeHeap();
630 uint32_t heap_size = ESP.getHeapSize();
631 uint32_t max_alloc = ESP.getMaxAllocHeap();
634 uint32_t min_heap = 0;
635 uint32_t heap_size = 0;
636 uint32_t max_alloc = 0;
639 num_field(s_metrics.uptime,
sizeof(s_metrics.uptime), (uint32_t)(up / 1000UL));
640 num_field(s_metrics.requests,
sizeof(s_metrics.requests), (uint32_t)_stat_requests);
641 num_field(s_metrics.n2xx,
sizeof(s_metrics.n2xx), (uint32_t)_stat_2xx);
642 num_field(s_metrics.n4xx,
sizeof(s_metrics.n4xx), (uint32_t)_stat_4xx);
643 num_field(s_metrics.n5xx,
sizeof(s_metrics.n5xx), (uint32_t)_stat_5xx);
644 num_field(s_metrics.active,
sizeof(s_metrics.active), (uint32_t)(active < 0 ? 0 : active));
645 num_field(s_metrics.max,
sizeof(s_metrics.max), (uint32_t)
MAX_CONNS);
646 num_field(s_metrics.heap,
sizeof(s_metrics.heap), heap);
647 num_field(s_metrics.minheap,
sizeof(s_metrics.minheap), min_heap);
648 num_field(s_metrics.heapsize,
sizeof(s_metrics.heapsize), heap_size);
649 num_field(s_metrics.maxalloc,
sizeof(s_metrics.maxalloc), max_alloc);
void clear_response_headers(uint8_t slot_id)
Discard any headers/cookies queued for this slot.
void send_chunked(uint8_t slot_id, int code, const char *content_type, ChunkSource source, void *ctx=nullptr)
Stream a response body of unknown length via chunked transfer.
static const char * mime_type(const char *path)
Guess a Content-Type from a path's file extension.
void add_response_header(uint8_t slot_id, const char *name, const char *value)
Queue a custom response header for the next send on this slot.
void send_template(uint8_t slot_id, int code, const char *content_type, const char *tmpl, TemplateVar resolver)
Send a response body with {{name}} placeholders substituted.
void set_cookie(uint8_t slot_id, const char *name, const char *value, const char *attrs=nullptr)
Queue a Set-Cookie response header for the next send on this slot.
Base-16 conversion between raw bytes and their ASCII digits.
uint8_t pc_hex_u32(uint32_t v, char *out)
Write v into out as lowercase hex digits, most significant first, and return the digit count (1....
HttpReq http_pool[CONN_POOL_SLOTS]
Pool of parser contexts, one per connection-pool slot (incl. reserved dispatch slots).
@ HTTP_11
HTTP/1.1 - persistent connection by default.
Shared HTTP Content-Type ("MIME") string constants (one source of truth).
void http_reset(uint8_t slot_id)
Reset the HTTP parser for a connection slot.
const char * status_text(int code)
Convert an HTTP status code to its standard reason phrase.
bool req_is_head(uint8_t slot_id)
True if the request in slot slot_id used the HEAD method (send headers, no body).
Layer 7 (Application) - public HTTP routing API.
size_t(* ChunkSource)(uint8_t *buf, size_t cap, void *ctx)
Source callback that produces a chunked response body incrementally.
const char *(* TemplateVar)(const char *name)
Resolver for {{name}} template placeholders used by send_template().
#define CHUNK_BUF_SIZE
Per-chunk staging buffer for send_chunked()'s ChunkSource (max bytes a source produces per call,...
#define RESP_HDR_BUF_SIZE
Stack buffer for HTTP response header lines in send() / send_empty() / send_unauth() / serve_file().
#define EXTRA_HDR_BUF_SIZE
Per-connection buffer for app-supplied custom response headers and cookies.
Library-private declarations shared between protocore.cpp and the src/server/*.cpp request-handler tr...
Bounded no-heap string builder that fails closed on overflow (one shared copy).
size_t pc_sb_finish(pc_sb *b)
NUL-terminate and return the built length, or 0 if the build overflowed.
void pc_sb_lit(pc_sb *b, const char(&s)[N])
Append a string literal. The array parameter deduces N, so the length is a constant.
void pc_sb_put(pc_sb *b, const char *s)
Append NUL-terminated s; leaves the buffer untouched and clears ok if it would not fit.
void pc_sb_u32(pc_sb *b, uint32_t v)
Append v as decimal (no leading zeros; "0" for zero).
ChunkSource source
body generator (active==false means none).
int status
response status, for note_response.
void * ctx
caller state passed to source (must outlive the send).
bool raw
HTTP/1.0 client: stream the body unframed, close-delimited (no chunk wrapping).
bool active
a chunked response is in progress on this slot.
int total
body bytes emitted so far (excludes framing).
bool keep
keep-alive vs close at completion.
HttpVersion version
Protocol version parsed from the request line.
ChunkSend chunk[MAX_CONNS]
Bump-append target; ok latches false once an append would overflow cap.
void pc_conn_touch_active(uint8_t slot_id)
Refresh slot's idle-timeout timestamp while a response body is in flight.
void pc_conn_flush(uint8_t slot)
Flush queued bytes / finish the send on slot (TLS-aware).
u16_t pc_conn_sndbuf(uint8_t slot)
Bytes that can currently be queued for sending on slot.
bool pc_conn_send(uint8_t slot, const void *data, u16_t len)
Send len bytes on connection slot (copies data; TLS-aware).
uint8_t pc_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.
const char PC_STATS_JSON[]
Runtime stats JSON (rendered via send_template); add/rename fields to taste.
const char PC_METRICS_PROM[]
All available Prometheus metrics; comment a value line out with a leading # to drop it.
Layer 7 (Application) - embedded web assets generated from web_assets/input/.