Compare commits

..
11 Commits
Author SHA1 Message Date
Sergio R. Caprile b766c65615 avoid 802.15.4 using a standard BSD function name, enable BSD test after mg_error() fix 2026-10-23 14:00:24 -03:00
cpq 8296b88fb6 Update readme 2026-10-23 15:21:10 +01:00
Sergio R. Caprile a5efbf0592 misc cleanups and standardizations 2026-10-22 16:53:56 -03:00
Sergio R. Caprile 37c98dd893 mg_delay_ms() survives mg_millis() restarting on some 32-bit platforms 2026-10-22 13:49:21 -03:00
cpq 770a4ff27e Sync description 2026-10-22 17:21:05 +01:00
Sergio R. Caprile b6560306ba avoid reconnect timer being disrupted by possible SNTP updates 2026-10-22 12:12:27 -03:00
Sergio R. Caprile 5b39c93478 Merge pull request #3746 from cesanta/ota
Make mg_http_ota_cb public, add /ota endpoints to MCU examples
2026-10-22 11:30:35 -03:00
cpq 086008b716 Nit 2026-10-22 14:55:19 +01:00
cpq 35eb50a1e6 Nit 2026-10-22 14:46:19 +01:00
cpq 431e35ae8c Update README 2026-10-22 14:44:50 +01:00
cpq 25e2a99d8e Make mg_http_ota_cb public, add /ota endpoints to MCU examples 2026-10-22 14:30:10 +01:00
63 changed files with 632 additions and 656 deletions

No files matched your search

