mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-25 12:04:47 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b766c65615 | ||
|
|
8296b88fb6 | ||
|
|
a5efbf0592 | ||
|
|
37c98dd893 | ||
|
|
770a4ff27e | ||
|
|
b6560306ba | ||
|
|
5b39c93478 | ||
|
|
086008b716 | ||
|
|
35eb50a1e6 | ||
|
|
431e35ae8c | ||
|
|
25e2a99d8e |
No files matched your search
@@ -637,7 +637,6 @@ jobs:
|
||||
example:
|
||||
- path: micropython/esp32
|
||||
- path: esp32/uart-bridge
|
||||
- path: esp32/minimal
|
||||
name: ${{ matrix.example.path }}
|
||||
env:
|
||||
GO: 0
|
||||
|
||||
@@ -186,7 +186,6 @@ jobs:
|
||||
matrix:
|
||||
example:
|
||||
- path: esp32/uart-bridge
|
||||
- path: esp32/minimal
|
||||
name: ${{ matrix.example.path }}
|
||||
env:
|
||||
GO: 0
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
# Mongoose - Embedded Network Library
|
||||
# Mongoose: a full-stack embedded networking library
|
||||
|
||||
Mongoose is a full-stack embedded networking library for C/C++: a TCP/IP stack
|
||||
with drivers, TLS 1.3, an embedded web server (HTTP, WebSocket), MQTT and
|
||||
firmware OTA updates in two files, `mongoose.c` and `mongoose.h`. Mongoose runs on
|
||||
STM32, NXP and other microcontrollers, on bare metal or with an RTOS, with no
|
||||
external dependencies. Use it for cloud connectivity, device web dashboards,
|
||||
secure firmware updates and remote control.
|
||||
|
||||
Mongoose is an open source, two-file C networking library and embedded web server for
|
||||
microcontrollers that combines TCP/IP stack, HTTP, WebSocket, MQTT, TLS 1.3
|
||||
stack, built-in firmware OTA updates, and device-dashboard infrastructure.
|
||||
License: GPLv2 or commercial.
|
||||
Website: https://mongoose.ws/
|
||||
GitHub repo: https://github.com/cesanta/mongoose
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Mongoose - Embedded Web Server / Embedded Network Library
|
||||
# Mongoose: a full-stack embedded networking library
|
||||
|
||||
[](https://opensource.org/licenses/gpl-2.0.php)
|
||||
[](https://github.com/cesanta/mongoose/actions)
|
||||
@@ -7,11 +7,17 @@
|
||||
|
||||
<img src="https://mongoose.ws/images/logo.svg" width="48" height="48"
|
||||
align="left" style="float:left;" />
|
||||
Two files. Drop `mongoose.c` and `mongoose.h` into any C/C++ project and get a
|
||||
full network stack — HTTP, WebSocket, MQTT, TLS, firmware OTA
|
||||
updates, device dashboard — on any microcontroller or desktop OS.
|
||||
Mongoose is a full-stack embedded networking library for C/C++: a TCP/IP stack
|
||||
with drivers, TLS 1.3, an embedded web server (HTTP, WebSocket), MQTT and
|
||||
firmware OTA updates in two files, `mongoose.c` and `mongoose.h`. Mongoose runs on
|
||||
STM32, NXP and other microcontrollers, on bare metal or with an RTOS, with no
|
||||
external dependencies. Use it for cloud connectivity, device web dashboards,
|
||||
secure firmware updates and remote control.
|
||||
|
||||
On the market since 2004, used by vast number of open source and commercial
|
||||
Use it instead of lwIP + mbedTLS + httpd + an MQTT client + OTA glue code, or
|
||||
run it on top of lwIP or Zephyr.
|
||||
|
||||
On the market since 2004, used by a vast number of open source and commercial
|
||||
products - it even runs on the International Space Station! Features include:
|
||||
|
||||
- Cross-platform:
|
||||
@@ -24,7 +30,7 @@ products - it even runs on the International Space Station! Features include:
|
||||
- Source code is both ISO C and ISO C++ compliant
|
||||
- Very easy to integrate
|
||||
- Own TCP/IP stack for bare metal / RTOS, or run on top of any existing one (lwIP, Zephyr, ...)
|
||||
- Built-in TLS 1.3 ECC stack. Also can use external TLS libraries - mbedTLS, OpenSSL, or other
|
||||
- Built-in TLS 1.3 stack. Also can use external TLS libraries - mbedTLS, OpenSSL, or other
|
||||
- Does not depend on any other software to implement networking
|
||||
- Built-in firmware updates for STM32 H5, STM32 H7, NXP IMXRT, RP2040/2350, ESP32 and more
|
||||
- Commercial support with proactive security updates; [CRA-compliant](https://mongoose.ws/security/cra-compliance/)
|
||||
@@ -35,8 +41,32 @@ See https://mongoose.ws/ for complete documentation, videos, case studies, etc.
|
||||
|
||||
## Supported platforms
|
||||
|
||||
Mongoose can work on top of any TCP/IP stack that supports BSD sockets API.
|
||||
Platforms supported by the 3rd party TCP/IP stacks:
|
||||
Mongoose has its own built-in TCP/IP stack with drivers, so it needs no
|
||||
additional software to implement networking. The built-in stack runs on bare
|
||||
metal and under any RTOS. Platforms supported by the Mongoose built-in TCP/IP
|
||||
stack:
|
||||
|
||||
| MCU | Connectivity | Notes |
|
||||
| :-- | :----------- | :---- |
|
||||
| **STM32** | Built-in Ethernet | Ethernet-capable STM32F, STM32H5, STM32H7, STM32N6, STM32C5 variants |
|
||||
| **NXP i.MX RT** | Built-in Ethernet | RT102x, RT104x, RT105x, RT106x, RT117x, RT118x (NETC) |
|
||||
| **NXP MCX** | Built-in Ethernet | MCXN94x, MCXE247 |
|
||||
| **NXP RW612** | Eth, WiFi, 802.15.4 | Separate Ethernet and radio drivers |
|
||||
| **Microchip ATSAME54** | Built-in Ethernet | GMAC driver |
|
||||
| **Renesas RA** | Built-in Ethernet | RA6M, RA8M |
|
||||
| **Infineon XMC** | Built-in Ethernet | XMC4, XMC7 |
|
||||
| **Texas Instruments** | Built-in Ethernet | TM4C, TMS570 |
|
||||
| **RP2040, RP2350, STM32, others** | CYW43xx Wi-Fi chips | SPI or SDIO; Pico W, Pico 2 W, Pico RM2, Arduino Portenta H7 |
|
||||
| **STM32, others** | ST67W6 Wi-Fi modules | SPI; for example, STM32U5 with the ST67W611M1 module |
|
||||
| **MCUs with an NXP Wi-Fi SDK port** | NXP Wi-Fi | Requires the SDK's Wi-Fi port implementing the driver's network hooks |
|
||||
| **NXP MCXW71** | Built-in 802.15.4 radio | IPv4 over IEEE 802.15.4 |
|
||||
| **NXP RW612** | Built-in 802.15.4 radio | IPv4 over IEEE 802.15.4; requires NXP radio firmware |
|
||||
| **Any MCU with SPI** | W5500 or W5100 Ethernet chips | External Wiznet MAC+PHY |
|
||||
| **MCUs connected to a cellular modem** | Cellular | AT commands and PPP; for example, SIM800 |
|
||||
| **MCUs with CMSIS Ethernet drivers** | Ethernet via CMSIS-Driver | Requires CMSIS Ethernet MAC and PHY drivers |
|
||||
|
||||
Mongoose can also run on top of any TCP/IP stack that supports the BSD
|
||||
sockets API. Platforms supported via 3rd party TCP/IP stacks:
|
||||
|
||||
| TCP/IP stack | Notes |
|
||||
| :-------------- | :------- |
|
||||
@@ -45,23 +75,6 @@ Platforms supported by the 3rd party TCP/IP stacks:
|
||||
| **Other** | Any other TCP/IP stack that supports BSD socket API, for example Amazon FreeRTOS-TCP |
|
||||
| **Linux, Mac, Windows** | Workstations, server, single board computers, embedded Linux devices running on MPUs or FPGAs |
|
||||
|
||||
Optionally, Mongoose provides its own built-in TCP/IP stack, eliminating the
|
||||
need for additional software to implement networking functionality. The
|
||||
built-in stack supports operation in both bare-metal and RTOS environments.
|
||||
Platforms supported by the Mongoose built-in TCP/IP stack:
|
||||
|
||||
| Hardware | Notes |
|
||||
| :------------- | :------- |
|
||||
| **STM32** | All STM32 MCUs with built-in Ethernet: STM32Fxx, STM32H5xx, STM32H7xx |
|
||||
| **NXP** | All NXP MCUs with built-in Ethernet: IMXRT102x, IMXRT104x, IMXRT105x, IMXRT106x, IMXRT117x, RW612, MCXN94x |
|
||||
| **Microchip** | ATSAME54 MCUs with built-int Ethernet |
|
||||
| **Renesas** | RA5M, RA6M, RA8M MCUs with built-in Ethernet |
|
||||
| **Infineon** | XMC4, XMC7 MCUs with built-in Ethernet |
|
||||
| **Texas Instruments** | TM4C, TMS570 MCUs with built-in Ethernet |
|
||||
| **Cypress WiFi** | Any MCU with CY43xx WiFi chips, like RP2040 Pico-W, RP2350 Pico2-W, Arduino Portenta |
|
||||
| **Wiznet Ethernet** | Any MCU that use Wiznet W5500 or Wiznet 5100 MAC+PHY chips |
|
||||
| **Cellular** | NRF9160, SIM800 |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
Below are quick snippets that should give an idea how simple the API is and
|
||||
@@ -158,6 +171,17 @@ int main() {
|
||||
}
|
||||
```
|
||||
|
||||
## Licensing
|
||||
|
||||
Mongoose is dual-licensed:
|
||||
- GPLv2: free for open source projects. Many popular open source projects use
|
||||
Mongoose, for example
|
||||
[SWUpdate](https://github.com/sbabic/swupdate/tree/master/mongoose), the
|
||||
embedded Linux update framework
|
||||
- Commercial: for closed-source products. Includes support directly from the
|
||||
engineers who wrote the whole stack, and early security fixes.
|
||||
See [licensing](https://mongoose.ws/licensing/)
|
||||
|
||||
## Commercial use
|
||||
- Mongoose is used by hundreds of businesses, from Fortune500 giants like
|
||||
Siemens, Schneider Electric, Broadcom, Bosch, Google, Samsung, Qualcomm, Caterpillar to the small businesses
|
||||
@@ -222,12 +246,12 @@ Technical guides and deep dives into embedded web servers, WebUI integration and
|
||||
- [lwIP vs Mongoose - TCP/IP Stack Integration Benchmark](https://mongoose.ws/articles/lwip-vs-mongoose-tcpip-stack-integration/)
|
||||
- [Embedded TCP/IP Stacks Comparison](https://mongoose.ws/embedded-tcpip-stack-comparison/)
|
||||
- [STM32H5 File System on Internal Flash: LittleFS + Mongoose](https://mongoose.ws/articles/stm32h5-file-system-on-internal-flash/)
|
||||
- [Firmware OTA and remote control on NXP FRDM-RW612](https://mongoose.ws/articles/mdash/)
|
||||
- [Firmware OTA and remote control on NXP FRDM-RW612](https://mongoose.ws/articles/rw612-mdash/)
|
||||
- [25000 WebSocket messages per second on STM32 Nucleo-H723ZG](https://mongoose.ws/articles/stm32-websocket-benchmark/)
|
||||
- [How to connect STM32 to AWS IoT Core](https://mongoose.ws/articles/stm32-aws-iot/)
|
||||
- [How to connect STM32 to Azure IoT Hub](https://mongoose.ws/articles/stm32-azure-iot-hub/)
|
||||
- [How to connect STM32 to EMQX](https://mongoose.ws/articles/stm32-emqx/)
|
||||
- [STM32 Device Management deep dive: Secure OTA, Remote Control, health monitor](https://mongoose.ws/articles/stm32-mdash-deep-dive/")
|
||||
- [STM32 Device Management deep dive: Secure OTA, Remote Control, health monitor](https://mongoose.ws/articles/stm32-mdash-deep-dive/)
|
||||
- [STM32 TCP/IP footprint: how much flash and RAM does Mongoose take?](https://mongoose.ws/articles/tcp-ip-footprint/)
|
||||
|
||||
|
||||
|
||||
+105
-195
@@ -1668,21 +1668,6 @@ static void mg_dash_handle_add(struct mg_connection *c, struct mg_dash *dash,
|
||||
}
|
||||
}
|
||||
|
||||
// Firmware upload callback for mg_http_stream_body(). Uses *p as a flag:
|
||||
// non-NULL means an OTA session is open and must be closed by mg_ota_end()
|
||||
static bool otacb(struct mg_http_message *hm, struct mg_str *data, void **p) {
|
||||
if (hm != NULL) { // Start
|
||||
if (!mg_ota_begin(hm->body.len)) return false;
|
||||
*p = (void *) (ptrdiff_t) 1;
|
||||
return true;
|
||||
} else if (data != NULL) { // Next chunk
|
||||
return mg_ota_write(data->buf, data->len);
|
||||
}
|
||||
if (*p == NULL) return false; // End. Begin failed, nothing to finish
|
||||
*p = NULL;
|
||||
return mg_ota_end();
|
||||
}
|
||||
|
||||
static void mg_serve_fs(struct mg_connection *c, struct mg_http_message *hm,
|
||||
struct mg_dash_user *u, bool stream) {
|
||||
struct mg_dash *dash = (struct mg_dash *) c->fn_data;
|
||||
@@ -1729,9 +1714,9 @@ void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
mg_serve_fs(c, hm, u, true);
|
||||
return; // Important - the serve_fs() call can invalidate hm
|
||||
} else if (mg_match(hm->uri, mg_str("/api/ota"), NULL)) {
|
||||
// Authenticated above. If streaming started, the stream handler owns
|
||||
// the connection and c->data, so skip the marker check below
|
||||
if (mg_http_stream_body(c, hm, otacb, NULL)) return;
|
||||
// Authenticated above. Streaming owns the connection and c->data.
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
return;
|
||||
}
|
||||
if (d->marker != '\0') mg_log_http_req(c, hm);
|
||||
} else if (ev == MG_EV_HTTP_MSG && d->marker != '\0') {
|
||||
@@ -3641,66 +3626,12 @@ struct mg_fs mg_fs_posix = {p_stat, p_list, p_open, p_close, p_read,
|
||||
|
||||
|
||||
|
||||
#if MG_ENABLE_HEALTH
|
||||
#if MG_ARCH == MG_ARCH_ESP32
|
||||
// reusable for other platforms, currently ESP32-only
|
||||
struct mg_health_coredump {
|
||||
const void *ptr;
|
||||
size_t poffset;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
static struct mg_health_coredump s_coredump;
|
||||
#endif
|
||||
|
||||
bool mg_health_coredump_begin(size_t *size) {
|
||||
#if MG_ARCH == MG_ARCH_ESP32 && CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
||||
const esp_partition_t *p = esp_partition_find_first(
|
||||
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_COREDUMP, NULL);
|
||||
size_t address = 0, len = 0;
|
||||
esp_err_t err = esp_core_dump_image_get(&address, &len);
|
||||
if (size == NULL || s_coredump.ptr != NULL || err != ESP_OK || p == NULL)
|
||||
return false;
|
||||
s_coredump.ptr = p;
|
||||
s_coredump.poffset = address - p->address;
|
||||
s_coredump.size = len;
|
||||
*size = len;
|
||||
return true;
|
||||
#else
|
||||
(void) size;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool mg_health_coredump_read(size_t offset, void *buf, size_t *len) {
|
||||
#if MG_ARCH == MG_ARCH_ESP32 && CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
||||
const esp_partition_t *p = (const esp_partition_t *) s_coredump.ptr;
|
||||
if (p == NULL || offset > s_coredump.size)
|
||||
return false;
|
||||
if (*len > s_coredump.size - offset) *len = s_coredump.size - offset;
|
||||
return esp_partition_read(p, s_coredump.poffset + offset, buf, *len) == ESP_OK;
|
||||
#else
|
||||
(void) offset, (void) buf, (void) len;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool mg_health_coredump_end(void) {
|
||||
#if MG_ARCH == MG_ARCH_ESP32 && CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
||||
bool ok = s_coredump.ptr != NULL;
|
||||
memset(&s_coredump, 0, sizeof(s_coredump));
|
||||
return ok;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// The one health record. Lives in RAM that survives a warm reset, see
|
||||
// MG_HEALTH_RAM and the .mg_health region in the linker script
|
||||
struct mg_health mg_health_record MG_HEALTH_RAM;
|
||||
|
||||
// STM32 H7, H5, C5 support. Needs the _estack symbol from the linker script
|
||||
#if defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5)
|
||||
// STM32 H723 support. Needs the _estack symbol from the linker script
|
||||
#if MG_ENABLE_HEALTH && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5))
|
||||
// Fault handler body. Runs in exception context: no printf, no malloc, no
|
||||
// blocking calls. Copies the unwind registers and raw stack into the health
|
||||
// record, then resets.
|
||||
@@ -3742,57 +3673,52 @@ void BusFault_Handler(void) __attribute__((alias("HardFault_Handler")));
|
||||
void UsageFault_Handler(void) __attribute__((alias("HardFault_Handler")));
|
||||
#endif
|
||||
|
||||
// Start a new boot: validate the record. Called by MG_HEALTH_INIT()
|
||||
// Start a new boot: validate the record, count the boot, store the reset
|
||||
// reason. The current boot moves to prev_* slots first, so the previous
|
||||
// boot's uptime and reason stay readable. Called by MG_HEALTH_INIT()
|
||||
void mg_health_init(void) {
|
||||
char magic[] = MG_HEALTH_MAGIC;
|
||||
// struct mg_health *h = mg_health_get();
|
||||
if (!mg_health_valid()) { // First boot, or RAM lost its contents
|
||||
memset(&mg_health_record, 0, sizeof(mg_health_record));
|
||||
memcpy(mg_health_record.magic, magic, sizeof(mg_health_record.magic));
|
||||
}
|
||||
#if MG_ARCH == MG_ARCH_ESP32
|
||||
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||
mg_health_record.arch = (uint16_t) MG_HEALTH_ARCH_XTENSA;
|
||||
#elif CONFIG_IDF_TARGET_ARCH_RISCV
|
||||
mg_health_record.arch = (uint16_t) MG_HEALTH_ARCH_RISCV;
|
||||
#endif
|
||||
esp_reset_reason_t rr = esp_reset_reason();
|
||||
mg_health_record.reset_reason = esp2mg(rr);
|
||||
#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH == 0
|
||||
MG_ERROR(("MG_HEALTH is enabled but CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH=n"));
|
||||
#else
|
||||
|
||||
#if 0 && MG_ARCH == MG_ARCH_ESP32 && CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
||||
{
|
||||
// After a crash, copy the backtrace from the core dump in flash: the same
|
||||
// addresses the panic handler prints as "Backtrace: PC:SP PC:SP ...".
|
||||
// The core dump stays in flash, so check the reset reason, otherwise an
|
||||
// old crash is reported on every boot
|
||||
esp_core_dump_summary_t *s = calloc(1, sizeof(*s));
|
||||
static esp_core_dump_summary_t s; // Big on RISC-V, keep it off the stack
|
||||
esp_reset_reason_t rr = esp_reset_reason();
|
||||
uint32_t *out = mg_health_record.data;
|
||||
size_t n = 0;
|
||||
size_t n = 0, max = MG_HEALTH_DATA_SIZE;
|
||||
if (rr != ESP_RST_PANIC && rr != ESP_RST_INT_WDT && rr != ESP_RST_TASK_WDT)
|
||||
return; // Not a crash that went through the panic handler
|
||||
if (max == 0 || esp_core_dump_get_summary(&s) != ESP_OK) return;
|
||||
|
||||
mg_health_record.cpuid = 42; // ESP32
|
||||
mg_health_record.reset_reason = MG_HEALTH_RESET_FAULT;
|
||||
|
||||
memset(out, 0, sizeof(mg_health_record.data));
|
||||
if (s == NULL) return;
|
||||
if ((rr != ESP_RST_PANIC && rr != ESP_RST_INT_WDT &&
|
||||
rr != ESP_RST_TASK_WDT) // Not a crash caught by the panic handler
|
||||
|| esp_core_dump_get_summary(s) != ESP_OK) {
|
||||
free(s);
|
||||
return;
|
||||
}
|
||||
out[n++] = s->exc_pc;
|
||||
memcpy(&out[n], s->exc_task, 16), n += 4;
|
||||
out[n++] = s.exc_pc;
|
||||
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||
out[n++] = s->ex_info.exc_a[0];
|
||||
out[n++] = s->ex_info.exc_a[1];
|
||||
size_t len = s->exc_bt_info.depth;
|
||||
if (len > MG_HEALTH_DATA_SIZE - n) len = (MG_HEALTH_DATA_SIZE - n);
|
||||
memcpy(&out[n], s->exc_bt_info.bt, 4 * len); // sizeof(uint32_t)
|
||||
for (uint32_t i = 0; i < s.exc_bt_info.depth && n < max; i++) {
|
||||
if (s.exc_bt_info.bt[i] != s.exc_pc) out[n++] = s.exc_bt_info.bt[i];
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ARCH_RISCV
|
||||
out[n++] = s->ex_info.exc_ra;
|
||||
out[n++] = s->ex_info.exc_sp;
|
||||
size_t len = s->exc_bt_info.dump_size / 4 ; // sizeof(uint32_t)
|
||||
if (len > MG_HEALTH_DATA_SIZE - n) len = (MG_HEALTH_DATA_SIZE - n);
|
||||
memcpy(&out[n], s->exc_bt_info.stackdump, 4 * len);
|
||||
// No unwinder: take the return address, then words on the stack that
|
||||
// point into executable memory (a heuristic, may include stale values)
|
||||
if (n < max) out[n++] = s.ex_info.ra;
|
||||
for (uint32_t i = 0; i + 4 <= s.exc_bt_info.dump_size && n < max; i += 4) {
|
||||
uint32_t w;
|
||||
memcpy(&w, &s.exc_bt_info.stackdump[i], sizeof(w)); // Byte array
|
||||
if (esp_ptr_executable((void *) (uintptr_t) w)) out[n++] = w;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
free(s);
|
||||
#endif // CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
||||
#endif // MG_ARCH_ESP32
|
||||
}
|
||||
|
||||
static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) {
|
||||
@@ -3806,17 +3732,11 @@ static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) {
|
||||
size_t mg_print_crash_record(void (*fn)(char, void *), void *arg, va_list *ap) {
|
||||
size_t len = 0;
|
||||
if (mg_health_valid() &&
|
||||
(mg_health_record.reset_reason == (uint16_t) MG_HEALTH_RESET_FAULT
|
||||
#if MG_ARCH == MG_ARCH_ESP32
|
||||
|| mg_health_record.reset_reason == (uint16_t) MG_HEALTH_RESET_WATCHDOG
|
||||
#endif
|
||||
)) {
|
||||
mg_health_record.reset_reason == MG_HEALTH_RESET_FAULT) {
|
||||
size_t n = sizeof(mg_health_record.data) / sizeof(mg_health_record.data[0]);
|
||||
len += mg_xprintf(fn, arg, "{%m:%hhu,%m:%hu,%m:%hu,%m:%u,%m:[%M]}", //
|
||||
MG_ESC("version"), mg_health_record.magic[3], //
|
||||
MG_ESC("arch"), mg_health_record.arch, //
|
||||
MG_ESC("reason"), mg_health_record.reset_reason, //
|
||||
MG_ESC("cpuid"), mg_health_record.cpuid, //
|
||||
len += mg_xprintf(fn, arg, "{%m:%hhu,%m:%u,%m:[%M]}", //
|
||||
MG_ESC("version"), mg_health_record.magic[3], //
|
||||
MG_ESC("cpuid"), mg_health_record.cpuid, //
|
||||
MG_ESC("data"), print_uint32, n, mg_health_record.data);
|
||||
} else {
|
||||
len += mg_xprintf(fn, arg, "{}");
|
||||
@@ -3824,7 +3744,6 @@ size_t mg_print_crash_record(void (*fn)(char, void *), void *arg, va_list *ap) {
|
||||
(void) ap;
|
||||
return len;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MG_ENABLE_LINES
|
||||
#line 1 "src/http.c"
|
||||
@@ -4710,8 +4629,8 @@ void mg_http_serve_upload(struct mg_connection *c, struct mg_http_message *hm,
|
||||
fs->rm(path); // MG_FS_WRITE appends, not truncates: drop any old file
|
||||
if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) {
|
||||
MG_ERROR(("Cannot open %s for upload", path));
|
||||
} else if (!mg_http_stream_body(c, hm, serve_upload_cb, fd)) {
|
||||
mg_fs_close(fd);
|
||||
} else {
|
||||
mg_http_stream_body(c, hm, serve_upload_cb, fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4845,6 +4764,7 @@ struct mg_upload_state {
|
||||
size_t received; // Number of body bytes received so far
|
||||
void *user_data; // Passed to cb, free for cb to use
|
||||
bool failed; // Set when cb returned false
|
||||
bool finished; // End callback has been called
|
||||
};
|
||||
|
||||
// Event handler installed by mg_http_stream_body(). Feeds the body to the
|
||||
@@ -4852,18 +4772,18 @@ struct mg_upload_state {
|
||||
// handler: the connection is closed after the reply.
|
||||
static void mg_stream_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_upload_state *us = (struct mg_upload_state *) c->data;
|
||||
bool (*fn)(struct mg_http_message *, struct mg_str *, void **) =
|
||||
bool (*fn)(struct mg_http_message *, struct mg_str *, void **) =
|
||||
(bool (*)(struct mg_http_message *, struct mg_str *, void **))
|
||||
(ptrdiff_t) c->fn_data;
|
||||
(void) ev_data;
|
||||
if (ev == MG_EV_CLOSE || ev == MG_EV_ERROR) {
|
||||
if (us->received < us->expected) {
|
||||
if (!us->finished) {
|
||||
// Connection dropped mid-upload. Let cb clean up, e.g. call mg_ota_end()
|
||||
us->received = us->expected; // MG_EV_ERROR is followed by MG_EV_CLOSE
|
||||
us->finished = true; // MG_EV_ERROR is followed by MG_EV_CLOSE
|
||||
fn(NULL, NULL, &us->user_data);
|
||||
}
|
||||
} else if (ev == MG_EV_READ && !c->is_draining) {
|
||||
if (c->recv.len > 0 && us->received < us->expected) {
|
||||
} else if (ev == MG_EV_READ && !c->is_draining && !us->finished) {
|
||||
if (!us->failed && c->recv.len > 0 && us->received < us->expected) {
|
||||
// Got some uploaded data. Feed by 512-byte aligned chunks, for OTA
|
||||
size_t left = us->expected - us->received, r = c->recv.len;
|
||||
size_t aligned = r < left ? MG_ROUND_DOWN(r, 512) : left;
|
||||
@@ -4874,10 +4794,13 @@ static void mg_stream_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
us->received += aligned;
|
||||
mg_iobuf_del(&c->recv, 0, aligned); // Shift the unprocessed tail
|
||||
}
|
||||
if (us->received >= us->expected) {
|
||||
if (!us->finished && (us->failed || us->received >= us->expected)) {
|
||||
// Finished upload. Always call cb, even after error, so it can clean up
|
||||
us->finished = true;
|
||||
us->failed |= !fn(NULL, NULL, &us->user_data);
|
||||
if (!c->is_client) {
|
||||
if (c->is_client && us->failed) {
|
||||
mg_error(c, "Body streaming failed");
|
||||
} else if (!c->is_client) {
|
||||
mg_http_reply(c, us->failed ? 500 : 200, NULL, "%zu %s\n",
|
||||
us->received, us->failed ? "fail" : "ok");
|
||||
}
|
||||
@@ -4886,48 +4809,54 @@ static void mg_stream_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
}
|
||||
}
|
||||
|
||||
bool mg_http_stream_body(struct mg_connection *c, struct mg_http_message *hm,
|
||||
void mg_http_stream_body(struct mg_connection *c, struct mg_http_message *hm,
|
||||
bool (*cb)(struct mg_http_message *, struct mg_str *,
|
||||
void **user_data),
|
||||
void **),
|
||||
void *user_data) {
|
||||
struct mg_upload_state *us = (struct mg_upload_state *) c->data;
|
||||
struct mg_str *te = mg_http_get_header(hm, "Transfer-Encoding");
|
||||
bool handled = false;
|
||||
int code = 0;
|
||||
size_t head_len = hm->head.len;
|
||||
|
||||
// Catch upload requests early, without buffering whole body. Called on the
|
||||
// MG_EV_HTTP_HDRS event: we've received all HTTP headers but not necessarily
|
||||
// the full HTTP body
|
||||
if (hm->body.len == (size_t) ~0 ||
|
||||
(te != NULL && mg_strcasecmp(*te, mg_str("chunked")) == 0)) {
|
||||
// Unknown length or chunked, cannot stream. Leave it to the caller
|
||||
} else if (!c->is_client &&
|
||||
mg_strcmp(hm->method, mg_str("POST")) != 0 &&
|
||||
mg_strcmp(hm->method, mg_str("PUT")) != 0) {
|
||||
// Not an upload request. Leave it to the caller
|
||||
if (!c->is_client && mg_strcmp(hm->method, mg_str("POST")) != 0 &&
|
||||
mg_strcmp(hm->method, mg_str("PUT")) != 0) {
|
||||
code = 405;
|
||||
} else if (mg_http_get_header(hm, "Transfer-Encoding") != NULL) {
|
||||
code = 400;
|
||||
} else if (hm->body.len == (size_t) ~0 ||
|
||||
(!c->is_client && mg_http_get_header(hm, "Content-Length") == NULL)) {
|
||||
code = 411;
|
||||
} else if (sizeof(*us) > sizeof(c->data)) {
|
||||
if (!c->is_client) { // Nobody to reply to on a client connection
|
||||
c->pfn = NULL;
|
||||
mg_http_reply(c, 500, NULL, "data too small\n");
|
||||
c->is_draining = 1;
|
||||
cb(NULL, NULL, &user_data); // Let cb release user_data, start skipped
|
||||
handled = true;
|
||||
code = 500;
|
||||
}
|
||||
c->pfn = NULL; // Take ownership even when validation fails
|
||||
if (code != 0) {
|
||||
c->fn = NULL;
|
||||
c->fn_data = NULL;
|
||||
mg_iobuf_del(&c->recv, 0, c->recv.len);
|
||||
cb(NULL, NULL, &user_data); // Release resources even if start was skipped
|
||||
if (c->is_client) {
|
||||
mg_error(c, "%s", mg_http_status_code_str(code));
|
||||
} else {
|
||||
mg_http_reply(c, code, code == 405 ? "Allow: POST, PUT\r\n" : NULL, "");
|
||||
}
|
||||
c->is_draining = 1;
|
||||
} else {
|
||||
c->pfn = NULL; // Silence HTTP protocol handler
|
||||
memset(us, 0, sizeof(*us));
|
||||
us->expected = hm->body.len; // Store number of bytes we expect
|
||||
us->user_data = user_data;
|
||||
c->fn = mg_stream_handler;
|
||||
c->fn_data = (void *) (ptrdiff_t) cb;
|
||||
us->failed = !cb(hm, NULL, &us->user_data); // Call before deleting headers
|
||||
if (mg_http_get_header(hm, "Expect") != NULL) {
|
||||
if (!c->is_client && !us->failed && !us->finished &&
|
||||
mg_http_get_header(hm, "Expect") != NULL) {
|
||||
mg_http_reply(c, 100, NULL, ""); // If curl expects, we continue
|
||||
}
|
||||
mg_iobuf_del(&c->recv, 0, hm->head.len); // Delete HTTP headers
|
||||
c->fn = mg_stream_handler; // We own the connection from now
|
||||
c->fn_data = (void *) (ptrdiff_t) cb;
|
||||
mg_iobuf_del(&c->recv, 0, head_len); // Delete HTTP headers
|
||||
mg_call(c, MG_EV_READ, &c->recv.len); // Process the body bytes we have
|
||||
handled = true;
|
||||
}
|
||||
return handled;
|
||||
}
|
||||
|
||||
static bool is_hex_digit(int c) {
|
||||
@@ -5852,7 +5781,7 @@ size_t mg_l2_driver_output(struct mg_tcpip_if *ifp, size_t len) {
|
||||
#define MGI_802154_DATA (125 - MGI_802154_HDR)
|
||||
|
||||
size_t mg_l2_802154_tx(struct mg_tcpip_if *ifp, const void *buf, size_t len,
|
||||
size_t (*send)(const uint8_t *, size_t,
|
||||
size_t (*send_fn)(const uint8_t *, size_t,
|
||||
struct mg_tcpip_if *)) {
|
||||
struct mg_l2_802154_data *d =
|
||||
(struct mg_l2_802154_data *) ifp->driver_data;
|
||||
@@ -5870,7 +5799,7 @@ size_t mg_l2_802154_tx(struct mg_tcpip_if *ifp, const void *buf, size_t len,
|
||||
f[12] = m[2], f[13] = m[1], f[14] = m[0]; // Little endian EUI-64
|
||||
f[15] = d->tag, f[16] = (uint8_t) ((i << 4) | last);
|
||||
memcpy(f + MGI_802154_HDR, p + i * MGI_802154_DATA, n);
|
||||
if (send(f, MGI_802154_HDR + n, ifp) == 0) return 0;
|
||||
if (send_fn(f, MGI_802154_HDR + n, ifp) == 0) return 0;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
@@ -7404,7 +7333,6 @@ static void mg_mdash_fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
}
|
||||
mg_iobuf_free(&io);
|
||||
} else if (ev == MG_EV_CLOSE) {
|
||||
mg_health_coredump_end();
|
||||
mg_mdash_conn = NULL;
|
||||
MG_INFO(("disconnected, ram %lu", 0));
|
||||
}
|
||||
@@ -7462,35 +7390,6 @@ static void mg_mdash_rpc_ota_end(struct mg_rpc_req *r) {
|
||||
// MG_ESC("percent"));
|
||||
}
|
||||
|
||||
static void rpc_coredump_begin(struct mg_rpc_req *r) {
|
||||
size_t size = 0;
|
||||
if (mg_health_coredump_begin(&size)) {
|
||||
mg_rpc_ok(r, "{%m:%llu}", MG_ESC("size"), (uint64_t) size);
|
||||
} else {
|
||||
mg_rpc_err(r, 500, "%m", MG_ESC("failed"));
|
||||
}
|
||||
}
|
||||
|
||||
static void rpc_coredump_read(struct mg_rpc_req *r) {
|
||||
uint8_t buf[768];
|
||||
size_t len = sizeof(buf);
|
||||
size_t offset = (size_t) mg_json_get_long(r->frame, "$.params.offset", -1);
|
||||
if (mg_health_coredump_read(offset, buf, &len)) {
|
||||
mg_rpc_ok(r, "{%m:\"%M\"}", MG_ESC("data"), mg_print_base64,
|
||||
(int) len, buf);
|
||||
} else {
|
||||
mg_rpc_err(r, 500, "%m", MG_ESC("failed"));
|
||||
}
|
||||
}
|
||||
|
||||
static void rpc_coredump_end(struct mg_rpc_req *r) {
|
||||
if (mg_health_coredump_end()) {
|
||||
mg_rpc_ok(r, "true");
|
||||
} else {
|
||||
mg_rpc_err(r, 500, "%m", MG_ESC("failed"));
|
||||
}
|
||||
}
|
||||
|
||||
void mg_mdash_init(struct mg_mgr *mgr) {
|
||||
MG_INFO(("Initialising mdash agent, key [%s]", MG_MDASH_KEY));
|
||||
mg_rpc_add(&mgr->rpcs, mg_str("RPC.List"), mg_rpc_list, NULL);
|
||||
@@ -7498,9 +7397,6 @@ void mg_mdash_init(struct mg_mgr *mgr) {
|
||||
mg_rpc_add(&mgr->rpcs, mg_str("OTA.Begin"), mg_mdash_rpc_ota_begin, NULL);
|
||||
mg_rpc_add(&mgr->rpcs, mg_str("OTA.Write"), mg_mdash_rpc_ota_write, NULL);
|
||||
mg_rpc_add(&mgr->rpcs, mg_str("OTA.End"), mg_mdash_rpc_ota_end, NULL);
|
||||
mg_rpc_add(&mgr->rpcs, mg_str("CoreDump.Begin"), rpc_coredump_begin, NULL);
|
||||
mg_rpc_add(&mgr->rpcs, mg_str("CoreDump.Read"), rpc_coredump_read, NULL);
|
||||
mg_rpc_add(&mgr->rpcs, mg_str("CoreDump.End"), rpc_coredump_end, NULL);
|
||||
}
|
||||
|
||||
void mg_mdash_poll(struct mg_mgr *mgr) {
|
||||
@@ -11528,9 +11424,7 @@ static void s_firmware_fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
(unsigned long) hm->body.len, (unsigned long) s_ota->size);
|
||||
} else {
|
||||
MG_DEBUG(("Beginning OTA (%lu bytes)", (unsigned long) s_ota->size));
|
||||
if (!mg_http_stream_body(c, hm, s_firmware_cb, NULL)) {
|
||||
mg_error(c, "Cannot stream firmware");
|
||||
}
|
||||
mg_http_stream_body(c, hm, s_firmware_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_ERROR) {
|
||||
s_ota->fn((char *) ev_data);
|
||||
@@ -11563,6 +11457,21 @@ void mg_ota_url_check(struct mg_mgr *mgr, const char *json_url,
|
||||
}
|
||||
}
|
||||
|
||||
// Uses *p as a flag: non-NULL means an OTA session is open and must be
|
||||
// closed by mg_ota_end()
|
||||
bool mg_http_ota_cb(struct mg_http_message *hm, struct mg_str *data, void **p) {
|
||||
if (hm != NULL) { // Start
|
||||
if (!mg_ota_begin(hm->body.len)) return false;
|
||||
*p = (void *) (ptrdiff_t) 1;
|
||||
return true;
|
||||
} else if (data != NULL) { // Next chunk
|
||||
return mg_ota_write(data->buf, data->len);
|
||||
}
|
||||
if (*p == NULL) return false; // End. Begin failed, nothing to finish
|
||||
*p = NULL;
|
||||
return mg_ota_end();
|
||||
}
|
||||
|
||||
void mg_ota_poll(struct mg_mgr *mgr) {
|
||||
static uint64_t t = 5000; // Fire first time 5 sec after boot
|
||||
|
||||
@@ -29490,8 +29399,9 @@ uint64_t mg_ntohll(uint64_t net) {
|
||||
}
|
||||
|
||||
void mg_delayms(unsigned int ms) {
|
||||
uint64_t to = mg_millis() + ms + 1;
|
||||
while (mg_millis() < to) (void) 0;
|
||||
uint64_t now, to = mg_millis() + ms + 1;
|
||||
while ((now = mg_millis()) < to)
|
||||
if (now + ms + 1 < to) to = now + ms + 1;
|
||||
}
|
||||
|
||||
#if MG_ENABLE_CUSTOM_CALLOC
|
||||
@@ -34618,7 +34528,7 @@ static bool st67w6_init(uint8_t *mac) {
|
||||
}
|
||||
// set clock, who cares ???
|
||||
if (!st67w6_at_cmd("AT+GET_CLOCK\r\n", 14)) return false;
|
||||
MG_DEBUG(("%.*s", s_at_resp_len, at_resp)); // TODO(scaprile): --> VERBOSE
|
||||
MG_VERBOSE(("%.*s", s_at_resp_len, at_resp));
|
||||
// BT-ENABLED DEPENDENCY
|
||||
|
||||
// MODULE DEPENDENCY
|
||||
|
||||
+23
-99
@@ -253,11 +253,8 @@ extern "C" {
|
||||
#include <esp_system.h>
|
||||
#include <esp_mac.h>
|
||||
#include <esp_idf_version.h>
|
||||
#if MG_ENABLE_HEALTH
|
||||
#include <esp_memory_utils.h>
|
||||
#include <esp_core_dump.h>
|
||||
#include <esp_partition.h>
|
||||
#endif
|
||||
// #include <esp_memory_utils.h> // For health record coredumps
|
||||
// #include <esp_core_dump.h> // For health record coredumps
|
||||
|
||||
#define MG_PATH_MAX 128
|
||||
|
||||
@@ -2727,29 +2724,32 @@ int mg_http_status(const struct mg_http_message *hm);
|
||||
|
||||
// Streams a POST/PUT body to a callback as it arrives, without buffering it.
|
||||
// Call it from an MG_EV_HTTP_HDRS handler, after matching the request, e.g.
|
||||
// with mg_match(). Returns true if it took over the connection, false if the
|
||||
// request is not POST/PUT, is chunked, or has no Content-Length.
|
||||
// with mg_match(). Always takes over the connection. Invalid requests receive
|
||||
// an HTTP error; client-side failures call mg_error(). Replies and connection
|
||||
// draining are handled internally, including callback failures.
|
||||
//
|
||||
// Callback upload_fn(hm, data, user_data) is called:
|
||||
// - at start: hm is set. Open the file, set *user_data.
|
||||
// - per chunk: data is set, 512 byte aligned except the last. Not called
|
||||
// again after a failure.
|
||||
// - at end: both NULL. Always called once, also when the connection drops
|
||||
// early, so release resources here. A truncated body looks like a complete
|
||||
// one: validate before committing.
|
||||
// Return true on success, false on error.
|
||||
// - at end: both NULL. Always called exactly once, including validation
|
||||
// failures before start and connections that drop early. Release resources
|
||||
// here. A truncated body looks like a complete one: validate before
|
||||
// committing.
|
||||
// The callback returns true on success, false on error. A failure stops
|
||||
// streaming immediately and calls the end callback before closing.
|
||||
// Example:
|
||||
// if (ev == MG_EV_HTTP_HDRS && mg_match(hm->uri, mg_str("/fs/*"), NULL) {
|
||||
// mg_http_stream_body(c, hm, upload, NULL));
|
||||
// if (ev == MG_EV_HTTP_HDRS && mg_match(hm->uri, mg_str("/fs/*"), NULL)) {
|
||||
// mg_http_stream_body(c, hm, upload, NULL);
|
||||
// }
|
||||
// Full example:
|
||||
// tutorials/http/file-upload-single-post
|
||||
// Related APIs:
|
||||
// mg_http_listen(), mg_http_next_multipart()
|
||||
// Notes:
|
||||
// After it returns true, this function hijacks the connection. Existing event
|
||||
// handler not called, hm is invalid, c->data and c->fn_data are modified
|
||||
bool mg_http_stream_body(struct mg_connection *c, struct mg_http_message *hm,
|
||||
// The existing event handler is no longer called. hm is invalid after this
|
||||
// call; c->data and c->fn_data are reserved for streaming state.
|
||||
void mg_http_stream_body(struct mg_connection *c, struct mg_http_message *hm,
|
||||
bool (*upload_fn)(struct mg_http_message *,
|
||||
struct mg_str *, void **user_data),
|
||||
void *user_data);
|
||||
@@ -4223,9 +4223,8 @@ bool mg_ota_end(void);
|
||||
// ```c
|
||||
// if (ev == MG_EV_HTTP_HDRS) {
|
||||
// struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
// if (mg_match(hm->uri, mg_str("/ota"), NULL) &&
|
||||
// !mg_http_stream_body(c, hm, mg_http_ota_cb, NULL)) {
|
||||
// mg_http_reply(c, 400, "", "POST firmware with Content-Length\n");
|
||||
// if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
// mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
@@ -4374,8 +4373,6 @@ void mg_ota_poll(struct mg_mgr *);
|
||||
|
||||
|
||||
|
||||
#if MG_ENABLE_HEALTH
|
||||
|
||||
#ifndef MG_HEALTH_MAGIC
|
||||
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
|
||||
#endif
|
||||
@@ -4384,15 +4381,13 @@ void mg_ota_poll(struct mg_mgr *);
|
||||
#define MG_HEALTH_RAM __attribute__((section(".mg_health")))
|
||||
#endif
|
||||
|
||||
#ifndef MG_HEALTH_DATA_SIZE
|
||||
#if (defined(STM32H7_DEV_ID) || defined(STM32H5)) || defined(STM32C5)
|
||||
#if !defined(MG_HEALTH_DATA_SIZE) && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5))
|
||||
#define MG_HEALTH_DATA_SIZE 250
|
||||
#elif MG_ARCH == MG_ARCH_ESP32
|
||||
#define MG_HEALTH_DATA_SIZE 23
|
||||
#else
|
||||
#endif
|
||||
|
||||
#ifndef MG_HEALTH_DATA_SIZE
|
||||
#define MG_HEALTH_DATA_SIZE 10
|
||||
#endif
|
||||
#endif // MG_HEALTH_DATA_SIZE
|
||||
|
||||
// Places the record in RAM that survives a reset. Define in mongoose_config.h
|
||||
// to match the linker script, e.g. __attribute__((section(".mg_health")))
|
||||
@@ -4400,13 +4395,6 @@ void mg_ota_poll(struct mg_mgr *);
|
||||
#define MG_HEALTH_RAM
|
||||
#endif
|
||||
|
||||
// CPU core architecture
|
||||
enum mg_health_arch {
|
||||
MG_HEALTH_ARCH_ARM = 0,
|
||||
MG_HEALTH_ARCH_XTENSA,
|
||||
MG_HEALTH_ARCH_RISCV
|
||||
};
|
||||
|
||||
// Why the device restarted. Hardware usually reports several flags at once,
|
||||
// so these are ordered by how much they tell you: the most specific cause a
|
||||
// device reports wins
|
||||
@@ -4423,8 +4411,7 @@ enum mg_health_reason {
|
||||
|
||||
struct mg_health {
|
||||
char magic[4]; // MG_HEALTH_MAGIC when the record holds valid data
|
||||
uint16_t reset_reason; // enum mg_health_reason that started this boot
|
||||
uint16_t arch; // enum mg_health_arch, core architecture (ARM, etc)
|
||||
uint32_t reset_reason; // enum mg_health_reason that started this boot
|
||||
uint32_t cpuid; // ARM CPU ID
|
||||
uint32_t data[MG_HEALTH_DATA_SIZE];
|
||||
};
|
||||
@@ -4457,72 +4444,9 @@ static inline const char *mg_health_reason_str(int reason) {
|
||||
}
|
||||
}
|
||||
|
||||
#if MG_ARCH == MG_ARCH_ESP32
|
||||
#ifndef CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
||||
#define CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH 0
|
||||
#endif
|
||||
#ifndef ESP_PARTITION_SUBTYPE_DATA_COREDUMP
|
||||
#define ESP_PARTITION_SUBTYPE_DATA_COREDUMP 0x03
|
||||
#endif
|
||||
// clang-format off
|
||||
static inline uint16_t esp2mg(esp_reset_reason_t rr) {
|
||||
switch(rr) {
|
||||
case ESP_RST_POWERON: return (uint16_t) MG_HEALTH_RESET_POWER;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 2)
|
||||
case ESP_RST_PWR_GLITCH:
|
||||
#endif
|
||||
case ESP_RST_BROWNOUT: return (uint16_t) MG_HEALTH_RESET_BROWNOUT;
|
||||
case ESP_RST_EXT: return (uint16_t) MG_HEALTH_RESET_PIN;
|
||||
case ESP_RST_SW: return (uint16_t) MG_HEALTH_RESET_SOFTWARE;
|
||||
case ESP_RST_DEEPSLEEP: return (uint16_t) MG_HEALTH_RESET_LOWPOWER;
|
||||
case ESP_RST_INT_WDT:
|
||||
case ESP_RST_TASK_WDT:
|
||||
case ESP_RST_WDT: return (uint16_t) MG_HEALTH_RESET_WATCHDOG;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 2)
|
||||
case ESP_RST_CPU_LOCKUP:
|
||||
#endif
|
||||
case ESP_RST_PANIC: return (uint16_t) MG_HEALTH_RESET_FAULT;
|
||||
default: break;
|
||||
}
|
||||
return (uint16_t) MG_HEALTH_RESET_UNKNOWN;
|
||||
}
|
||||
// clang-format on
|
||||
#endif
|
||||
|
||||
#define MG_HEALTH_INIT() mg_health_init()
|
||||
extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *);
|
||||
extern void mg_health_init(void);
|
||||
extern bool mg_health_coredump_begin(size_t *size);
|
||||
extern bool mg_health_coredump_read(size_t offset, void *buf, size_t *len);
|
||||
extern bool mg_health_coredump_end(void);
|
||||
#else
|
||||
#define MG_HEALTH_INIT()
|
||||
static inline int mg_health_reason(void) {
|
||||
return 0;
|
||||
}
|
||||
static inline const char *mg_health_reason_str(int reason) {
|
||||
(void) reason;
|
||||
return "unknown";
|
||||
}
|
||||
static inline size_t mg_print_crash_record(mg_pfn_t fn, void *arg,
|
||||
va_list *ap) {
|
||||
(void) ap;
|
||||
return mg_xprintf(fn, arg, "{}");
|
||||
}
|
||||
static inline bool mg_health_coredump_begin(size_t *size) {
|
||||
(void) size;
|
||||
return false;
|
||||
}
|
||||
static inline bool mg_health_coredump_read(size_t offset, void *buf,
|
||||
size_t *len) {
|
||||
(void) offset, (void) buf, (void) len;
|
||||
return false;
|
||||
}
|
||||
static inline bool mg_health_coredump_end(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
// Cloud connector API to https://mdash.net - device management cloud
|
||||
|
||||
|
||||
|
||||
+2
-5
@@ -22,11 +22,8 @@
|
||||
#include <esp_system.h>
|
||||
#include <esp_mac.h>
|
||||
#include <esp_idf_version.h>
|
||||
#if MG_ENABLE_HEALTH
|
||||
#include <esp_memory_utils.h>
|
||||
#include <esp_core_dump.h>
|
||||
#include <esp_partition.h>
|
||||
#endif
|
||||
// #include <esp_memory_utils.h> // For health record coredumps
|
||||
// #include <esp_core_dump.h> // For health record coredumps
|
||||
|
||||
#define MG_PATH_MAX 128
|
||||
|
||||
|
||||
+3
-18
@@ -533,21 +533,6 @@ static void mg_dash_handle_add(struct mg_connection *c, struct mg_dash *dash,
|
||||
}
|
||||
}
|
||||
|
||||
// Firmware upload callback for mg_http_stream_body(). Uses *p as a flag:
|
||||
// non-NULL means an OTA session is open and must be closed by mg_ota_end()
|
||||
static bool otacb(struct mg_http_message *hm, struct mg_str *data, void **p) {
|
||||
if (hm != NULL) { // Start
|
||||
if (!mg_ota_begin(hm->body.len)) return false;
|
||||
*p = (void *) (ptrdiff_t) 1;
|
||||
return true;
|
||||
} else if (data != NULL) { // Next chunk
|
||||
return mg_ota_write(data->buf, data->len);
|
||||
}
|
||||
if (*p == NULL) return false; // End. Begin failed, nothing to finish
|
||||
*p = NULL;
|
||||
return mg_ota_end();
|
||||
}
|
||||
|
||||
static void mg_serve_fs(struct mg_connection *c, struct mg_http_message *hm,
|
||||
struct mg_dash_user *u, bool stream) {
|
||||
struct mg_dash *dash = (struct mg_dash *) c->fn_data;
|
||||
@@ -594,9 +579,9 @@ void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
mg_serve_fs(c, hm, u, true);
|
||||
return; // Important - the serve_fs() call can invalidate hm
|
||||
} else if (mg_match(hm->uri, mg_str("/api/ota"), NULL)) {
|
||||
// Authenticated above. If streaming started, the stream handler owns
|
||||
// the connection and c->data, so skip the marker check below
|
||||
if (mg_http_stream_body(c, hm, otacb, NULL)) return;
|
||||
// Authenticated above. Streaming owns the connection and c->data.
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
return;
|
||||
}
|
||||
if (d->marker != '\0') mg_log_http_req(c, hm);
|
||||
} else if (ev == MG_EV_HTTP_MSG && d->marker != '\0') {
|
||||
|
||||
@@ -365,7 +365,7 @@ static bool st67w6_init(uint8_t *mac) {
|
||||
}
|
||||
// set clock, who cares ???
|
||||
if (!st67w6_at_cmd("AT+GET_CLOCK\r\n", 14)) return false;
|
||||
MG_DEBUG(("%.*s", s_at_resp_len, at_resp)); // TODO(scaprile): --> VERBOSE
|
||||
MG_VERBOSE(("%.*s", s_at_resp_len, at_resp));
|
||||
// BT-ENABLED DEPENDENCY
|
||||
|
||||
// MODULE DEPENDENCY
|
||||
|
||||
+37
-103
@@ -1,66 +1,12 @@
|
||||
#include "config.h"
|
||||
#include "health.h"
|
||||
|
||||
#if MG_ENABLE_HEALTH
|
||||
#if MG_ARCH == MG_ARCH_ESP32
|
||||
// reusable for other platforms, currently ESP32-only
|
||||
struct mg_health_coredump {
|
||||
const void *ptr;
|
||||
size_t poffset;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
static struct mg_health_coredump s_coredump;
|
||||
#endif
|
||||
|
||||
bool mg_health_coredump_begin(size_t *size) {
|
||||
#if MG_ARCH == MG_ARCH_ESP32 && CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
||||
const esp_partition_t *p = esp_partition_find_first(
|
||||
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_COREDUMP, NULL);
|
||||
size_t address = 0, len = 0;
|
||||
esp_err_t err = esp_core_dump_image_get(&address, &len);
|
||||
if (size == NULL || s_coredump.ptr != NULL || err != ESP_OK || p == NULL)
|
||||
return false;
|
||||
s_coredump.ptr = p;
|
||||
s_coredump.poffset = address - p->address;
|
||||
s_coredump.size = len;
|
||||
*size = len;
|
||||
return true;
|
||||
#else
|
||||
(void) size;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool mg_health_coredump_read(size_t offset, void *buf, size_t *len) {
|
||||
#if MG_ARCH == MG_ARCH_ESP32 && CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
||||
const esp_partition_t *p = (const esp_partition_t *) s_coredump.ptr;
|
||||
if (p == NULL || offset > s_coredump.size)
|
||||
return false;
|
||||
if (*len > s_coredump.size - offset) *len = s_coredump.size - offset;
|
||||
return esp_partition_read(p, s_coredump.poffset + offset, buf, *len) == ESP_OK;
|
||||
#else
|
||||
(void) offset, (void) buf, (void) len;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool mg_health_coredump_end(void) {
|
||||
#if MG_ARCH == MG_ARCH_ESP32 && CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
||||
bool ok = s_coredump.ptr != NULL;
|
||||
memset(&s_coredump, 0, sizeof(s_coredump));
|
||||
return ok;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// The one health record. Lives in RAM that survives a warm reset, see
|
||||
// MG_HEALTH_RAM and the .mg_health region in the linker script
|
||||
struct mg_health mg_health_record MG_HEALTH_RAM;
|
||||
|
||||
// STM32 H7, H5, C5 support. Needs the _estack symbol from the linker script
|
||||
#if defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5)
|
||||
// STM32 H723 support. Needs the _estack symbol from the linker script
|
||||
#if MG_ENABLE_HEALTH && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5))
|
||||
// Fault handler body. Runs in exception context: no printf, no malloc, no
|
||||
// blocking calls. Copies the unwind registers and raw stack into the health
|
||||
// record, then resets.
|
||||
@@ -102,57 +48,52 @@ void BusFault_Handler(void) __attribute__((alias("HardFault_Handler")));
|
||||
void UsageFault_Handler(void) __attribute__((alias("HardFault_Handler")));
|
||||
#endif
|
||||
|
||||
// Start a new boot: validate the record. Called by MG_HEALTH_INIT()
|
||||
// Start a new boot: validate the record, count the boot, store the reset
|
||||
// reason. The current boot moves to prev_* slots first, so the previous
|
||||
// boot's uptime and reason stay readable. Called by MG_HEALTH_INIT()
|
||||
void mg_health_init(void) {
|
||||
char magic[] = MG_HEALTH_MAGIC;
|
||||
// struct mg_health *h = mg_health_get();
|
||||
if (!mg_health_valid()) { // First boot, or RAM lost its contents
|
||||
memset(&mg_health_record, 0, sizeof(mg_health_record));
|
||||
memcpy(mg_health_record.magic, magic, sizeof(mg_health_record.magic));
|
||||
}
|
||||
#if MG_ARCH == MG_ARCH_ESP32
|
||||
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||
mg_health_record.arch = (uint16_t) MG_HEALTH_ARCH_XTENSA;
|
||||
#elif CONFIG_IDF_TARGET_ARCH_RISCV
|
||||
mg_health_record.arch = (uint16_t) MG_HEALTH_ARCH_RISCV;
|
||||
#endif
|
||||
esp_reset_reason_t rr = esp_reset_reason();
|
||||
mg_health_record.reset_reason = esp2mg(rr);
|
||||
#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH == 0
|
||||
MG_ERROR(("MG_HEALTH is enabled but CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH=n"));
|
||||
#else
|
||||
|
||||
#if 0 && MG_ARCH == MG_ARCH_ESP32 && CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
||||
{
|
||||
// After a crash, copy the backtrace from the core dump in flash: the same
|
||||
// addresses the panic handler prints as "Backtrace: PC:SP PC:SP ...".
|
||||
// The core dump stays in flash, so check the reset reason, otherwise an
|
||||
// old crash is reported on every boot
|
||||
esp_core_dump_summary_t *s = calloc(1, sizeof(*s));
|
||||
static esp_core_dump_summary_t s; // Big on RISC-V, keep it off the stack
|
||||
esp_reset_reason_t rr = esp_reset_reason();
|
||||
uint32_t *out = mg_health_record.data;
|
||||
size_t n = 0;
|
||||
size_t n = 0, max = MG_HEALTH_DATA_SIZE;
|
||||
if (rr != ESP_RST_PANIC && rr != ESP_RST_INT_WDT && rr != ESP_RST_TASK_WDT)
|
||||
return; // Not a crash that went through the panic handler
|
||||
if (max == 0 || esp_core_dump_get_summary(&s) != ESP_OK) return;
|
||||
|
||||
mg_health_record.cpuid = 42; // ESP32
|
||||
mg_health_record.reset_reason = MG_HEALTH_RESET_FAULT;
|
||||
|
||||
memset(out, 0, sizeof(mg_health_record.data));
|
||||
if (s == NULL) return;
|
||||
if ((rr != ESP_RST_PANIC && rr != ESP_RST_INT_WDT &&
|
||||
rr != ESP_RST_TASK_WDT) // Not a crash caught by the panic handler
|
||||
|| esp_core_dump_get_summary(s) != ESP_OK) {
|
||||
free(s);
|
||||
return;
|
||||
}
|
||||
out[n++] = s->exc_pc;
|
||||
memcpy(&out[n], s->exc_task, 16), n += 4;
|
||||
out[n++] = s.exc_pc;
|
||||
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||
out[n++] = s->ex_info.exc_a[0];
|
||||
out[n++] = s->ex_info.exc_a[1];
|
||||
size_t len = s->exc_bt_info.depth;
|
||||
if (len > MG_HEALTH_DATA_SIZE - n) len = (MG_HEALTH_DATA_SIZE - n);
|
||||
memcpy(&out[n], s->exc_bt_info.bt, 4 * len); // sizeof(uint32_t)
|
||||
for (uint32_t i = 0; i < s.exc_bt_info.depth && n < max; i++) {
|
||||
if (s.exc_bt_info.bt[i] != s.exc_pc) out[n++] = s.exc_bt_info.bt[i];
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ARCH_RISCV
|
||||
out[n++] = s->ex_info.exc_ra;
|
||||
out[n++] = s->ex_info.exc_sp;
|
||||
size_t len = s->exc_bt_info.dump_size / 4 ; // sizeof(uint32_t)
|
||||
if (len > MG_HEALTH_DATA_SIZE - n) len = (MG_HEALTH_DATA_SIZE - n);
|
||||
memcpy(&out[n], s->exc_bt_info.stackdump, 4 * len);
|
||||
// No unwinder: take the return address, then words on the stack that
|
||||
// point into executable memory (a heuristic, may include stale values)
|
||||
if (n < max) out[n++] = s.ex_info.ra;
|
||||
for (uint32_t i = 0; i + 4 <= s.exc_bt_info.dump_size && n < max; i += 4) {
|
||||
uint32_t w;
|
||||
memcpy(&w, &s.exc_bt_info.stackdump[i], sizeof(w)); // Byte array
|
||||
if (esp_ptr_executable((void *) (uintptr_t) w)) out[n++] = w;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
free(s);
|
||||
#endif // CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
||||
#endif // MG_ARCH_ESP32
|
||||
}
|
||||
|
||||
static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) {
|
||||
@@ -166,17 +107,11 @@ static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) {
|
||||
size_t mg_print_crash_record(void (*fn)(char, void *), void *arg, va_list *ap) {
|
||||
size_t len = 0;
|
||||
if (mg_health_valid() &&
|
||||
(mg_health_record.reset_reason == (uint16_t) MG_HEALTH_RESET_FAULT
|
||||
#if MG_ARCH == MG_ARCH_ESP32
|
||||
|| mg_health_record.reset_reason == (uint16_t) MG_HEALTH_RESET_WATCHDOG
|
||||
#endif
|
||||
)) {
|
||||
mg_health_record.reset_reason == MG_HEALTH_RESET_FAULT) {
|
||||
size_t n = sizeof(mg_health_record.data) / sizeof(mg_health_record.data[0]);
|
||||
len += mg_xprintf(fn, arg, "{%m:%hhu,%m:%hu,%m:%hu,%m:%u,%m:[%M]}", //
|
||||
MG_ESC("version"), mg_health_record.magic[3], //
|
||||
MG_ESC("arch"), mg_health_record.arch, //
|
||||
MG_ESC("reason"), mg_health_record.reset_reason, //
|
||||
MG_ESC("cpuid"), mg_health_record.cpuid, //
|
||||
len += mg_xprintf(fn, arg, "{%m:%hhu,%m:%u,%m:[%M]}", //
|
||||
MG_ESC("version"), mg_health_record.magic[3], //
|
||||
MG_ESC("cpuid"), mg_health_record.cpuid, //
|
||||
MG_ESC("data"), print_uint32, n, mg_health_record.data);
|
||||
} else {
|
||||
len += mg_xprintf(fn, arg, "{}");
|
||||
@@ -184,4 +119,3 @@ size_t mg_print_crash_record(void (*fn)(char, void *), void *arg, va_list *ap) {
|
||||
(void) ap;
|
||||
return len;
|
||||
}
|
||||
#endif
|
||||
+5
-80
@@ -5,8 +5,6 @@
|
||||
#include "arch.h"
|
||||
#include "printf.h"
|
||||
|
||||
#if MG_ENABLE_HEALTH
|
||||
|
||||
#ifndef MG_HEALTH_MAGIC
|
||||
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
|
||||
#endif
|
||||
@@ -15,15 +13,13 @@
|
||||
#define MG_HEALTH_RAM __attribute__((section(".mg_health")))
|
||||
#endif
|
||||
|
||||
#ifndef MG_HEALTH_DATA_SIZE
|
||||
#if (defined(STM32H7_DEV_ID) || defined(STM32H5)) || defined(STM32C5)
|
||||
#if !defined(MG_HEALTH_DATA_SIZE) && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5))
|
||||
#define MG_HEALTH_DATA_SIZE 250
|
||||
#elif MG_ARCH == MG_ARCH_ESP32
|
||||
#define MG_HEALTH_DATA_SIZE 23
|
||||
#else
|
||||
#endif
|
||||
|
||||
#ifndef MG_HEALTH_DATA_SIZE
|
||||
#define MG_HEALTH_DATA_SIZE 10
|
||||
#endif
|
||||
#endif // MG_HEALTH_DATA_SIZE
|
||||
|
||||
// Places the record in RAM that survives a reset. Define in mongoose_config.h
|
||||
// to match the linker script, e.g. __attribute__((section(".mg_health")))
|
||||
@@ -31,13 +27,6 @@
|
||||
#define MG_HEALTH_RAM
|
||||
#endif
|
||||
|
||||
// CPU core architecture
|
||||
enum mg_health_arch {
|
||||
MG_HEALTH_ARCH_ARM = 0,
|
||||
MG_HEALTH_ARCH_XTENSA,
|
||||
MG_HEALTH_ARCH_RISCV
|
||||
};
|
||||
|
||||
// Why the device restarted. Hardware usually reports several flags at once,
|
||||
// so these are ordered by how much they tell you: the most specific cause a
|
||||
// device reports wins
|
||||
@@ -54,8 +43,7 @@ enum mg_health_reason {
|
||||
|
||||
struct mg_health {
|
||||
char magic[4]; // MG_HEALTH_MAGIC when the record holds valid data
|
||||
uint16_t reset_reason; // enum mg_health_reason that started this boot
|
||||
uint16_t arch; // enum mg_health_arch, core architecture (ARM, etc)
|
||||
uint32_t reset_reason; // enum mg_health_reason that started this boot
|
||||
uint32_t cpuid; // ARM CPU ID
|
||||
uint32_t data[MG_HEALTH_DATA_SIZE];
|
||||
};
|
||||
@@ -88,69 +76,6 @@ static inline const char *mg_health_reason_str(int reason) {
|
||||
}
|
||||
}
|
||||
|
||||
#if MG_ARCH == MG_ARCH_ESP32
|
||||
#ifndef CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
||||
#define CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH 0
|
||||
#endif
|
||||
#ifndef ESP_PARTITION_SUBTYPE_DATA_COREDUMP
|
||||
#define ESP_PARTITION_SUBTYPE_DATA_COREDUMP 0x03
|
||||
#endif
|
||||
// clang-format off
|
||||
static inline uint16_t esp2mg(esp_reset_reason_t rr) {
|
||||
switch(rr) {
|
||||
case ESP_RST_POWERON: return (uint16_t) MG_HEALTH_RESET_POWER;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 2)
|
||||
case ESP_RST_PWR_GLITCH:
|
||||
#endif
|
||||
case ESP_RST_BROWNOUT: return (uint16_t) MG_HEALTH_RESET_BROWNOUT;
|
||||
case ESP_RST_EXT: return (uint16_t) MG_HEALTH_RESET_PIN;
|
||||
case ESP_RST_SW: return (uint16_t) MG_HEALTH_RESET_SOFTWARE;
|
||||
case ESP_RST_DEEPSLEEP: return (uint16_t) MG_HEALTH_RESET_LOWPOWER;
|
||||
case ESP_RST_INT_WDT:
|
||||
case ESP_RST_TASK_WDT:
|
||||
case ESP_RST_WDT: return (uint16_t) MG_HEALTH_RESET_WATCHDOG;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 2)
|
||||
case ESP_RST_CPU_LOCKUP:
|
||||
#endif
|
||||
case ESP_RST_PANIC: return (uint16_t) MG_HEALTH_RESET_FAULT;
|
||||
default: break;
|
||||
}
|
||||
return (uint16_t) MG_HEALTH_RESET_UNKNOWN;
|
||||
}
|
||||
// clang-format on
|
||||
#endif
|
||||
|
||||
#define MG_HEALTH_INIT() mg_health_init()
|
||||
extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *);
|
||||
extern void mg_health_init(void);
|
||||
extern bool mg_health_coredump_begin(size_t *size);
|
||||
extern bool mg_health_coredump_read(size_t offset, void *buf, size_t *len);
|
||||
extern bool mg_health_coredump_end(void);
|
||||
#else
|
||||
#define MG_HEALTH_INIT()
|
||||
static inline int mg_health_reason(void) {
|
||||
return 0;
|
||||
}
|
||||
static inline const char *mg_health_reason_str(int reason) {
|
||||
(void) reason;
|
||||
return "unknown";
|
||||
}
|
||||
static inline size_t mg_print_crash_record(mg_pfn_t fn, void *arg,
|
||||
va_list *ap) {
|
||||
(void) ap;
|
||||
return mg_xprintf(fn, arg, "{}");
|
||||
}
|
||||
static inline bool mg_health_coredump_begin(size_t *size) {
|
||||
(void) size;
|
||||
return false;
|
||||
}
|
||||
static inline bool mg_health_coredump_read(size_t offset, void *buf,
|
||||
size_t *len) {
|
||||
(void) offset, (void) buf, (void) len;
|
||||
return false;
|
||||
}
|
||||
static inline bool mg_health_coredump_end(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
+43
-33
@@ -879,8 +879,8 @@ void mg_http_serve_upload(struct mg_connection *c, struct mg_http_message *hm,
|
||||
fs->rm(path); // MG_FS_WRITE appends, not truncates: drop any old file
|
||||
if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) {
|
||||
MG_ERROR(("Cannot open %s for upload", path));
|
||||
} else if (!mg_http_stream_body(c, hm, serve_upload_cb, fd)) {
|
||||
mg_fs_close(fd);
|
||||
} else {
|
||||
mg_http_stream_body(c, hm, serve_upload_cb, fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1014,6 +1014,7 @@ struct mg_upload_state {
|
||||
size_t received; // Number of body bytes received so far
|
||||
void *user_data; // Passed to cb, free for cb to use
|
||||
bool failed; // Set when cb returned false
|
||||
bool finished; // End callback has been called
|
||||
};
|
||||
|
||||
// Event handler installed by mg_http_stream_body(). Feeds the body to the
|
||||
@@ -1021,18 +1022,18 @@ struct mg_upload_state {
|
||||
// handler: the connection is closed after the reply.
|
||||
static void mg_stream_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_upload_state *us = (struct mg_upload_state *) c->data;
|
||||
bool (*fn)(struct mg_http_message *, struct mg_str *, void **) =
|
||||
bool (*fn)(struct mg_http_message *, struct mg_str *, void **) =
|
||||
(bool (*)(struct mg_http_message *, struct mg_str *, void **))
|
||||
(ptrdiff_t) c->fn_data;
|
||||
(void) ev_data;
|
||||
if (ev == MG_EV_CLOSE || ev == MG_EV_ERROR) {
|
||||
if (us->received < us->expected) {
|
||||
if (!us->finished) {
|
||||
// Connection dropped mid-upload. Let cb clean up, e.g. call mg_ota_end()
|
||||
us->received = us->expected; // MG_EV_ERROR is followed by MG_EV_CLOSE
|
||||
us->finished = true; // MG_EV_ERROR is followed by MG_EV_CLOSE
|
||||
fn(NULL, NULL, &us->user_data);
|
||||
}
|
||||
} else if (ev == MG_EV_READ && !c->is_draining) {
|
||||
if (c->recv.len > 0 && us->received < us->expected) {
|
||||
} else if (ev == MG_EV_READ && !c->is_draining && !us->finished) {
|
||||
if (!us->failed && c->recv.len > 0 && us->received < us->expected) {
|
||||
// Got some uploaded data. Feed by 512-byte aligned chunks, for OTA
|
||||
size_t left = us->expected - us->received, r = c->recv.len;
|
||||
size_t aligned = r < left ? MG_ROUND_DOWN(r, 512) : left;
|
||||
@@ -1043,10 +1044,13 @@ static void mg_stream_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
us->received += aligned;
|
||||
mg_iobuf_del(&c->recv, 0, aligned); // Shift the unprocessed tail
|
||||
}
|
||||
if (us->received >= us->expected) {
|
||||
if (!us->finished && (us->failed || us->received >= us->expected)) {
|
||||
// Finished upload. Always call cb, even after error, so it can clean up
|
||||
us->finished = true;
|
||||
us->failed |= !fn(NULL, NULL, &us->user_data);
|
||||
if (!c->is_client) {
|
||||
if (c->is_client && us->failed) {
|
||||
mg_error(c, "Body streaming failed");
|
||||
} else if (!c->is_client) {
|
||||
mg_http_reply(c, us->failed ? 500 : 200, NULL, "%zu %s\n",
|
||||
us->received, us->failed ? "fail" : "ok");
|
||||
}
|
||||
@@ -1055,48 +1059,54 @@ static void mg_stream_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
}
|
||||
}
|
||||
|
||||
bool mg_http_stream_body(struct mg_connection *c, struct mg_http_message *hm,
|
||||
void mg_http_stream_body(struct mg_connection *c, struct mg_http_message *hm,
|
||||
bool (*cb)(struct mg_http_message *, struct mg_str *,
|
||||
void **user_data),
|
||||
void **),
|
||||
void *user_data) {
|
||||
struct mg_upload_state *us = (struct mg_upload_state *) c->data;
|
||||
struct mg_str *te = mg_http_get_header(hm, "Transfer-Encoding");
|
||||
bool handled = false;
|
||||
int code = 0;
|
||||
size_t head_len = hm->head.len;
|
||||
|
||||
// Catch upload requests early, without buffering whole body. Called on the
|
||||
// MG_EV_HTTP_HDRS event: we've received all HTTP headers but not necessarily
|
||||
// the full HTTP body
|
||||
if (hm->body.len == (size_t) ~0 ||
|
||||
(te != NULL && mg_strcasecmp(*te, mg_str("chunked")) == 0)) {
|
||||
// Unknown length or chunked, cannot stream. Leave it to the caller
|
||||
} else if (!c->is_client &&
|
||||
mg_strcmp(hm->method, mg_str("POST")) != 0 &&
|
||||
mg_strcmp(hm->method, mg_str("PUT")) != 0) {
|
||||
// Not an upload request. Leave it to the caller
|
||||
if (!c->is_client && mg_strcmp(hm->method, mg_str("POST")) != 0 &&
|
||||
mg_strcmp(hm->method, mg_str("PUT")) != 0) {
|
||||
code = 405;
|
||||
} else if (mg_http_get_header(hm, "Transfer-Encoding") != NULL) {
|
||||
code = 400;
|
||||
} else if (hm->body.len == (size_t) ~0 ||
|
||||
(!c->is_client && mg_http_get_header(hm, "Content-Length") == NULL)) {
|
||||
code = 411;
|
||||
} else if (sizeof(*us) > sizeof(c->data)) {
|
||||
if (!c->is_client) { // Nobody to reply to on a client connection
|
||||
c->pfn = NULL;
|
||||
mg_http_reply(c, 500, NULL, "data too small\n");
|
||||
c->is_draining = 1;
|
||||
cb(NULL, NULL, &user_data); // Let cb release user_data, start skipped
|
||||
handled = true;
|
||||
code = 500;
|
||||
}
|
||||
c->pfn = NULL; // Take ownership even when validation fails
|
||||
if (code != 0) {
|
||||
c->fn = NULL;
|
||||
c->fn_data = NULL;
|
||||
mg_iobuf_del(&c->recv, 0, c->recv.len);
|
||||
cb(NULL, NULL, &user_data); // Release resources even if start was skipped
|
||||
if (c->is_client) {
|
||||
mg_error(c, "%s", mg_http_status_code_str(code));
|
||||
} else {
|
||||
mg_http_reply(c, code, code == 405 ? "Allow: POST, PUT\r\n" : NULL, "");
|
||||
}
|
||||
c->is_draining = 1;
|
||||
} else {
|
||||
c->pfn = NULL; // Silence HTTP protocol handler
|
||||
memset(us, 0, sizeof(*us));
|
||||
us->expected = hm->body.len; // Store number of bytes we expect
|
||||
us->user_data = user_data;
|
||||
c->fn = mg_stream_handler;
|
||||
c->fn_data = (void *) (ptrdiff_t) cb;
|
||||
us->failed = !cb(hm, NULL, &us->user_data); // Call before deleting headers
|
||||
if (mg_http_get_header(hm, "Expect") != NULL) {
|
||||
if (!c->is_client && !us->failed && !us->finished &&
|
||||
mg_http_get_header(hm, "Expect") != NULL) {
|
||||
mg_http_reply(c, 100, NULL, ""); // If curl expects, we continue
|
||||
}
|
||||
mg_iobuf_del(&c->recv, 0, hm->head.len); // Delete HTTP headers
|
||||
c->fn = mg_stream_handler; // We own the connection from now
|
||||
c->fn_data = (void *) (ptrdiff_t) cb;
|
||||
mg_iobuf_del(&c->recv, 0, head_len); // Delete HTTP headers
|
||||
mg_call(c, MG_EV_READ, &c->recv.len); // Process the body bytes we have
|
||||
handled = true;
|
||||
}
|
||||
return handled;
|
||||
}
|
||||
|
||||
static bool is_hex_digit(int c) {
|
||||
|
||||
+14
-11
@@ -205,29 +205,32 @@ int mg_http_status(const struct mg_http_message *hm);
|
||||
|
||||
// Streams a POST/PUT body to a callback as it arrives, without buffering it.
|
||||
// Call it from an MG_EV_HTTP_HDRS handler, after matching the request, e.g.
|
||||
// with mg_match(). Returns true if it took over the connection, false if the
|
||||
// request is not POST/PUT, is chunked, or has no Content-Length.
|
||||
// with mg_match(). Always takes over the connection. Invalid requests receive
|
||||
// an HTTP error; client-side failures call mg_error(). Replies and connection
|
||||
// draining are handled internally, including callback failures.
|
||||
//
|
||||
// Callback upload_fn(hm, data, user_data) is called:
|
||||
// - at start: hm is set. Open the file, set *user_data.
|
||||
// - per chunk: data is set, 512 byte aligned except the last. Not called
|
||||
// again after a failure.
|
||||
// - at end: both NULL. Always called once, also when the connection drops
|
||||
// early, so release resources here. A truncated body looks like a complete
|
||||
// one: validate before committing.
|
||||
// Return true on success, false on error.
|
||||
// - at end: both NULL. Always called exactly once, including validation
|
||||
// failures before start and connections that drop early. Release resources
|
||||
// here. A truncated body looks like a complete one: validate before
|
||||
// committing.
|
||||
// The callback returns true on success, false on error. A failure stops
|
||||
// streaming immediately and calls the end callback before closing.
|
||||
// Example:
|
||||
// if (ev == MG_EV_HTTP_HDRS && mg_match(hm->uri, mg_str("/fs/*"), NULL) {
|
||||
// mg_http_stream_body(c, hm, upload, NULL));
|
||||
// if (ev == MG_EV_HTTP_HDRS && mg_match(hm->uri, mg_str("/fs/*"), NULL)) {
|
||||
// mg_http_stream_body(c, hm, upload, NULL);
|
||||
// }
|
||||
// Full example:
|
||||
// tutorials/http/file-upload-single-post
|
||||
// Related APIs:
|
||||
// mg_http_listen(), mg_http_next_multipart()
|
||||
// Notes:
|
||||
// After it returns true, this function hijacks the connection. Existing event
|
||||
// handler not called, hm is invalid, c->data and c->fn_data are modified
|
||||
bool mg_http_stream_body(struct mg_connection *c, struct mg_http_message *hm,
|
||||
// The existing event handler is no longer called. hm is invalid after this
|
||||
// call; c->data and c->fn_data are reserved for streaming state.
|
||||
void mg_http_stream_body(struct mg_connection *c, struct mg_http_message *hm,
|
||||
bool (*upload_fn)(struct mg_http_message *,
|
||||
struct mg_str *, void **user_data),
|
||||
void *user_data);
|
||||
+2
-2
@@ -11,7 +11,7 @@
|
||||
#define MGI_802154_DATA (125 - MGI_802154_HDR)
|
||||
|
||||
size_t mg_l2_802154_tx(struct mg_tcpip_if *ifp, const void *buf, size_t len,
|
||||
size_t (*send)(const uint8_t *, size_t,
|
||||
size_t (*send_fn)(const uint8_t *, size_t,
|
||||
struct mg_tcpip_if *)) {
|
||||
struct mg_l2_802154_data *d =
|
||||
(struct mg_l2_802154_data *) ifp->driver_data;
|
||||
@@ -29,7 +29,7 @@ size_t mg_l2_802154_tx(struct mg_tcpip_if *ifp, const void *buf, size_t len,
|
||||
f[12] = m[2], f[13] = m[1], f[14] = m[0]; // Little endian EUI-64
|
||||
f[15] = d->tag, f[16] = (uint8_t) ((i << 4) | last);
|
||||
memcpy(f + MGI_802154_HDR, p + i * MGI_802154_DATA, n);
|
||||
if (send(f, MGI_802154_HDR + n, ifp) == 0) return 0;
|
||||
if (send_fn(f, MGI_802154_HDR + n, ifp) == 0) return 0;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
-33
@@ -89,7 +89,6 @@ static void mg_mdash_fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
}
|
||||
mg_iobuf_free(&io);
|
||||
} else if (ev == MG_EV_CLOSE) {
|
||||
mg_health_coredump_end();
|
||||
mg_mdash_conn = NULL;
|
||||
MG_INFO(("disconnected, ram %lu", 0));
|
||||
}
|
||||
@@ -147,35 +146,6 @@ static void mg_mdash_rpc_ota_end(struct mg_rpc_req *r) {
|
||||
// MG_ESC("percent"));
|
||||
}
|
||||
|
||||
static void rpc_coredump_begin(struct mg_rpc_req *r) {
|
||||
size_t size = 0;
|
||||
if (mg_health_coredump_begin(&size)) {
|
||||
mg_rpc_ok(r, "{%m:%llu}", MG_ESC("size"), (uint64_t) size);
|
||||
} else {
|
||||
mg_rpc_err(r, 500, "%m", MG_ESC("failed"));
|
||||
}
|
||||
}
|
||||
|
||||
static void rpc_coredump_read(struct mg_rpc_req *r) {
|
||||
uint8_t buf[768];
|
||||
size_t len = sizeof(buf);
|
||||
size_t offset = (size_t) mg_json_get_long(r->frame, "$.params.offset", -1);
|
||||
if (mg_health_coredump_read(offset, buf, &len)) {
|
||||
mg_rpc_ok(r, "{%m:\"%M\"}", MG_ESC("data"), mg_print_base64,
|
||||
(int) len, buf);
|
||||
} else {
|
||||
mg_rpc_err(r, 500, "%m", MG_ESC("failed"));
|
||||
}
|
||||
}
|
||||
|
||||
static void rpc_coredump_end(struct mg_rpc_req *r) {
|
||||
if (mg_health_coredump_end()) {
|
||||
mg_rpc_ok(r, "true");
|
||||
} else {
|
||||
mg_rpc_err(r, 500, "%m", MG_ESC("failed"));
|
||||
}
|
||||
}
|
||||
|
||||
void mg_mdash_init(struct mg_mgr *mgr) {
|
||||
MG_INFO(("Initialising mdash agent, key [%s]", MG_MDASH_KEY));
|
||||
mg_rpc_add(&mgr->rpcs, mg_str("RPC.List"), mg_rpc_list, NULL);
|
||||
@@ -183,9 +153,6 @@ void mg_mdash_init(struct mg_mgr *mgr) {
|
||||
mg_rpc_add(&mgr->rpcs, mg_str("OTA.Begin"), mg_mdash_rpc_ota_begin, NULL);
|
||||
mg_rpc_add(&mgr->rpcs, mg_str("OTA.Write"), mg_mdash_rpc_ota_write, NULL);
|
||||
mg_rpc_add(&mgr->rpcs, mg_str("OTA.End"), mg_mdash_rpc_ota_end, NULL);
|
||||
mg_rpc_add(&mgr->rpcs, mg_str("CoreDump.Begin"), rpc_coredump_begin, NULL);
|
||||
mg_rpc_add(&mgr->rpcs, mg_str("CoreDump.Read"), rpc_coredump_read, NULL);
|
||||
mg_rpc_add(&mgr->rpcs, mg_str("CoreDump.End"), rpc_coredump_end, NULL);
|
||||
}
|
||||
|
||||
void mg_mdash_poll(struct mg_mgr *mgr) {
|
||||
|
||||
@@ -208,9 +208,7 @@ static void s_firmware_fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
(unsigned long) hm->body.len, (unsigned long) s_ota->size);
|
||||
} else {
|
||||
MG_DEBUG(("Beginning OTA (%lu bytes)", (unsigned long) s_ota->size));
|
||||
if (!mg_http_stream_body(c, hm, s_firmware_cb, NULL)) {
|
||||
mg_error(c, "Cannot stream firmware");
|
||||
}
|
||||
mg_http_stream_body(c, hm, s_firmware_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_ERROR) {
|
||||
s_ota->fn((char *) ev_data);
|
||||
@@ -243,6 +241,21 @@ void mg_ota_url_check(struct mg_mgr *mgr, const char *json_url,
|
||||
}
|
||||
}
|
||||
|
||||
// Uses *p as a flag: non-NULL means an OTA session is open and must be
|
||||
// closed by mg_ota_end()
|
||||
bool mg_http_ota_cb(struct mg_http_message *hm, struct mg_str *data, void **p) {
|
||||
if (hm != NULL) { // Start
|
||||
if (!mg_ota_begin(hm->body.len)) return false;
|
||||
*p = (void *) (ptrdiff_t) 1;
|
||||
return true;
|
||||
} else if (data != NULL) { // Next chunk
|
||||
return mg_ota_write(data->buf, data->len);
|
||||
}
|
||||
if (*p == NULL) return false; // End. Begin failed, nothing to finish
|
||||
*p = NULL;
|
||||
return mg_ota_end();
|
||||
}
|
||||
|
||||
void mg_ota_poll(struct mg_mgr *mgr) {
|
||||
static uint64_t t = 5000; // Fire first time 5 sec after boot
|
||||
|
||||
|
||||
@@ -80,9 +80,8 @@ bool mg_ota_end(void);
|
||||
// ```c
|
||||
// if (ev == MG_EV_HTTP_HDRS) {
|
||||
// struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
// if (mg_match(hm->uri, mg_str("/ota"), NULL) &&
|
||||
// !mg_http_stream_body(c, hm, mg_http_ota_cb, NULL)) {
|
||||
// mg_http_reply(c, 400, "", "POST firmware with Content-Length\n");
|
||||
// if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
// mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
|
||||
+3
-2
@@ -261,8 +261,9 @@ uint64_t mg_ntohll(uint64_t net) {
|
||||
}
|
||||
|
||||
void mg_delayms(unsigned int ms) {
|
||||
uint64_t to = mg_millis() + ms + 1;
|
||||
while (mg_millis() < to) (void) 0;
|
||||
uint64_t now, to = mg_millis() + ms + 1;
|
||||
while ((now = mg_millis()) < to)
|
||||
if (now + ms + 1 < to) to = now + ms + 1;
|
||||
}
|
||||
|
||||
#if MG_ENABLE_CUSTOM_CALLOC
|
||||
|
||||
@@ -555,7 +555,6 @@ static void task2(void *args) {
|
||||
DASHBOARD("select_child_write");
|
||||
DASHBOARD("poll_child_write");
|
||||
|
||||
#if 0
|
||||
s_error = false;
|
||||
s_backlog_ready = s_backlog_full = s_backlog_accept = false;
|
||||
ASSERT(xTaskCreate(backlog_task, "backlog", 512, NULL,
|
||||
@@ -570,12 +569,6 @@ static void task2(void *args) {
|
||||
while (!client_done(first, &status)) vTaskDelay(1);
|
||||
check_client(status);
|
||||
DASHBOARD("listen_backlog");
|
||||
#else
|
||||
(void) first;
|
||||
(void) second;
|
||||
(void) status;
|
||||
(void) backlog_task;
|
||||
#endif
|
||||
|
||||
s_error = false;
|
||||
// atask at higher priority than task1
|
||||
|
||||
+108
-6
@@ -3158,8 +3158,6 @@ static bool su_cb(struct mg_http_message *hm, struct mg_str *data, void **p) {
|
||||
static void su(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_HTTP_HDRS) {
|
||||
mg_http_stream_body(c, (struct mg_http_message *) ev_data, su_cb, NULL);
|
||||
} else if (ev == MG_EV_HTTP_MSG) { // Only for requests that were not streamed
|
||||
mg_http_reply(c, 400, "", "not streamed\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3194,8 +3192,7 @@ static void cr(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_CONNECT) {
|
||||
mg_printf(c, "GET / HTTP/1.0\r\n\r\n");
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
ASSERT(mg_http_stream_body(c, (struct mg_http_message *) ev_data, cr_cb,
|
||||
NULL));
|
||||
mg_http_stream_body(c, (struct mg_http_message *) ev_data, cr_cb, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3208,12 +3205,103 @@ static void cs(struct mg_connection *c, int ev, void *ev_data) {
|
||||
(void) ev_data;
|
||||
}
|
||||
|
||||
struct stream_test_state {
|
||||
int fail_at, starts, chunks, ends;
|
||||
};
|
||||
|
||||
static bool stream_test_cb(struct mg_http_message *hm, struct mg_str *data,
|
||||
void **p) {
|
||||
struct stream_test_state *s = (struct stream_test_state *) *p;
|
||||
ASSERT(s != NULL);
|
||||
if (hm != NULL) {
|
||||
s->starts++;
|
||||
return s->fail_at != 1;
|
||||
} else if (data != NULL) {
|
||||
s->chunks++;
|
||||
return s->fail_at != 2;
|
||||
}
|
||||
s->ends++;
|
||||
*p = NULL;
|
||||
return s->fail_at != 3;
|
||||
}
|
||||
|
||||
static void stream_test_original(struct mg_connection *c, int ev, void *data) {
|
||||
(void) c, (void) ev, (void) data;
|
||||
ASSERT(false); // Streaming must take ownership even when validation fails
|
||||
}
|
||||
|
||||
static void test_http_stream_errors(void) {
|
||||
static const struct {
|
||||
const char *request;
|
||||
int client, fail_at, body_size, code, starts, chunks;
|
||||
} cases[] = {
|
||||
{"GET / HTTP/1.1\r\nContent-Length: 0\r\n\r\n", 0, 0, 0, 405, 0, 0},
|
||||
{"POST / HTTP/1.1\r\n\r\n", 0, 0, 0, 411, 0, 0},
|
||||
{"POST / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n",
|
||||
0, 0, 0, 400, 0, 0},
|
||||
{"POST / HTTP/1.1\r\nContent-Length: 3\r\n\r\nabc", 0, 0, 0, 200, 1, 1},
|
||||
{"PUT / HTTP/1.1\r\nContent-Length: 0\r\n\r\n", 0, 0, 0, 200, 1, 0},
|
||||
// Start failure must reply without waiting for the body or sending 100.
|
||||
{"POST / HTTP/1.1\r\nContent-Length: 1000000\r\nExpect: 100-continue\r\n\r\n",
|
||||
0, 1, 0, 500, 1, 0},
|
||||
// Write failure must also reply before the announced body is complete.
|
||||
{"POST / HTTP/1.1\r\nContent-Length: 1000000\r\n\r\n",
|
||||
0, 2, 512, 500, 1, 1},
|
||||
{"POST / HTTP/1.1\r\nContent-Length: 3\r\n\r\nabc", 0, 3, 0, 500, 1, 1},
|
||||
// Clients close on errors and never send an HTTP error response.
|
||||
{"HTTP/1.1 200 OK\r\n\r\n", 1, 0, 0, 411, 0, 0},
|
||||
{"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n",
|
||||
1, 0, 0, 400, 0, 0},
|
||||
{"HTTP/1.1 200 OK\r\nContent-Length: 1000000\r\n\r\n",
|
||||
1, 1, 0, 500, 1, 0},
|
||||
{"HTTP/1.1 200 OK\r\nContent-Length: 1000000\r\n\r\n",
|
||||
1, 2, 512, 500, 1, 1},
|
||||
{"HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nabc", 1, 3, 0, 500, 1, 1},
|
||||
{"HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nabc", 1, 0, 0, 200, 1, 1},
|
||||
{"HTTP/1.1 204 No Content\r\n\r\n", 1, 0, 0, 200, 1, 0},
|
||||
};
|
||||
size_t i;
|
||||
for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
|
||||
struct mg_connection c;
|
||||
struct mg_http_message hm;
|
||||
struct stream_test_state state = {0, 0, 0, 0};
|
||||
char body[512];
|
||||
memset(&c, 0, sizeof(c));
|
||||
memset(body, 'x', sizeof(body));
|
||||
state.fail_at = cases[i].fail_at;
|
||||
c.is_client = cases[i].client != 0;
|
||||
c.fn = c.pfn = stream_test_original;
|
||||
mg_iobuf_add(&c.recv, 0, cases[i].request, strlen(cases[i].request));
|
||||
mg_iobuf_add(&c.recv, c.recv.len, body, (size_t) cases[i].body_size);
|
||||
ASSERT(mg_http_parse((char *) c.recv.buf, c.recv.len, &hm) > 0);
|
||||
mg_http_stream_body(&c, &hm, stream_test_cb, &state);
|
||||
ASSERT(c.pfn == NULL && c.is_draining);
|
||||
ASSERT(state.starts == cases[i].starts && state.chunks == cases[i].chunks);
|
||||
ASSERT(state.ends == 1);
|
||||
if (c.is_client) {
|
||||
ASSERT(c.send.len == 0);
|
||||
ASSERT(c.is_closing == (cases[i].code != 200));
|
||||
} else {
|
||||
ASSERT(mg_http_parse((char *) c.send.buf, c.send.len, &hm) > 0);
|
||||
ASSERT(mg_http_status(&hm) == cases[i].code);
|
||||
ASSERT(hm.message.len == c.send.len); // Exactly one response
|
||||
}
|
||||
mg_call(&c, MG_EV_READ, NULL);
|
||||
mg_error(&c, "stream cleanup test");
|
||||
mg_call(&c, MG_EV_CLOSE, NULL);
|
||||
ASSERT(state.ends == 1); // Error followed by close must not clean up twice
|
||||
mg_iobuf_free(&c.recv);
|
||||
mg_iobuf_free(&c.send);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_http_upload(void) {
|
||||
struct mg_mgr mgr;
|
||||
const char *url = "http://127.0.0.1:12352";
|
||||
int i;
|
||||
const char *s = "ok (8 foo\nbar\n)\n";
|
||||
|
||||
test_http_stream_errors();
|
||||
mg_mgr_init(&mgr);
|
||||
mg_http_listen(&mgr, url, us, NULL);
|
||||
mg_http_connect(&mgr, url, uc, (void *) &s);
|
||||
@@ -3257,7 +3345,7 @@ static void test_http_upload(void) {
|
||||
ASSERT(mgr.conns == NULL);
|
||||
}
|
||||
|
||||
// mg_http_stream_body: chunked request is not streamed, caller replies "400"
|
||||
// mg_http_stream_body: chunked request rejected with "400", cleanup still runs
|
||||
{
|
||||
char buf[FETCH_BUF_SIZE];
|
||||
s_su_done = s_su_end = 0;
|
||||
@@ -3268,7 +3356,7 @@ static void test_http_upload(void) {
|
||||
"POST /su_chunked.txt HTTP/1.1\r\n"
|
||||
"Transfer-Encoding: chunked\r\n\r\n"
|
||||
"3\r\nfoo\r\n0\r\n\r\n") == 400);
|
||||
ASSERT(s_su_done == 0 && s_su_end == 0); // The callback was never called
|
||||
ASSERT(s_su_done == 1 && s_su_end == 1); // End called without start
|
||||
ASSERT(fopen("su_chunked.txt", "r") == NULL);
|
||||
mg_mgr_free(&mgr);
|
||||
ASSERT(mgr.conns == NULL);
|
||||
@@ -5010,6 +5098,20 @@ static void test_dash(void) {
|
||||
"Authorization: Basic YWRtaW46YWRtaW4=\n\n") == 200);
|
||||
ASSERT(cmpbody(buf, login_admin_expected) == 0);
|
||||
|
||||
// OTA uses streaming and enforces the dashboard's authentication.
|
||||
ASSERT(fetch(&mgr, buf, url,
|
||||
"POST /api/ota HTTP/1.0\nContent-Length: 1\n\nx") == 403);
|
||||
ASSERT(fetch(&mgr, buf, url,
|
||||
"GET /api/ota HTTP/1.0\nContent-Length: 0\n"
|
||||
"Authorization: Basic YWRtaW46YWRtaW4=\n\n") == 405);
|
||||
ASSERT(fetch(&mgr, buf, url,
|
||||
"POST /ota HTTP/1.0\nContent-Length: 1\n"
|
||||
"Authorization: Basic YWRtaW46YWRtaW4=\n\nx") == 404);
|
||||
ASSERT(fetch(&mgr, buf, url,
|
||||
"POST /api/ota HTTP/1.0\nContent-Length: 1\n"
|
||||
"Authorization: Basic YWRtaW46YWRtaW4=\n\nx") == 200);
|
||||
ASSERT(cmpbody(buf, "1 ok\n") == 0);
|
||||
|
||||
// set3 is admin-only
|
||||
ASSERT(fetch(&mgr, buf, url,
|
||||
"GET /api/get/set3 HTTP/1.0\n"
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
THISDIR = $(realpath $(CURDIR))
|
||||
ROOTDIR = $(realpath $(CURDIR)/../../..)
|
||||
DOCKER ?= docker run --rm $(DA) -v $(ROOTDIR):$(ROOTDIR) -w $(THISDIR) espressif/idf
|
||||
CMD ?= idf.py build
|
||||
CMD ?= idf.py -DCFLAGS_EXTRA="$(CFLAGS_EXTRA)" build
|
||||
PORT ?= /dev/ttyUSB0
|
||||
|
||||
CFLAGS_EXTRA ?= -DMG_TLS=MG_TLS_BUILTIN
|
||||
|
||||
all: example
|
||||
|
||||
example:
|
||||
|
||||
@@ -12,3 +12,5 @@ idf_component_register(SRCS "main.c"
|
||||
espcoredump
|
||||
nvs_flash
|
||||
app_update)
|
||||
separate_arguments(CFLAGS_EXTRA)
|
||||
component_compile_options(${CFLAGS_EXTRA})
|
||||
@@ -44,6 +44,11 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
size_t total = heap_caps_get_total_size(MALLOC_CAP_DEFAULT);
|
||||
@@ -65,10 +70,8 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
}
|
||||
}
|
||||
|
||||
extern void wifi_init(const char *ssid, const char *pass);
|
||||
|
||||
void app_main(void) {
|
||||
MG_HEALTH_INIT();
|
||||
void wifi_init(const char *ssid, const char *pass);
|
||||
wifi_init(WIFI_SSID, WIFI_PASS);
|
||||
|
||||
struct mg_mgr mgr;
|
||||
|
||||
@@ -1,6 +1 @@
|
||||
#define MG_ARCH MG_ARCH_ESP32
|
||||
|
||||
#define MG_ENABLE_HEALTH 1
|
||||
|
||||
#define MG_TLS MG_TLS_BUILTIN
|
||||
//#define MG_TLS MG_TLS_MBED
|
||||
@@ -9,7 +9,12 @@ static void t_fn(void *arg) { // Tick periodically
|
||||
}
|
||||
|
||||
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_HTTP_MSG) mg_http_reply(c, 200, "", "hi\n");
|
||||
if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) mg_http_reply(c, 200, "", "hi\n");
|
||||
(void) ev_data;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,12 @@ static void t_fn(void *arg) { // Tick periodically
|
||||
}
|
||||
|
||||
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_HTTP_MSG) mg_http_reply(c, 200, "", "hi\n");
|
||||
if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) mg_http_reply(c, 200, "", "hi\n");
|
||||
(void) ev_data;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,24 +3,24 @@
|
||||
extern void mgmp_init(void);
|
||||
extern void mgmp_poll(int);
|
||||
|
||||
STATIC mp_obj_t mongoose_init(void) {
|
||||
static mp_obj_t mongoose_init(void) {
|
||||
mgmp_init();
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mongoose_init_obj, mongoose_init);
|
||||
static MP_DEFINE_CONST_FUN_OBJ_0(mongoose_init_obj, mongoose_init);
|
||||
|
||||
STATIC mp_obj_t mongoose_poll(mp_obj_t t_obj) {
|
||||
static mp_obj_t mongoose_poll(mp_obj_t t_obj) {
|
||||
mgmp_poll(mp_obj_get_int(t_obj));
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mongoose_poll_obj, mongoose_poll);
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(mongoose_poll_obj, mongoose_poll);
|
||||
|
||||
STATIC const mp_rom_map_elem_t mongoose_module_globals_table[] = {
|
||||
static const mp_rom_map_elem_t mongoose_module_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_mongoose) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&mongoose_init_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&mongoose_poll_obj) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(mongoose_module_globals, mongoose_module_globals_table);
|
||||
static MP_DEFINE_CONST_DICT(mongoose_module_globals, mongoose_module_globals_table);
|
||||
|
||||
const mp_obj_module_t mongoose_module = {
|
||||
.base = { &mp_type_module },
|
||||
|
||||
@@ -120,7 +120,7 @@ void mg_mqtt_poll(struct mg_mgr *mgr) {
|
||||
|
||||
// Reconnect if connection is closed, and send MQTT PINGs to keep
|
||||
// the connection alive or to detect connection loss
|
||||
if (mg_timer_expired(&timer, MQTT_RECONNECT_PERIOD_MS, mg_now())) {
|
||||
if (mg_timer_expired(&timer, MQTT_RECONNECT_PERIOD_MS, mg_millis())) {
|
||||
if (s_mqtt_conn == NULL) {
|
||||
struct mg_mqtt_opts opts = {
|
||||
.clean = true,
|
||||
|
||||
@@ -65,6 +65,11 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
|
||||
@@ -89,6 +89,11 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
|
||||
@@ -97,6 +97,11 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
|
||||
@@ -47,6 +47,11 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
|
||||
@@ -65,6 +65,11 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
|
||||
@@ -61,6 +61,11 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
|
||||
@@ -65,6 +65,11 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
|
||||
@@ -5,7 +5,12 @@
|
||||
#include "mongoose.h"
|
||||
|
||||
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
uint64_t tick = to_ms_since_boot(get_absolute_time());
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
|
||||
@@ -33,7 +33,12 @@ static void driver_init(struct mg_mgr *mgr) {
|
||||
}
|
||||
|
||||
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
uint64_t tick = to_ms_since_boot(get_absolute_time());
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
|
||||
@@ -58,7 +58,12 @@ static void driver_init(struct mg_mgr *mgr) {
|
||||
}
|
||||
|
||||
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
uint64_t tick = to_ms_since_boot(get_absolute_time());
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
|
||||
@@ -39,6 +39,11 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
uint64_t tick = to_ms_since_boot(get_absolute_time());
|
||||
|
||||
@@ -82,6 +82,11 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
uint64_t tick = to_ms_since_boot(get_absolute_time());
|
||||
|
||||
@@ -195,6 +195,11 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
uint64_t tick = to_ms_since_boot(get_absolute_time());
|
||||
|
||||
@@ -63,6 +63,11 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
|
||||
@@ -66,7 +66,12 @@ static bool usb_poll(struct mg_tcpip_if *ifp, bool s1) {
|
||||
}
|
||||
|
||||
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_HTTP_MSG) {
|
||||
if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/debug"), NULL)) {
|
||||
int level = mg_json_get_long(hm->body, "$.level", MG_LL_DEBUG);
|
||||
|
||||
@@ -68,6 +68,11 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
|
||||
@@ -28,7 +28,12 @@ bool mg_random(void *buf, size_t len) { // Use on-board RNG
|
||||
|
||||
|
||||
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_HTTP_MSG) {
|
||||
if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
(void) hm;
|
||||
mg_http_reply(c, 200, "", "hi\n");
|
||||
|
||||
@@ -66,7 +66,12 @@ static bool usb_poll(struct mg_tcpip_if *ifp, bool s1) {
|
||||
}
|
||||
|
||||
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_HTTP_MSG) {
|
||||
if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/debug"), NULL)) {
|
||||
int level = mg_json_get_long(hm->body, "$.level", MG_LL_DEBUG);
|
||||
|
||||
@@ -68,6 +68,11 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
|
||||
@@ -68,6 +68,11 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
|
||||
@@ -47,7 +47,12 @@ static void timer_fn(void *arg) {
|
||||
|
||||
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->fn_data;
|
||||
if (ev == MG_EV_HTTP_MSG) {
|
||||
if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/hello"),
|
||||
NULL)) { // Request to /api/hello
|
||||
|
||||
@@ -68,6 +68,11 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
|
||||
@@ -89,7 +89,12 @@ static void log_fn(char ch, void *param) {
|
||||
}
|
||||
|
||||
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_HTTP_MSG) {
|
||||
if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
mg_http_reply(c, 200, "", "{%m:%lu}\n", MG_ESC("tick"), HAL_GetTick());
|
||||
|
||||
@@ -44,7 +44,12 @@ bool mg_random(void *buf, size_t len) {
|
||||
}
|
||||
|
||||
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_HTTP_MSG) {
|
||||
if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick());
|
||||
|
||||
@@ -43,7 +43,12 @@ bool mg_random(void *buf, size_t len) {
|
||||
}
|
||||
|
||||
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_HTTP_MSG) {
|
||||
if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick());
|
||||
|
||||
@@ -68,6 +68,11 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
|
||||
@@ -68,6 +68,11 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
|
||||
@@ -64,6 +64,11 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
|
||||
@@ -60,6 +60,11 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
|
||||
@@ -68,6 +68,11 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
|
||||
@@ -68,6 +68,11 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
|
||||
@@ -68,6 +68,11 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
|
||||
@@ -63,7 +63,12 @@ static bool usb_poll(struct mg_tcpip_if *ifp, bool s1) {
|
||||
}
|
||||
|
||||
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_HTTP_MSG) {
|
||||
if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/debug"), NULL)) {
|
||||
int level = mg_json_get_long(hm->body, "$.level", MG_LL_DEBUG);
|
||||
|
||||
@@ -30,7 +30,12 @@ static void timer_fn(void *arg) {
|
||||
|
||||
// https://mongoose.ws/documentation/#2-minute-integration-guide
|
||||
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_HTTP_MSG) {
|
||||
if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
mg_http_reply(c, 200, "", "ok %p\r\n", hm);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,11 @@ static void wcb(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_ACCEPT && c->is_tls) {
|
||||
struct mg_tls_opts opts = {.cert = (char *) s_ssl_cert, .key = (char *) s_ssl_key};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = ev_data;
|
||||
MG_INFO(("%.*s %.*s %ld", (int) hm->method.len, hm->method.buf,
|
||||
|
||||
Reference in new issue
Block a user