DeterministicESPAsyncWebServer 1.2.0
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
BasicExample.ino
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file BasicExample.ino
6 * @brief Minimal example demonstrating core DetWebServer features.
7 *
8 * Demonstrates:
9 * - WiFi connection via init_wifi_physical() + wifi_ready() polling
10 * - Checking begin() return value for heap exhaustion
11 * - CORS headers via set_cors()
12 * - Exact route: GET /api/status → JSON with HTTP version field
13 * - Wildcard route: GET /files/* → echoes the requested path
14 * - POST route with body access
15 * - Custom 404 handler via on_not_found()
16 * - Query parameter access via http_get_query()
17 * - Request header access via http_get_header()
18 *
19 * The framework handles these automatically — no application code needed:
20 * - 400 Bad Request — RFC 7230 character violation in method/path/headers
21 * - 413 Payload Too Large — Content-Length exceeds BODY_BUF_SIZE
22 * - 414 URI Too Long — path exceeds MAX_PATH_LEN
23 * - 501 Not Implemented — Transfer-Encoding header present (chunked not supported)
24 *
25 * Flash to any ESP32 board. Open the Serial Monitor at 115200 baud to
26 * see the assigned IP address, then use curl or a browser to test routes.
27 *
28 * Example curl commands:
29 * curl http://<ip>/api/status
30 * curl http://<ip>/api/status?verbose=1
31 * curl -X POST http://<ip>/api/echo -d "hello world"
32 * curl http://<ip>/files/image.png
33 */
34
35#include "DeterministicESPAsyncWebServer.h"
36#include "network_drivers/physical.h"
37#include <WiFi.h>
38
39static const char *SSID = "YOUR_SSID";
40static const char *PASSWORD = "YOUR_PASSWORD";
41
42DetWebServer server;
43
44static int request_count = 0;
45
46// GET /api/status
47// Returns a JSON object with a rolling request counter and the HTTP version
48// the client used. Demonstrates req->version and query param access.
49void handle_status(uint8_t slot_id, HttpReq *req)
50{
51 request_count++;
52
53 const char *version_str;
54 switch (req->version)
55 {
56 case HTTP_11:
57 version_str = "1.1";
58 break;
59 case HTTP_10:
60 version_str = "1.0";
61 break;
62 default:
63 version_str = "?";
64 break;
65 }
66
67 // ?verbose=1 includes the path in the response
68 const char *verbose = http_get_query(req, "verbose");
69
70 char body[160];
71 if (verbose && strcmp(verbose, "1") == 0)
72 snprintf(body, sizeof(body), "{\"status\":\"ok\",\"count\":%d,\"http\":\"%s\",\"path\":\"%s\"}", request_count,
73 version_str, req->path);
74 else
75 snprintf(body, sizeof(body), "{\"status\":\"ok\",\"count\":%d,\"http\":\"%s\"}", request_count, version_str);
76
77 server.send(slot_id, 200, "application/json", body);
78}
79
80// POST /api/echo
81// Echoes the request body back as plain text.
82// req->body is always null-terminated; body_len holds the byte count.
83void handle_echo(uint8_t slot_id, HttpReq *req)
84{
85 server.send(slot_id, 200, "text/plain", (const char *)req->body);
86}
87
88// GET /files/* (wildcard)
89// A placeholder for static file serving. req->path holds the full request
90// path, not just the suffix after the wildcard prefix.
91void handle_files(uint8_t slot_id, HttpReq *req)
92{
93 // Demonstrate header lookup — check if the client prefers gzip
94 const char *accept_enc = http_get_header(req, "Accept-Encoding");
95 bool wants_gzip = accept_enc && strstr(accept_enc, "gzip") != nullptr;
96
97 char msg[MAX_PATH_LEN + 64];
98 snprintf(msg, sizeof(msg), "Requested: %s%s", req->path, wants_gzip ? " (gzip accepted)" : "");
99 server.send(slot_id, 200, "text/plain", msg);
100}
101
102// Fallback for any unmatched route
103void handle_not_found(uint8_t slot_id, HttpReq *req)
104{
105 char msg[MAX_PATH_LEN + 32];
106 snprintf(msg, sizeof(msg), "Not found: %s", req->path);
107 server.send(slot_id, 404, "text/plain", msg);
108}
109
110void setup()
111{
112 Serial.begin(115200);
113
114 init_wifi_physical(SSID, PASSWORD);
115 Serial.print("Connecting to WiFi");
116 while (!wifi_ready())
117 {
118 delay(250);
119 Serial.print('.');
120 }
121 Serial.print("\nIP: ");
122 Serial.println(WiFi.localIP());
123
124 // Allow cross-origin requests from any origin (swap "*" for your
125 // frontend origin in production).
126 server.set_cors("*");
127
128 server.on("/api/status", HTTP_GET, handle_status);
129 server.on("/api/echo", HTTP_POST, handle_echo);
130 server.on("/files/*", HTTP_GET, handle_files);
131 server.on_not_found(handle_not_found);
132
133 int32_t result = server.begin(80);
134 if (result < 0)
135 {
136 // Negative return means we ran out of heap.
137 // abs(result) is how many bytes were needed.
138 Serial.printf("begin() failed — need %d more heap bytes\n", -result);
139 return;
140 }
141 Serial.println("Server started on port 80");
142}
143
144void loop()
145{
146 // Drives the full pipeline each iteration:
147 // 1. Timeout sweep (force-closes idle connections)
148 // 2. Event queue drain (TCP connect/data/disconnect events)
149 // 3. Route dispatch for completed requests
150 // 4. Auto-sends 400 / 413 / 414 for parser error states
151 server.handle();
152}