-1
View File
@@ -637,7 +637,6 @@ jobs:
example: example:
- path: micropython/esp32 - path: micropython/esp32
- path: esp32/uart-bridge - path: esp32/uart-bridge
- path: esp32/minimal
name: ${{ matrix.example.path }} name: ${{ matrix.example.path }}
env: env:
GO: 0 GO: 0
-1
View File
@@ -186,7 +186,6 @@ jobs:
matrix: matrix:
example: example:
- path: esp32/uart-bridge - path: esp32/uart-bridge
- path: esp32/minimal
name: ${{ matrix.example.path }} name: ${{ matrix.example.path }}
env: env:
GO: 0 GO: 0
+8 -4
View File
@@ -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. License: GPLv2 or commercial.
Website: https://mongoose.ws/ Website: https://mongoose.ws/
GitHub repo: https://github.com/cesanta/mongoose GitHub repo: https://github.com/cesanta/mongoose
+51 -27
View File
@@ -1,4 +1,4 @@
# Mongoose - Embedded Web Server / Embedded Network Library # Mongoose: a full-stack embedded networking library
[![License: GPLv2/Commercial](https://img.shields.io/badge/License-GPLv2%20or%20Commercial-green.svg)](https://opensource.org/licenses/gpl-2.0.php) [![License: GPLv2/Commercial](https://img.shields.io/badge/License-GPLv2%20or%20Commercial-green.svg)](https://opensource.org/licenses/gpl-2.0.php)
[![Build Status]( https://github.com/cesanta/mongoose/actions/workflows/quicktest.yml/badge.svg)](https://github.com/cesanta/mongoose/actions) [![Build Status]( https://github.com/cesanta/mongoose/actions/workflows/quicktest.yml/badge.svg)](https://github.com/cesanta/mongoose/actions)
@@ -7,11 +7,17 @@
<img src="https://mongoose.ws/images/logo.svg" width="48" height="48" <img src="https://mongoose.ws/images/logo.svg" width="48" height="48"
align="left" style="float:left;" /> align="left" style="float:left;" />
Two files. Drop `mongoose.c` and `mongoose.h` into any C/C++ project and get a Mongoose is a full-stack embedded networking library for C/C++: a TCP/IP stack
full network stack — HTTP, WebSocket, MQTT, TLS, firmware OTA with drivers, TLS 1.3, an embedded web server (HTTP, WebSocket), MQTT and
updates, device dashboard — on any microcontroller or desktop OS. 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: products - it even runs on the International Space Station! Features include:
- Cross-platform: - 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 - Source code is both ISO C and ISO C++ compliant
- Very easy to integrate - Very easy to integrate
- Own TCP/IP stack for bare metal / RTOS, or run on top of any existing one (lwIP, Zephyr, ...) - 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 - 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 - 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/) - 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 ## Supported platforms
Mongoose can work on top of any TCP/IP stack that supports BSD sockets API. Mongoose has its own built-in TCP/IP stack with drivers, so it needs no
Platforms supported by the 3rd party TCP/IP stacks: 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 | | 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 | | **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 | | **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 ## Usage Examples
Below are quick snippets that should give an idea how simple the API is and 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 ## Commercial use
- Mongoose is used by hundreds of businesses, from Fortune500 giants like - Mongoose is used by hundreds of businesses, from Fortune500 giants like
Siemens, Schneider Electric, Broadcom, Bosch, Google, Samsung, Qualcomm, Caterpillar to the small businesses 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/) - [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/) - [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/) - [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/) - [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 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 Azure IoT Hub](https://mongoose.ws/articles/stm32-azure-iot-hub/)
- [How to connect STM32 to EMQX](https://mongoose.ws/articles/stm32-emqx/) - [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/) - [STM32 TCP/IP footprint: how much flash and RAM does Mongoose take?](https://mongoose.ws/articles/tcp-ip-footprint/)
+101 -191
View File
@@ -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, static void mg_serve_fs(struct mg_connection *c, struct mg_http_message *hm,
struct mg_dash_user *u, bool stream) { struct mg_dash_user *u, bool stream) {
struct mg_dash *dash = (struct mg_dash *) c->fn_data; 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); mg_serve_fs(c, hm, u, true);
return; // Important - the serve_fs() call can invalidate hm return; // Important - the serve_fs() call can invalidate hm
} else if (mg_match(hm->uri, mg_str("/api/ota"), NULL)) { } else if (mg_match(hm->uri, mg_str("/api/ota"), NULL)) {
// Authenticated above. If streaming started, the stream handler owns // Authenticated above. Streaming owns the connection and c->data.
// the connection and c->data, so skip the marker check below mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
if (mg_http_stream_body(c, hm, otacb, NULL)) return; return;
} }
if (d->marker != '\0') mg_log_http_req(c, hm); if (d->marker != '\0') mg_log_http_req(c, hm);
} else if (ev == MG_EV_HTTP_MSG && d->marker != '\0') { } 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 // 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 // MG_HEALTH_RAM and the .mg_health region in the linker script
struct mg_health mg_health_record MG_HEALTH_RAM; struct mg_health mg_health_record MG_HEALTH_RAM;
// STM32 H7, H5, C5 support. Needs the _estack symbol from the linker script // STM32 H723 support. Needs the _estack symbol from the linker script
#if defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5) #if MG_ENABLE_HEALTH && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5))
// Fault handler body. Runs in exception context: no printf, no malloc, no // Fault handler body. Runs in exception context: no printf, no malloc, no
// blocking calls. Copies the unwind registers and raw stack into the health // blocking calls. Copies the unwind registers and raw stack into the health
// record, then resets. // record, then resets.
@@ -3742,57 +3673,52 @@ void BusFault_Handler(void) __attribute__((alias("HardFault_Handler")));
void UsageFault_Handler(void) __attribute__((alias("HardFault_Handler"))); void UsageFault_Handler(void) __attribute__((alias("HardFault_Handler")));
#endif #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) { void mg_health_init(void) {
char magic[] = MG_HEALTH_MAGIC; char magic[] = MG_HEALTH_MAGIC;
// struct mg_health *h = mg_health_get();
if (!mg_health_valid()) { // First boot, or RAM lost its contents if (!mg_health_valid()) { // First boot, or RAM lost its contents
memset(&mg_health_record, 0, sizeof(mg_health_record)); memset(&mg_health_record, 0, sizeof(mg_health_record));
memcpy(mg_health_record.magic, magic, sizeof(mg_health_record.magic)); memcpy(mg_health_record.magic, magic, sizeof(mg_health_record.magic));
} }
#if MG_ARCH == MG_ARCH_ESP32
#if CONFIG_IDF_TARGET_ARCH_XTENSA #if 0 && MG_ARCH == MG_ARCH_ESP32 && CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
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
// After a crash, copy the backtrace from the core dump in flash: the same // 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 ...". // addresses the panic handler prints as "Backtrace: PC:SP PC:SP ...".
// The core dump stays in flash, so check the reset reason, otherwise an // The core dump stays in flash, so check the reset reason, otherwise an
// old crash is reported on every boot // 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; 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)); memset(out, 0, sizeof(mg_health_record.data));
if (s == NULL) return; out[n++] = s.exc_pc;
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;
#if CONFIG_IDF_TARGET_ARCH_XTENSA #if CONFIG_IDF_TARGET_ARCH_XTENSA
out[n++] = s->ex_info.exc_a[0]; for (uint32_t i = 0; i < s.exc_bt_info.depth && n < max; i++) {
out[n++] = s->ex_info.exc_a[1]; if (s.exc_bt_info.bt[i] != s.exc_pc) out[n++] = s.exc_bt_info.bt[i];
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)
#elif CONFIG_IDF_TARGET_ARCH_RISCV #elif CONFIG_IDF_TARGET_ARCH_RISCV
out[n++] = s->ex_info.exc_ra; // No unwinder: take the return address, then words on the stack that
out[n++] = s->ex_info.exc_sp; // point into executable memory (a heuristic, may include stale values)
size_t len = s->exc_bt_info.dump_size / 4 ; // sizeof(uint32_t) if (n < max) out[n++] = s.ex_info.ra;
if (len > MG_HEALTH_DATA_SIZE - n) len = (MG_HEALTH_DATA_SIZE - n); for (uint32_t i = 0; i + 4 <= s.exc_bt_info.dump_size && n < max; i += 4) {
memcpy(&out[n], s->exc_bt_info.stackdump, 4 * len); 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 #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) { static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) {
@@ -3806,16 +3732,10 @@ 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 mg_print_crash_record(void (*fn)(char, void *), void *arg, va_list *ap) {
size_t len = 0; size_t len = 0;
if (mg_health_valid() && if (mg_health_valid() &&
(mg_health_record.reset_reason == (uint16_t) MG_HEALTH_RESET_FAULT mg_health_record.reset_reason == MG_HEALTH_RESET_FAULT) {
#if MG_ARCH == MG_ARCH_ESP32
|| mg_health_record.reset_reason == (uint16_t) MG_HEALTH_RESET_WATCHDOG
#endif
)) {
size_t n = sizeof(mg_health_record.data) / sizeof(mg_health_record.data[0]); 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]}", // len += mg_xprintf(fn, arg, "{%m:%hhu,%m:%u,%m:[%M]}", //
MG_ESC("version"), mg_health_record.magic[3], // 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, // MG_ESC("cpuid"), mg_health_record.cpuid, //
MG_ESC("data"), print_uint32, n, mg_health_record.data); MG_ESC("data"), print_uint32, n, mg_health_record.data);
} else { } else {
@@ -3824,7 +3744,6 @@ size_t mg_print_crash_record(void (*fn)(char, void *), void *arg, va_list *ap) {
(void) ap; (void) ap;
return len; return len;
} }
#endif
#ifdef MG_ENABLE_LINES #ifdef MG_ENABLE_LINES
#line 1 "src/http.c" #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 fs->rm(path); // MG_FS_WRITE appends, not truncates: drop any old file
if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) { if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) {
MG_ERROR(("Cannot open %s for upload", path)); MG_ERROR(("Cannot open %s for upload", path));
} else if (!mg_http_stream_body(c, hm, serve_upload_cb, fd)) { } else {
mg_fs_close(fd); 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 size_t received; // Number of body bytes received so far
void *user_data; // Passed to cb, free for cb to use void *user_data; // Passed to cb, free for cb to use
bool failed; // Set when cb returned false 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 // Event handler installed by mg_http_stream_body(). Feeds the body to the
@@ -4857,13 +4777,13 @@ static void mg_stream_handler(struct mg_connection *c, int ev, void *ev_data) {
(ptrdiff_t) c->fn_data; (ptrdiff_t) c->fn_data;
(void) ev_data; (void) ev_data;
if (ev == MG_EV_CLOSE || ev == MG_EV_ERROR) { 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() // 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); fn(NULL, NULL, &us->user_data);
} }
} else if (ev == MG_EV_READ && !c->is_draining) { } else if (ev == MG_EV_READ && !c->is_draining && !us->finished) {
if (c->recv.len > 0 && us->received < us->expected) { if (!us->failed && c->recv.len > 0 && us->received < us->expected) {
// Got some uploaded data. Feed by 512-byte aligned chunks, for OTA // 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 left = us->expected - us->received, r = c->recv.len;
size_t aligned = r < left ? MG_ROUND_DOWN(r, 512) : left; 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; us->received += aligned;
mg_iobuf_del(&c->recv, 0, aligned); // Shift the unprocessed tail 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 // Finished upload. Always call cb, even after error, so it can clean up
us->finished = true;
us->failed |= !fn(NULL, NULL, &us->user_data); 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", mg_http_reply(c, us->failed ? 500 : 200, NULL, "%zu %s\n",
us->received, us->failed ? "fail" : "ok"); 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 *, bool (*cb)(struct mg_http_message *, struct mg_str *,
void **user_data), void **),
void *user_data) { void *user_data) {
struct mg_upload_state *us = (struct mg_upload_state *) c->data; struct mg_upload_state *us = (struct mg_upload_state *) c->data;
struct mg_str *te = mg_http_get_header(hm, "Transfer-Encoding"); int code = 0;
bool handled = false; size_t head_len = hm->head.len;
// Catch upload requests early, without buffering whole body. Called on the // 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 // MG_EV_HTTP_HDRS event: we've received all HTTP headers but not necessarily
// the full HTTP body // the full HTTP body
if (hm->body.len == (size_t) ~0 || if (!c->is_client && mg_strcmp(hm->method, mg_str("POST")) != 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) { mg_strcmp(hm->method, mg_str("PUT")) != 0) {
// Not an upload request. Leave it to the caller 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)) { } else if (sizeof(*us) > sizeof(c->data)) {
if (!c->is_client) { // Nobody to reply to on a client connection code = 500;
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;
} }
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 { } else {
c->pfn = NULL; // Silence HTTP protocol handler
memset(us, 0, sizeof(*us)); memset(us, 0, sizeof(*us));
us->expected = hm->body.len; // Store number of bytes we expect us->expected = hm->body.len; // Store number of bytes we expect
us->user_data = user_data; 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 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_http_reply(c, 100, NULL, ""); // If curl expects, we continue
} }
mg_iobuf_del(&c->recv, 0, hm->head.len); // Delete HTTP headers mg_iobuf_del(&c->recv, 0, head_len); // Delete HTTP headers
c->fn = mg_stream_handler; // We own the connection from now
c->fn_data = (void *) (ptrdiff_t) cb;
mg_call(c, MG_EV_READ, &c->recv.len); // Process the body bytes we have 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) { 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) #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 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_tcpip_if *)) {
struct mg_l2_802154_data *d = struct mg_l2_802154_data *d =
(struct mg_l2_802154_data *) ifp->driver_data; (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[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); f[15] = d->tag, f[16] = (uint8_t) ((i << 4) | last);
memcpy(f + MGI_802154_HDR, p + i * MGI_802154_DATA, n); 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; return len;
} }
@@ -7404,7 +7333,6 @@ static void mg_mdash_fn(struct mg_connection *c, int ev, void *ev_data) {
} }
mg_iobuf_free(&io); mg_iobuf_free(&io);
} else if (ev == MG_EV_CLOSE) { } else if (ev == MG_EV_CLOSE) {
mg_health_coredump_end();
mg_mdash_conn = NULL; mg_mdash_conn = NULL;
MG_INFO(("disconnected, ram %lu", 0)); 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")); // 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) { void mg_mdash_init(struct mg_mgr *mgr) {
MG_INFO(("Initialising mdash agent, key [%s]", MG_MDASH_KEY)); MG_INFO(("Initialising mdash agent, key [%s]", MG_MDASH_KEY));
mg_rpc_add(&mgr->rpcs, mg_str("RPC.List"), mg_rpc_list, NULL); 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.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.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("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) { 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); (unsigned long) hm->body.len, (unsigned long) s_ota->size);
} else { } else {
MG_DEBUG(("Beginning OTA (%lu bytes)", (unsigned long) s_ota->size)); MG_DEBUG(("Beginning OTA (%lu bytes)", (unsigned long) s_ota->size));
if (!mg_http_stream_body(c, hm, s_firmware_cb, NULL)) { mg_http_stream_body(c, hm, s_firmware_cb, NULL);
mg_error(c, "Cannot stream firmware");
}
} }
} else if (ev == MG_EV_ERROR) { } else if (ev == MG_EV_ERROR) {
s_ota->fn((char *) ev_data); 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) { void mg_ota_poll(struct mg_mgr *mgr) {
static uint64_t t = 5000; // Fire first time 5 sec after boot 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) { void mg_delayms(unsigned int ms) {
uint64_t to = mg_millis() + ms + 1; uint64_t now, to = mg_millis() + ms + 1;
while (mg_millis() < to) (void) 0; while ((now = mg_millis()) < to)
if (now + ms + 1 < to) to = now + ms + 1;
} }
#if MG_ENABLE_CUSTOM_CALLOC #if MG_ENABLE_CUSTOM_CALLOC
@@ -34618,7 +34528,7 @@ static bool st67w6_init(uint8_t *mac) {
} }
// set clock, who cares ??? // set clock, who cares ???
if (!st67w6_at_cmd("AT+GET_CLOCK\r\n", 14)) return false; 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 // BT-ENABLED DEPENDENCY
// MODULE DEPENDENCY // MODULE DEPENDENCY
+23 -99
View File
@@ -253,11 +253,8 @@ extern "C" {
#include <esp_system.h> #include <esp_system.h>
#include <esp_mac.h> #include <esp_mac.h>
#include <esp_idf_version.h> #include <esp_idf_version.h>
#if MG_ENABLE_HEALTH // #include <esp_memory_utils.h> // For health record coredumps
#include <esp_memory_utils.h> // #include <esp_core_dump.h> // For health record coredumps
#include <esp_core_dump.h>
#include <esp_partition.h>
#endif
#define MG_PATH_MAX 128 #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. // 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. // 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 // with mg_match(). Always takes over the connection. Invalid requests receive
// request is not POST/PUT, is chunked, or has no Content-Length. // 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: // Callback upload_fn(hm, data, user_data) is called:
// - at start: hm is set. Open the file, set *user_data. // - at start: hm is set. Open the file, set *user_data.
// - per chunk: data is set, 512 byte aligned except the last. Not called // - per chunk: data is set, 512 byte aligned except the last. Not called
// again after a failure. // again after a failure.
// - at end: both NULL. Always called once, also when the connection drops // - at end: both NULL. Always called exactly once, including validation
// early, so release resources here. A truncated body looks like a complete // failures before start and connections that drop early. Release resources
// one: validate before committing. // here. A truncated body looks like a complete one: validate before
// Return true on success, false on error. // committing.
// The callback returns true on success, false on error. A failure stops
// streaming immediately and calls the end callback before closing.
// Example: // Example:
// if (ev == MG_EV_HTTP_HDRS && mg_match(hm->uri, mg_str("/fs/*"), NULL) { // if (ev == MG_EV_HTTP_HDRS && mg_match(hm->uri, mg_str("/fs/*"), NULL)) {
// mg_http_stream_body(c, hm, upload, NULL)); // mg_http_stream_body(c, hm, upload, NULL);
// } // }
// Full example: // Full example:
// tutorials/http/file-upload-single-post // tutorials/http/file-upload-single-post
// Related APIs: // Related APIs:
// mg_http_listen(), mg_http_next_multipart() // mg_http_listen(), mg_http_next_multipart()
// Notes: // Notes:
// After it returns true, this function hijacks the connection. Existing event // The existing event handler is no longer called. hm is invalid after this
// handler not called, hm is invalid, c->data and c->fn_data are modified // call; c->data and c->fn_data are reserved for streaming state.
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 (*upload_fn)(struct mg_http_message *, bool (*upload_fn)(struct mg_http_message *,
struct mg_str *, void **user_data), struct mg_str *, void **user_data),
void *user_data); void *user_data);
@@ -4223,9 +4223,8 @@ bool mg_ota_end(void);
// ```c // ```c
// if (ev == MG_EV_HTTP_HDRS) { // if (ev == MG_EV_HTTP_HDRS) {
// struct mg_http_message *hm = (struct mg_http_message *) ev_data; // struct mg_http_message *hm = (struct mg_http_message *) ev_data;
// if (mg_match(hm->uri, mg_str("/ota"), NULL) && // if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
// !mg_http_stream_body(c, hm, mg_http_ota_cb, NULL)) { // mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
// mg_http_reply(c, 400, "", "POST firmware with Content-Length\n");
// } // }
// } // }
// ``` // ```
@@ -4374,8 +4373,6 @@ void mg_ota_poll(struct mg_mgr *);
#if MG_ENABLE_HEALTH
#ifndef MG_HEALTH_MAGIC #ifndef MG_HEALTH_MAGIC
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version #define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
#endif #endif
@@ -4384,15 +4381,13 @@ void mg_ota_poll(struct mg_mgr *);
#define MG_HEALTH_RAM __attribute__((section(".mg_health"))) #define MG_HEALTH_RAM __attribute__((section(".mg_health")))
#endif #endif
#ifndef MG_HEALTH_DATA_SIZE #if !defined(MG_HEALTH_DATA_SIZE) && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5))
#if (defined(STM32H7_DEV_ID) || defined(STM32H5)) || defined(STM32C5)
#define MG_HEALTH_DATA_SIZE 250 #define MG_HEALTH_DATA_SIZE 250
#elif MG_ARCH == MG_ARCH_ESP32 #endif
#define MG_HEALTH_DATA_SIZE 23
#else #ifndef MG_HEALTH_DATA_SIZE
#define MG_HEALTH_DATA_SIZE 10 #define MG_HEALTH_DATA_SIZE 10
#endif #endif
#endif // MG_HEALTH_DATA_SIZE
// Places the record in RAM that survives a reset. Define in mongoose_config.h // 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"))) // 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 #define MG_HEALTH_RAM
#endif #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, // 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 // so these are ordered by how much they tell you: the most specific cause a
// device reports wins // device reports wins
@@ -4423,8 +4411,7 @@ enum mg_health_reason {
struct mg_health { struct mg_health {
char magic[4]; // MG_HEALTH_MAGIC when the record holds valid data char magic[4]; // MG_HEALTH_MAGIC when the record holds valid data
uint16_t reset_reason; // enum mg_health_reason that started this boot uint32_t reset_reason; // enum mg_health_reason that started this boot
uint16_t arch; // enum mg_health_arch, core architecture (ARM, etc)
uint32_t cpuid; // ARM CPU ID uint32_t cpuid; // ARM CPU ID
uint32_t data[MG_HEALTH_DATA_SIZE]; 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() #define MG_HEALTH_INIT() mg_health_init()
extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *); extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *);
extern void mg_health_init(void); 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 // Cloud connector API to https://mdash.net - device management cloud
+2 -5
View File
@@ -22,11 +22,8 @@
#include <esp_system.h> #include <esp_system.h>
#include <esp_mac.h> #include <esp_mac.h>
#include <esp_idf_version.h> #include <esp_idf_version.h>
#if MG_ENABLE_HEALTH // #include <esp_memory_utils.h> // For health record coredumps
#include <esp_memory_utils.h> // #include <esp_core_dump.h> // For health record coredumps
#include <esp_core_dump.h>
#include <esp_partition.h>
#endif
#define MG_PATH_MAX 128 #define MG_PATH_MAX 128
+3 -18
View File
@@ -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, static void mg_serve_fs(struct mg_connection *c, struct mg_http_message *hm,
struct mg_dash_user *u, bool stream) { struct mg_dash_user *u, bool stream) {
struct mg_dash *dash = (struct mg_dash *) c->fn_data; 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); mg_serve_fs(c, hm, u, true);
return; // Important - the serve_fs() call can invalidate hm return; // Important - the serve_fs() call can invalidate hm
} else if (mg_match(hm->uri, mg_str("/api/ota"), NULL)) { } else if (mg_match(hm->uri, mg_str("/api/ota"), NULL)) {
// Authenticated above. If streaming started, the stream handler owns // Authenticated above. Streaming owns the connection and c->data.
// the connection and c->data, so skip the marker check below mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
if (mg_http_stream_body(c, hm, otacb, NULL)) return; return;
} }
if (d->marker != '\0') mg_log_http_req(c, hm); if (d->marker != '\0') mg_log_http_req(c, hm);
} else if (ev == MG_EV_HTTP_MSG && d->marker != '\0') { } else if (ev == MG_EV_HTTP_MSG && d->marker != '\0') {
+1 -1
View File
@@ -365,7 +365,7 @@ static bool st67w6_init(uint8_t *mac) {
} }
// set clock, who cares ??? // set clock, who cares ???
if (!st67w6_at_cmd("AT+GET_CLOCK\r\n", 14)) return false; 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 // BT-ENABLED DEPENDENCY
// MODULE DEPENDENCY // MODULE DEPENDENCY
+35 -101
View File
@@ -1,66 +1,12 @@
#include "config.h" #include "config.h"
#include "health.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 // 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 // MG_HEALTH_RAM and the .mg_health region in the linker script
struct mg_health mg_health_record MG_HEALTH_RAM; struct mg_health mg_health_record MG_HEALTH_RAM;
// STM32 H7, H5, C5 support. Needs the _estack symbol from the linker script // STM32 H723 support. Needs the _estack symbol from the linker script
#if defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5) #if MG_ENABLE_HEALTH && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5))
// Fault handler body. Runs in exception context: no printf, no malloc, no // Fault handler body. Runs in exception context: no printf, no malloc, no
// blocking calls. Copies the unwind registers and raw stack into the health // blocking calls. Copies the unwind registers and raw stack into the health
// record, then resets. // record, then resets.
@@ -102,57 +48,52 @@ void BusFault_Handler(void) __attribute__((alias("HardFault_Handler")));
void UsageFault_Handler(void) __attribute__((alias("HardFault_Handler"))); void UsageFault_Handler(void) __attribute__((alias("HardFault_Handler")));
#endif #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) { void mg_health_init(void) {
char magic[] = MG_HEALTH_MAGIC; char magic[] = MG_HEALTH_MAGIC;
// struct mg_health *h = mg_health_get();
if (!mg_health_valid()) { // First boot, or RAM lost its contents if (!mg_health_valid()) { // First boot, or RAM lost its contents
memset(&mg_health_record, 0, sizeof(mg_health_record)); memset(&mg_health_record, 0, sizeof(mg_health_record));
memcpy(mg_health_record.magic, magic, sizeof(mg_health_record.magic)); memcpy(mg_health_record.magic, magic, sizeof(mg_health_record.magic));
} }
#if MG_ARCH == MG_ARCH_ESP32
#if CONFIG_IDF_TARGET_ARCH_XTENSA #if 0 && MG_ARCH == MG_ARCH_ESP32 && CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
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
// After a crash, copy the backtrace from the core dump in flash: the same // 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 ...". // addresses the panic handler prints as "Backtrace: PC:SP PC:SP ...".
// The core dump stays in flash, so check the reset reason, otherwise an // The core dump stays in flash, so check the reset reason, otherwise an
// old crash is reported on every boot // 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; 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)); memset(out, 0, sizeof(mg_health_record.data));
if (s == NULL) return; out[n++] = s.exc_pc;
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;
#if CONFIG_IDF_TARGET_ARCH_XTENSA #if CONFIG_IDF_TARGET_ARCH_XTENSA
out[n++] = s->ex_info.exc_a[0]; for (uint32_t i = 0; i < s.exc_bt_info.depth && n < max; i++) {
out[n++] = s->ex_info.exc_a[1]; if (s.exc_bt_info.bt[i] != s.exc_pc) out[n++] = s.exc_bt_info.bt[i];
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)
#elif CONFIG_IDF_TARGET_ARCH_RISCV #elif CONFIG_IDF_TARGET_ARCH_RISCV
out[n++] = s->ex_info.exc_ra; // No unwinder: take the return address, then words on the stack that
out[n++] = s->ex_info.exc_sp; // point into executable memory (a heuristic, may include stale values)
size_t len = s->exc_bt_info.dump_size / 4 ; // sizeof(uint32_t) if (n < max) out[n++] = s.ex_info.ra;
if (len > MG_HEALTH_DATA_SIZE - n) len = (MG_HEALTH_DATA_SIZE - n); for (uint32_t i = 0; i + 4 <= s.exc_bt_info.dump_size && n < max; i += 4) {
memcpy(&out[n], s->exc_bt_info.stackdump, 4 * len); 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 #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) { static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) {
@@ -166,16 +107,10 @@ 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 mg_print_crash_record(void (*fn)(char, void *), void *arg, va_list *ap) {
size_t len = 0; size_t len = 0;
if (mg_health_valid() && if (mg_health_valid() &&
(mg_health_record.reset_reason == (uint16_t) MG_HEALTH_RESET_FAULT mg_health_record.reset_reason == MG_HEALTH_RESET_FAULT) {
#if MG_ARCH == MG_ARCH_ESP32
|| mg_health_record.reset_reason == (uint16_t) MG_HEALTH_RESET_WATCHDOG
#endif
)) {
size_t n = sizeof(mg_health_record.data) / sizeof(mg_health_record.data[0]); 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]}", // len += mg_xprintf(fn, arg, "{%m:%hhu,%m:%u,%m:[%M]}", //
MG_ESC("version"), mg_health_record.magic[3], // 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, // MG_ESC("cpuid"), mg_health_record.cpuid, //
MG_ESC("data"), print_uint32, n, mg_health_record.data); MG_ESC("data"), print_uint32, n, mg_health_record.data);
} else { } else {
@@ -184,4 +119,3 @@ size_t mg_print_crash_record(void (*fn)(char, void *), void *arg, va_list *ap) {
(void) ap; (void) ap;
return len; return len;
} }
#endif
+5 -80
View File
@@ -5,8 +5,6 @@
#include "arch.h" #include "arch.h"
#include "printf.h" #include "printf.h"
#if MG_ENABLE_HEALTH
#ifndef MG_HEALTH_MAGIC #ifndef MG_HEALTH_MAGIC
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version #define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
#endif #endif
@@ -15,15 +13,13 @@
#define MG_HEALTH_RAM __attribute__((section(".mg_health"))) #define MG_HEALTH_RAM __attribute__((section(".mg_health")))
#endif #endif
#ifndef MG_HEALTH_DATA_SIZE #if !defined(MG_HEALTH_DATA_SIZE) && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5))
#if (defined(STM32H7_DEV_ID) || defined(STM32H5)) || defined(STM32C5)
#define MG_HEALTH_DATA_SIZE 250 #define MG_HEALTH_DATA_SIZE 250
#elif MG_ARCH == MG_ARCH_ESP32 #endif
#define MG_HEALTH_DATA_SIZE 23
#else #ifndef MG_HEALTH_DATA_SIZE
#define MG_HEALTH_DATA_SIZE 10 #define MG_HEALTH_DATA_SIZE 10
#endif #endif
#endif // MG_HEALTH_DATA_SIZE
// Places the record in RAM that survives a reset. Define in mongoose_config.h // 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"))) // to match the linker script, e.g. __attribute__((section(".mg_health")))
@@ -31,13 +27,6 @@
#define MG_HEALTH_RAM #define MG_HEALTH_RAM
#endif #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, // 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 // so these are ordered by how much they tell you: the most specific cause a
// device reports wins // device reports wins
@@ -54,8 +43,7 @@ enum mg_health_reason {
struct mg_health { struct mg_health {
char magic[4]; // MG_HEALTH_MAGIC when the record holds valid data char magic[4]; // MG_HEALTH_MAGIC when the record holds valid data
uint16_t reset_reason; // enum mg_health_reason that started this boot uint32_t reset_reason; // enum mg_health_reason that started this boot
uint16_t arch; // enum mg_health_arch, core architecture (ARM, etc)
uint32_t cpuid; // ARM CPU ID uint32_t cpuid; // ARM CPU ID
uint32_t data[MG_HEALTH_DATA_SIZE]; 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() #define MG_HEALTH_INIT() mg_health_init()
extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *); extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *);
extern void mg_health_init(void); 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
+41 -31
View File
@@ -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 fs->rm(path); // MG_FS_WRITE appends, not truncates: drop any old file
if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) { if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) {
MG_ERROR(("Cannot open %s for upload", path)); MG_ERROR(("Cannot open %s for upload", path));
} else if (!mg_http_stream_body(c, hm, serve_upload_cb, fd)) { } else {
mg_fs_close(fd); 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 size_t received; // Number of body bytes received so far
void *user_data; // Passed to cb, free for cb to use void *user_data; // Passed to cb, free for cb to use
bool failed; // Set when cb returned false 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 // Event handler installed by mg_http_stream_body(). Feeds the body to the
@@ -1026,13 +1027,13 @@ static void mg_stream_handler(struct mg_connection *c, int ev, void *ev_data) {
(ptrdiff_t) c->fn_data; (ptrdiff_t) c->fn_data;
(void) ev_data; (void) ev_data;
if (ev == MG_EV_CLOSE || ev == MG_EV_ERROR) { 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() // 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); fn(NULL, NULL, &us->user_data);
} }
} else if (ev == MG_EV_READ && !c->is_draining) { } else if (ev == MG_EV_READ && !c->is_draining && !us->finished) {
if (c->recv.len > 0 && us->received < us->expected) { if (!us->failed && c->recv.len > 0 && us->received < us->expected) {
// Got some uploaded data. Feed by 512-byte aligned chunks, for OTA // 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 left = us->expected - us->received, r = c->recv.len;
size_t aligned = r < left ? MG_ROUND_DOWN(r, 512) : left; 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; us->received += aligned;
mg_iobuf_del(&c->recv, 0, aligned); // Shift the unprocessed tail 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 // Finished upload. Always call cb, even after error, so it can clean up
us->finished = true;
us->failed |= !fn(NULL, NULL, &us->user_data); 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", mg_http_reply(c, us->failed ? 500 : 200, NULL, "%zu %s\n",
us->received, us->failed ? "fail" : "ok"); 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 *, bool (*cb)(struct mg_http_message *, struct mg_str *,
void **user_data), void **),
void *user_data) { void *user_data) {
struct mg_upload_state *us = (struct mg_upload_state *) c->data; struct mg_upload_state *us = (struct mg_upload_state *) c->data;
struct mg_str *te = mg_http_get_header(hm, "Transfer-Encoding"); int code = 0;
bool handled = false; size_t head_len = hm->head.len;
// Catch upload requests early, without buffering whole body. Called on the // 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 // MG_EV_HTTP_HDRS event: we've received all HTTP headers but not necessarily
// the full HTTP body // the full HTTP body
if (hm->body.len == (size_t) ~0 || if (!c->is_client && mg_strcmp(hm->method, mg_str("POST")) != 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) { mg_strcmp(hm->method, mg_str("PUT")) != 0) {
// Not an upload request. Leave it to the caller 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)) { } else if (sizeof(*us) > sizeof(c->data)) {
if (!c->is_client) { // Nobody to reply to on a client connection code = 500;
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;
} }
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 { } else {
c->pfn = NULL; // Silence HTTP protocol handler
memset(us, 0, sizeof(*us)); memset(us, 0, sizeof(*us));
us->expected = hm->body.len; // Store number of bytes we expect us->expected = hm->body.len; // Store number of bytes we expect
us->user_data = user_data; 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 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_http_reply(c, 100, NULL, ""); // If curl expects, we continue
} }
mg_iobuf_del(&c->recv, 0, hm->head.len); // Delete HTTP headers mg_iobuf_del(&c->recv, 0, head_len); // Delete HTTP headers
c->fn = mg_stream_handler; // We own the connection from now
c->fn_data = (void *) (ptrdiff_t) cb;
mg_call(c, MG_EV_READ, &c->recv.len); // Process the body bytes we have 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) { static bool is_hex_digit(int c) {
+14 -11
View File
@@ -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. // 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. // 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 // with mg_match(). Always takes over the connection. Invalid requests receive
// request is not POST/PUT, is chunked, or has no Content-Length. // 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: // Callback upload_fn(hm, data, user_data) is called:
// - at start: hm is set. Open the file, set *user_data. // - at start: hm is set. Open the file, set *user_data.
// - per chunk: data is set, 512 byte aligned except the last. Not called // - per chunk: data is set, 512 byte aligned except the last. Not called
// again after a failure. // again after a failure.
// - at end: both NULL. Always called once, also when the connection drops // - at end: both NULL. Always called exactly once, including validation
// early, so release resources here. A truncated body looks like a complete // failures before start and connections that drop early. Release resources
// one: validate before committing. // here. A truncated body looks like a complete one: validate before
// Return true on success, false on error. // committing.
// The callback returns true on success, false on error. A failure stops
// streaming immediately and calls the end callback before closing.
// Example: // Example:
// if (ev == MG_EV_HTTP_HDRS && mg_match(hm->uri, mg_str("/fs/*"), NULL) { // if (ev == MG_EV_HTTP_HDRS && mg_match(hm->uri, mg_str("/fs/*"), NULL)) {
// mg_http_stream_body(c, hm, upload, NULL)); // mg_http_stream_body(c, hm, upload, NULL);
// } // }
// Full example: // Full example:
// tutorials/http/file-upload-single-post // tutorials/http/file-upload-single-post
// Related APIs: // Related APIs:
// mg_http_listen(), mg_http_next_multipart() // mg_http_listen(), mg_http_next_multipart()
// Notes: // Notes:
// After it returns true, this function hijacks the connection. Existing event // The existing event handler is no longer called. hm is invalid after this
// handler not called, hm is invalid, c->data and c->fn_data are modified // call; c->data and c->fn_data are reserved for streaming state.
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 (*upload_fn)(struct mg_http_message *, bool (*upload_fn)(struct mg_http_message *,
struct mg_str *, void **user_data), struct mg_str *, void **user_data),
void *user_data); void *user_data);
+2 -2
View File
@@ -11,7 +11,7 @@
#define MGI_802154_DATA (125 - MGI_802154_HDR) #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 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_tcpip_if *)) {
struct mg_l2_802154_data *d = struct mg_l2_802154_data *d =
(struct mg_l2_802154_data *) ifp->driver_data; (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[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); f[15] = d->tag, f[16] = (uint8_t) ((i << 4) | last);
memcpy(f + MGI_802154_HDR, p + i * MGI_802154_DATA, n); 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; return len;
} }
-33
View File
@@ -89,7 +89,6 @@ static void mg_mdash_fn(struct mg_connection *c, int ev, void *ev_data) {
} }
mg_iobuf_free(&io); mg_iobuf_free(&io);
} else if (ev == MG_EV_CLOSE) { } else if (ev == MG_EV_CLOSE) {
mg_health_coredump_end();
mg_mdash_conn = NULL; mg_mdash_conn = NULL;
MG_INFO(("disconnected, ram %lu", 0)); 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")); // 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) { void mg_mdash_init(struct mg_mgr *mgr) {
MG_INFO(("Initialising mdash agent, key [%s]", MG_MDASH_KEY)); MG_INFO(("Initialising mdash agent, key [%s]", MG_MDASH_KEY));
mg_rpc_add(&mgr->rpcs, mg_str("RPC.List"), mg_rpc_list, NULL); 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.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.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("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) { void mg_mdash_poll(struct mg_mgr *mgr) {
+16 -3
View File
@@ -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); (unsigned long) hm->body.len, (unsigned long) s_ota->size);
} else { } else {
MG_DEBUG(("Beginning OTA (%lu bytes)", (unsigned long) s_ota->size)); MG_DEBUG(("Beginning OTA (%lu bytes)", (unsigned long) s_ota->size));
if (!mg_http_stream_body(c, hm, s_firmware_cb, NULL)) { mg_http_stream_body(c, hm, s_firmware_cb, NULL);
mg_error(c, "Cannot stream firmware");
}
} }
} else if (ev == MG_EV_ERROR) { } else if (ev == MG_EV_ERROR) {
s_ota->fn((char *) ev_data); 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) { void mg_ota_poll(struct mg_mgr *mgr) {
static uint64_t t = 5000; // Fire first time 5 sec after boot static uint64_t t = 5000; // Fire first time 5 sec after boot
+2 -3
View File
@@ -80,9 +80,8 @@ bool mg_ota_end(void);
// ```c // ```c
// if (ev == MG_EV_HTTP_HDRS) { // if (ev == MG_EV_HTTP_HDRS) {
// struct mg_http_message *hm = (struct mg_http_message *) ev_data; // struct mg_http_message *hm = (struct mg_http_message *) ev_data;
// if (mg_match(hm->uri, mg_str("/ota"), NULL) && // if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
// !mg_http_stream_body(c, hm, mg_http_ota_cb, NULL)) { // mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
// mg_http_reply(c, 400, "", "POST firmware with Content-Length\n");
// } // }
// } // }
// ``` // ```
+3 -2
View File
@@ -261,8 +261,9 @@ uint64_t mg_ntohll(uint64_t net) {
} }
void mg_delayms(unsigned int ms) { void mg_delayms(unsigned int ms) {
uint64_t to = mg_millis() + ms + 1; uint64_t now, to = mg_millis() + ms + 1;
while (mg_millis() < to) (void) 0; while ((now = mg_millis()) < to)
if (now + ms + 1 < to) to = now + ms + 1;
} }
#if MG_ENABLE_CUSTOM_CALLOC #if MG_ENABLE_CUSTOM_CALLOC
-7
View File
@@ -555,7 +555,6 @@ static void task2(void *args) {
DASHBOARD("select_child_write"); DASHBOARD("select_child_write");
DASHBOARD("poll_child_write"); DASHBOARD("poll_child_write");
#if 0
s_error = false; s_error = false;
s_backlog_ready = s_backlog_full = s_backlog_accept = false; s_backlog_ready = s_backlog_full = s_backlog_accept = false;
ASSERT(xTaskCreate(backlog_task, "backlog", 512, NULL, ASSERT(xTaskCreate(backlog_task, "backlog", 512, NULL,
@@ -570,12 +569,6 @@ static void task2(void *args) {
while (!client_done(first, &status)) vTaskDelay(1); while (!client_done(first, &status)) vTaskDelay(1);
check_client(status); check_client(status);
DASHBOARD("listen_backlog"); DASHBOARD("listen_backlog");
#else
(void) first;
(void) second;
(void) status;
(void) backlog_task;
#endif
s_error = false; s_error = false;
// atask at higher priority than task1 // atask at higher priority than task1
+108 -6
View File
@@ -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) { static void su(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_HDRS) { if (ev == MG_EV_HTTP_HDRS) {
mg_http_stream_body(c, (struct mg_http_message *) ev_data, su_cb, NULL); 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) { if (ev == MG_EV_CONNECT) {
mg_printf(c, "GET / HTTP/1.0\r\n\r\n"); mg_printf(c, "GET / HTTP/1.0\r\n\r\n");
} else if (ev == MG_EV_HTTP_HDRS) { } else if (ev == MG_EV_HTTP_HDRS) {
ASSERT(mg_http_stream_body(c, (struct mg_http_message *) ev_data, cr_cb, mg_http_stream_body(c, (struct mg_http_message *) ev_data, cr_cb, NULL);
NULL));
} }
} }
@@ -3208,12 +3205,103 @@ static void cs(struct mg_connection *c, int ev, void *ev_data) {
(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) { static void test_http_upload(void) {
struct mg_mgr mgr; struct mg_mgr mgr;
const char *url = "http://127.0.0.1:12352"; const char *url = "http://127.0.0.1:12352";
int i; int i;
const char *s = "ok (8 foo\nbar\n)\n"; const char *s = "ok (8 foo\nbar\n)\n";
test_http_stream_errors();
mg_mgr_init(&mgr); mg_mgr_init(&mgr);
mg_http_listen(&mgr, url, us, NULL); mg_http_listen(&mgr, url, us, NULL);
mg_http_connect(&mgr, url, uc, (void *) &s); mg_http_connect(&mgr, url, uc, (void *) &s);
@@ -3257,7 +3345,7 @@ static void test_http_upload(void) {
ASSERT(mgr.conns == NULL); 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]; char buf[FETCH_BUF_SIZE];
s_su_done = s_su_end = 0; 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" "POST /su_chunked.txt HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n\r\n" "Transfer-Encoding: chunked\r\n\r\n"
"3\r\nfoo\r\n0\r\n\r\n") == 400); "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); ASSERT(fopen("su_chunked.txt", "r") == NULL);
mg_mgr_free(&mgr); mg_mgr_free(&mgr);
ASSERT(mgr.conns == NULL); ASSERT(mgr.conns == NULL);
@@ -5010,6 +5098,20 @@ static void test_dash(void) {
"Authorization: Basic YWRtaW46YWRtaW4=\n\n") == 200); "Authorization: Basic YWRtaW46YWRtaW4=\n\n") == 200);
ASSERT(cmpbody(buf, login_admin_expected) == 0); 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 // set3 is admin-only
ASSERT(fetch(&mgr, buf, url, ASSERT(fetch(&mgr, buf, url,
"GET /api/get/set3 HTTP/1.0\n" "GET /api/get/set3 HTTP/1.0\n"
+3 -1
View File
@@ -1,9 +1,11 @@
THISDIR = $(realpath $(CURDIR)) THISDIR = $(realpath $(CURDIR))
ROOTDIR = $(realpath $(CURDIR)/../../..) ROOTDIR = $(realpath $(CURDIR)/../../..)
DOCKER ?= docker run --rm $(DA) -v $(ROOTDIR):$(ROOTDIR) -w $(THISDIR) espressif/idf 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 PORT ?= /dev/ttyUSB0
CFLAGS_EXTRA ?= -DMG_TLS=MG_TLS_BUILTIN
all: example all: example
example: example:
@@ -12,3 +12,5 @@ idf_component_register(SRCS "main.c"
espcoredump espcoredump
nvs_flash nvs_flash
app_update) app_update)
separate_arguments(CFLAGS_EXTRA)
component_compile_options(${CFLAGS_EXTRA})
+6 -3
View File
@@ -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), struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)}; .key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts); 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) { } else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
size_t total = heap_caps_get_total_size(MALLOC_CAP_DEFAULT); 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) { void app_main(void) {
MG_HEALTH_INIT(); void wifi_init(const char *ssid, const char *pass);
wifi_init(WIFI_SSID, WIFI_PASS); wifi_init(WIFI_SSID, WIFI_PASS);
struct mg_mgr mgr; struct mg_mgr mgr;
@@ -1,6 +1 @@
#define MG_ARCH MG_ARCH_ESP32 #define MG_ARCH MG_ARCH_ESP32
#define MG_ENABLE_HEALTH 1
#define MG_TLS MG_TLS_BUILTIN
//#define MG_TLS MG_TLS_MBED
+6 -1
View File
@@ -9,7 +9,12 @@ static void t_fn(void *arg) { // Tick periodically
} }
static void fn(struct mg_connection *c, int ev, void *ev_data) { 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; (void) ev_data;
} }
+6 -1
View File
@@ -9,7 +9,12 @@ static void t_fn(void *arg) { // Tick periodically
} }
static void fn(struct mg_connection *c, int ev, void *ev_data) { 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; (void) ev_data;
} }
+6 -6
View File
@@ -3,24 +3,24 @@
extern void mgmp_init(void); extern void mgmp_init(void);
extern void mgmp_poll(int); extern void mgmp_poll(int);
STATIC mp_obj_t mongoose_init(void) { static mp_obj_t mongoose_init(void) {
mgmp_init(); mgmp_init();
return mp_const_none; 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)); mgmp_poll(mp_obj_get_int(t_obj));
return mp_const_none; 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___name__), MP_ROM_QSTR(MP_QSTR_mongoose) },
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&mongoose_init_obj) }, { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&mongoose_init_obj) },
{ MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&mongoose_poll_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 = { const mp_obj_module_t mongoose_module = {
.base = { &mp_type_module }, .base = { &mp_type_module },
+1 -1
View File
@@ -120,7 +120,7 @@ void mg_mqtt_poll(struct mg_mgr *mgr) {
// Reconnect if connection is closed, and send MQTT PINGs to keep // Reconnect if connection is closed, and send MQTT PINGs to keep
// the connection alive or to detect connection loss // 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) { if (s_mqtt_conn == NULL) {
struct mg_mqtt_opts opts = { struct mg_mqtt_opts opts = {
.clean = true, .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), struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)}; .key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts); 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 } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { 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), struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)}; .key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts); 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 } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { 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), struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)}; .key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts); 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 } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
+5
View File
@@ -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), struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)}; .key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts); 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) { } else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
+5
View File
@@ -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), struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)}; .key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts); 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 } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
+5
View File
@@ -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), struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)}; .key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts); 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 } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
+5
View File
@@ -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), struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)}; .key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts); 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 } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
+6 -1
View File
@@ -5,7 +5,12 @@
#include "mongoose.h" #include "mongoose.h"
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { 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; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
uint64_t tick = to_ms_since_boot(get_absolute_time()); uint64_t tick = to_ms_since_boot(get_absolute_time());
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
+6 -1
View File
@@ -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) { 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; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
uint64_t tick = to_ms_since_boot(get_absolute_time()); uint64_t tick = to_ms_since_boot(get_absolute_time());
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
+6 -1
View File
@@ -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) { 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; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
uint64_t tick = to_ms_since_boot(get_absolute_time()); uint64_t tick = to_ms_since_boot(get_absolute_time());
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
+5
View File
@@ -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), struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)}; .key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts); 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 } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
uint64_t tick = to_ms_since_boot(get_absolute_time()); uint64_t tick = to_ms_since_boot(get_absolute_time());
+5
View File
@@ -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), struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)}; .key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts); 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 } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
uint64_t tick = to_ms_since_boot(get_absolute_time()); uint64_t tick = to_ms_since_boot(get_absolute_time());
+5
View File
@@ -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), struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)}; .key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts); 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 } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
uint64_t tick = to_ms_since_boot(get_absolute_time()); 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), struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)}; .key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts); 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) { } else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { 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) { 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; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/debug"), NULL)) { if (mg_match(hm->uri, mg_str("/api/debug"), NULL)) {
int level = mg_json_get_long(hm->body, "$.level", MG_LL_DEBUG); 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), struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)}; .key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts); 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 } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { 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) { 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; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
(void) hm; (void) hm;
mg_http_reply(c, 200, "", "hi\n"); 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) { 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; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/debug"), NULL)) { if (mg_match(hm->uri, mg_str("/api/debug"), NULL)) {
int level = mg_json_get_long(hm->body, "$.level", MG_LL_DEBUG); 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), struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)}; .key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts); 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 } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { 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), struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)}; .key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts); 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 } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { 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) { static void fn(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->fn_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; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/hello"), if (mg_match(hm->uri, mg_str("/api/hello"),
NULL)) { // Request to /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), struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)}; .key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts); 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) { } else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { 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) { 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; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
mg_http_reply(c, 200, "", "{%m:%lu}\n", MG_ESC("tick"), HAL_GetTick()); 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) { 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; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick()); 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) { 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; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick()); 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), struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)}; .key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts); 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) { } else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { 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), struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)}; .key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts); 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 } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { 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), struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)}; .key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts); 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 } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { 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), struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)}; .key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts); 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 } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { 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), struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)}; .key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts); 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 } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { 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), struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)}; .key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts); 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 } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { 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), struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)}; .key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts); 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 } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { 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) { 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; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/debug"), NULL)) { if (mg_match(hm->uri, mg_str("/api/debug"), NULL)) {
int level = mg_json_get_long(hm->body, "$.level", MG_LL_DEBUG); 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 // https://mongoose.ws/documentation/#2-minute-integration-guide
static void fn(struct mg_connection *c, int ev, void *ev_data) { 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; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
mg_http_reply(c, 200, "", "ok %p\r\n", hm); mg_http_reply(c, 200, "", "ok %p\r\n", hm);
} }
+5
View File
@@ -14,6 +14,11 @@ static void wcb(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_ACCEPT && c->is_tls) { if (ev == MG_EV_ACCEPT && c->is_tls) {
struct mg_tls_opts opts = {.cert = (char *) s_ssl_cert, .key = (char *) s_ssl_key}; struct mg_tls_opts opts = {.cert = (char *) s_ssl_cert, .key = (char *) s_ssl_key};
mg_tls_init(c, &opts); 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) { } else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = ev_data; struct mg_http_message *hm = ev_data;
MG_INFO(("%.*s %.*s %ld", (int) hm->method.len, hm->method.buf, MG_INFO(("%.*s %.*s %ld", (int) hm->method.len, hm->method.buf,