mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-26 12:14:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b766c65615 | ||
|
|
8296b88fb6 | ||
|
|
a5efbf0592 | ||
|
|
37c98dd893 | ||
|
|
770a4ff27e | ||
|
|
b6560306ba | ||
|
|
5b39c93478 | ||
|
|
086008b716 | ||
|
|
35eb50a1e6 | ||
|
|
431e35ae8c | ||
|
|
25e2a99d8e |
No files matched your search
@@ -637,7 +637,6 @@ jobs:
|
|||||||
example:
|
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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Mongoose - Embedded Web Server / Embedded Network Library
|
# Mongoose: a full-stack embedded networking library
|
||||||
|
|
||||||
[](https://opensource.org/licenses/gpl-2.0.php)
|
[](https://opensource.org/licenses/gpl-2.0.php)
|
||||||
[](https://github.com/cesanta/mongoose/actions)
|
[](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
@@ -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
@@ -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
@@ -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
@@ -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') {
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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) {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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"
|
||||||
|
|||||||
@@ -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})
|
||||||
@@ -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
|
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 },
|
||||||
|
|||||||
@@ -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)) {
|
||||||
|
|||||||
@@ -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)) {
|
||||||
|
|||||||
@@ -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)) {
|
||||||
|
|||||||
@@ -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)) {
|
||||||
|
|||||||
@@ -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,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)) {
|
||||||
|
|||||||
@@ -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)) {
|
||||||
|
|||||||
@@ -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)) {
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in new issue
Block a user