mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-31 13:00:49 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb63083bc3 | ||
|
|
b766c65615 | ||
|
|
8296b88fb6 | ||
|
|
a5efbf0592 | ||
|
|
37c98dd893 | ||
|
|
770a4ff27e | ||
|
|
b6560306ba | ||
|
|
5b39c93478 | ||
|
|
086008b716 | ||
|
|
35eb50a1e6 | ||
|
|
431e35ae8c | ||
|
|
25e2a99d8e |
No files matched your search
@@ -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/)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+277
-105
@@ -167,6 +167,7 @@ fail:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
#if MG_ENABLE_BSD_SOCKETS && MG_ENABLE_FREERTOS
|
#if MG_ENABLE_BSD_SOCKETS && MG_ENABLE_FREERTOS
|
||||||
@@ -277,6 +278,57 @@ static struct mg_connection *bsd_new_conn(struct mg_bsd_sock *s) {
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mirror net_builtin.c::trim_len() logic
|
||||||
|
static bool bsd_udp_too_big(struct mg_connection *c, size_t len) {
|
||||||
|
struct mg_tcpip_if *ifp = c->mgr->ifp;
|
||||||
|
size_t hlen = c->rem.is_ip6 ? 48 : 32; // IP plus UDP headers
|
||||||
|
size_t cap = ifp->mtu - hlen;
|
||||||
|
size_t room = ifp->tx.len - (ifp->framesize - ifp->l2mtu) - hlen;
|
||||||
|
|
||||||
|
return len > cap || len > room;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool bsd_udp_send(struct mg_connection *c, uint32_t ip, uint16_t port,
|
||||||
|
const void *buf, size_t len) {
|
||||||
|
struct mg_bsd_udp_frame *h;
|
||||||
|
size_t off = c->send.len, total = sizeof(*h) + len;
|
||||||
|
|
||||||
|
if (mg_iobuf_add(&c->send, off, NULL, total) != total) return false;
|
||||||
|
h = (struct mg_bsd_udp_frame *) (c->send.buf + off);
|
||||||
|
h->ip = ip;
|
||||||
|
h->port = port;
|
||||||
|
h->len = (uint16_t) len;
|
||||||
|
memcpy(h + 1, buf, len);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t bsd_recv(struct mg_bsd_sock *s, void *buf, size_t len,
|
||||||
|
int flags, struct sockaddr *src, socklen_t *addrlen) {
|
||||||
|
struct mg_connection *c = s->c;
|
||||||
|
size_t n;
|
||||||
|
|
||||||
|
if (!s->is_udp) {
|
||||||
|
n = c->recv.len < len ? c->recv.len : len;
|
||||||
|
memcpy(buf, c->recv.buf, n);
|
||||||
|
if (!(flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, n);
|
||||||
|
} else {
|
||||||
|
struct mg_bsd_udp_frame *h = (struct mg_bsd_udp_frame *) c->recv.buf;
|
||||||
|
assert(c->recv.len >= sizeof(*h));
|
||||||
|
assert(h->len <= c->recv.len - sizeof(*h));
|
||||||
|
n = h->len < len ? h->len : len;
|
||||||
|
memcpy(buf, h + 1, n);
|
||||||
|
if (src != NULL && addrlen != NULL) {
|
||||||
|
struct sockaddr_in *sa = (struct sockaddr_in *) src;
|
||||||
|
sa->sin_family = AF_INET;
|
||||||
|
sa->sin_port = h->port;
|
||||||
|
sa->sin_addr.s_addr = h->ip;
|
||||||
|
*addrlen = sizeof(*sa);
|
||||||
|
}
|
||||||
|
if (!(flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, sizeof(*h) + h->len);
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
#if MG_ENABLE_FREERTOS
|
#if MG_ENABLE_FREERTOS
|
||||||
// Complete operations blocked on c before Mongoose destroys it.
|
// Complete operations blocked on c before Mongoose destroys it.
|
||||||
static void bsd_finish_waiters(struct mg_bsd_sock *s, bool error) {
|
static void bsd_finish_waiters(struct mg_bsd_sock *s, bool error) {
|
||||||
@@ -457,9 +509,9 @@ static int bsd_ready(struct mg_bsd_sock *s) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (s->c->recv.len > 0 || s->c->is_draining) ready |= BSD_READ;
|
if (s->c->recv.len > 0 || s->c->is_draining) ready |= BSD_READ;
|
||||||
if (!s->is_udp && !s->c->is_connecting && !s->c->is_closing &&
|
if ((s->is_udp || !s->c->is_connecting) && !s->c->is_closing &&
|
||||||
!s->c->is_draining)
|
!s->c->is_draining)
|
||||||
ready |= BSD_WRITE; // Established TCP socket
|
ready |= BSD_WRITE; // UDP socket or established TCP socket
|
||||||
return ready;
|
return ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,7 +525,7 @@ static void bsd_close(struct mg_bsd_sock *s) {
|
|||||||
if (t->orphan && t->listener == s) {
|
if (t->orphan && t->listener == s) {
|
||||||
if (t->c != NULL) {
|
if (t->c != NULL) {
|
||||||
t->c->fn_data = NULL;
|
t->c->fn_data = NULL;
|
||||||
mg_close_conn(t->c);
|
t->c->is_draining = 1;
|
||||||
t->c = NULL;
|
t->c = NULL;
|
||||||
}
|
}
|
||||||
memset(t, 0, sizeof(*t)); // Release orphan
|
memset(t, 0, sizeof(*t)); // Release orphan
|
||||||
@@ -481,7 +533,7 @@ static void bsd_close(struct mg_bsd_sock *s) {
|
|||||||
}
|
}
|
||||||
if (s->c != NULL) {
|
if (s->c != NULL) {
|
||||||
s->c->fn_data = NULL;
|
s->c->fn_data = NULL;
|
||||||
mg_close_conn(s->c);
|
s->c->is_draining = 1;
|
||||||
s->c = NULL;
|
s->c = NULL;
|
||||||
}
|
}
|
||||||
memset(s, 0, sizeof(*s)); // Release socket
|
memset(s, 0, sizeof(*s)); // Release socket
|
||||||
@@ -660,8 +712,8 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
|||||||
cmd->result = -1, cmd->err = ECONNREFUSED;
|
cmd->result = -1, cmd->err = ECONNREFUSED;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!c->is_connecting) {
|
if (s->is_udp || !c->is_connecting) {
|
||||||
cmd->result = 0;
|
cmd->result = 0; // UDP may still resolve ARP; TCP waits for SYN/SYN-ACK
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (s->nonblock) {
|
if (s->nonblock) {
|
||||||
@@ -685,11 +737,8 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (c->recv.len > 0) {
|
if (c->recv.len > 0) {
|
||||||
size_t n = cmd->op_recv.len;
|
cmd->result = (int) bsd_recv(s, cmd->op_recv.buf, cmd->op_recv.len,
|
||||||
if (c->recv.len < n) n = c->recv.len;
|
cmd->op_recv.flags, NULL, NULL);
|
||||||
memcpy(cmd->op_recv.buf, c->recv.buf, n);
|
|
||||||
if (!(cmd->op_recv.flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, n);
|
|
||||||
cmd->result = (int) n;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (s->err != 0) {
|
if (s->err != 0) {
|
||||||
@@ -710,10 +759,17 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
|||||||
case BSD_OP_SEND:
|
case BSD_OP_SEND:
|
||||||
if ((s = bsd_get(cmd->op_send.fd)) == NULL) {
|
if ((s = bsd_get(cmd->op_send.fd)) == NULL) {
|
||||||
cmd->result = -1, cmd->err = EBADF;
|
cmd->result = -1, cmd->err = EBADF;
|
||||||
} else if ((c = s->c) == NULL || s->err != 0 || c->is_closing ||
|
} else if (s->err != 0 || s->closed ||
|
||||||
c->is_draining) {
|
((c = s->c) != NULL && (c->is_closing || c->is_draining))) {
|
||||||
cmd->result = -1, cmd->err = EPIPE;
|
cmd->result = -1, cmd->err = EPIPE;
|
||||||
} else if (mg_send(c, cmd->op_send.buf, cmd->op_send.len)) {
|
} else if (c == NULL || (s->is_udp && !c->is_client)) {
|
||||||
|
cmd->result = -1, cmd->err = s->is_udp ? EDESTADDRREQ : EPIPE;
|
||||||
|
} else if (s->is_udp && bsd_udp_too_big(c, cmd->op_send.len)) {
|
||||||
|
cmd->result = -1, cmd->err = EMSGSIZE;
|
||||||
|
} else if ((s->is_udp ? bsd_udp_send(c, c->rem.addr.ip4, c->rem.port,
|
||||||
|
cmd->op_send.buf, cmd->op_send.len)
|
||||||
|
: mg_send(c, cmd->op_send.buf,
|
||||||
|
cmd->op_send.len))) {
|
||||||
cmd->result = (int) cmd->op_send.len;
|
cmd->result = (int) cmd->op_send.len;
|
||||||
} else if (s->nonblock || (cmd->op_send.flags & MSG_DONTWAIT)) {
|
} else if (s->nonblock || (cmd->op_send.flags & MSG_DONTWAIT)) {
|
||||||
cmd->result = -1, cmd->err = EAGAIN;
|
cmd->result = -1, cmd->err = EAGAIN;
|
||||||
@@ -728,16 +784,20 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
|||||||
case BSD_OP_SENDTO:
|
case BSD_OP_SENDTO:
|
||||||
if ((s = bsd_get(cmd->op_sendto.fd)) == NULL) {
|
if ((s = bsd_get(cmd->op_sendto.fd)) == NULL) {
|
||||||
cmd->result = -1, cmd->err = EBADF;
|
cmd->result = -1, cmd->err = EBADF;
|
||||||
|
} else if (!s->is_udp) {
|
||||||
|
cmd->result = -1, cmd->err = EOPNOTSUPP;
|
||||||
} else if ((c = s->c) == NULL || s->err != 0 || c->is_closing ||
|
} else if ((c = s->c) == NULL || s->err != 0 || c->is_closing ||
|
||||||
c->is_draining) {
|
c->is_draining) {
|
||||||
cmd->result = -1, cmd->err = EPIPE;
|
cmd->result = -1, cmd->err = EPIPE;
|
||||||
|
} else if (c->is_client) {
|
||||||
|
cmd->result = -1, cmd->err = EISCONN;
|
||||||
} else {
|
} else {
|
||||||
const struct sockaddr_in *sa =
|
const struct sockaddr_in *sa =
|
||||||
(const struct sockaddr_in *) cmd->op_sendto.addr;
|
(const struct sockaddr_in *) cmd->op_sendto.addr;
|
||||||
c->rem.port = sa->sin_port;
|
if (bsd_udp_too_big(c, cmd->op_sendto.len)) {
|
||||||
c->rem.addr.ip4 = sa->sin_addr.s_addr;
|
cmd->result = -1, cmd->err = EMSGSIZE;
|
||||||
c->rem.is_ip6 = false;
|
} else if (bsd_udp_send(c, sa->sin_addr.s_addr, sa->sin_port,
|
||||||
if (mg_send(c, cmd->op_sendto.buf, cmd->op_sendto.len)) {
|
cmd->op_sendto.buf, cmd->op_sendto.len)) {
|
||||||
cmd->result = (int) cmd->op_sendto.len;
|
cmd->result = (int) cmd->op_sendto.len;
|
||||||
} else if (s->nonblock || (cmd->op_sendto.flags & MSG_DONTWAIT)) {
|
} else if (s->nonblock || (cmd->op_sendto.flags & MSG_DONTWAIT)) {
|
||||||
cmd->result = -1, cmd->err = EAGAIN;
|
cmd->result = -1, cmd->err = EAGAIN;
|
||||||
@@ -754,6 +814,9 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
|||||||
if ((s = bsd_get(cmd->op_recvfrom.fd)) == NULL) {
|
if ((s = bsd_get(cmd->op_recvfrom.fd)) == NULL) {
|
||||||
cmd->result = -1, cmd->err = EBADF;
|
cmd->result = -1, cmd->err = EBADF;
|
||||||
return true;
|
return true;
|
||||||
|
} else if (!s->is_udp) {
|
||||||
|
cmd->result = -1, cmd->err = EOPNOTSUPP;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
if ((c = s->c) == NULL) {
|
if ((c = s->c) == NULL) {
|
||||||
cmd->result = s->closed && s->err == 0 ? 0 : -1;
|
cmd->result = s->closed && s->err == 0 ? 0 : -1;
|
||||||
@@ -761,18 +824,11 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (c->recv.len > 0) {
|
if (c->recv.len > 0) {
|
||||||
size_t n = cmd->op_recvfrom.len;
|
cmd->result = (int) bsd_recv(s, cmd->op_recvfrom.buf,
|
||||||
if (c->recv.len < n) n = c->recv.len;
|
cmd->op_recvfrom.len,
|
||||||
memcpy(cmd->op_recvfrom.buf, c->recv.buf, n);
|
cmd->op_recvfrom.flags,
|
||||||
if (!(cmd->op_recvfrom.flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, n);
|
cmd->op_recvfrom.src,
|
||||||
if (cmd->op_recvfrom.src != NULL && cmd->op_recvfrom.addrlen != NULL) {
|
cmd->op_recvfrom.addrlen);
|
||||||
struct sockaddr_in *sa = (struct sockaddr_in *) cmd->op_recvfrom.src;
|
|
||||||
sa->sin_family = AF_INET;
|
|
||||||
sa->sin_port = c->rem.port;
|
|
||||||
sa->sin_addr.s_addr = c->rem.addr.ip4;
|
|
||||||
*cmd->op_recvfrom.addrlen = sizeof(*sa);
|
|
||||||
}
|
|
||||||
cmd->result = (int) n;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (s->err != 0) {
|
if (s->err != 0) {
|
||||||
@@ -1668,21 +1724,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 +1770,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') {
|
||||||
@@ -4644,8 +4685,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4779,6 +4820,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
|
||||||
@@ -4791,13 +4833,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;
|
||||||
@@ -4808,10 +4850,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");
|
||||||
}
|
}
|
||||||
@@ -4820,48 +4865,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) {
|
||||||
@@ -5786,7 +5837,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;
|
||||||
@@ -5804,7 +5855,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;
|
||||||
}
|
}
|
||||||
@@ -9279,6 +9330,36 @@ static void l2addr_resolved(struct mg_connection *c);
|
|||||||
static uint8_t *get_return_l2addr(struct mg_tcpip_if *ifp, struct mg_addr *rem,
|
static uint8_t *get_return_l2addr(struct mg_tcpip_if *ifp, struct mg_addr *rem,
|
||||||
bool is_udp, struct pkt *pkt);
|
bool is_udp, struct pkt *pkt);
|
||||||
|
|
||||||
|
#if MG_ENABLE_BSD_SOCKETS
|
||||||
|
static uint8_t *sendto_cache_find(struct mg_tcpip_if *ifp, uint32_t ip) {
|
||||||
|
struct mg_tcpip_sendto_entry *a;
|
||||||
|
for (a = ifp->sendto; a < ifp->sendto + MG_TCPIP_SENDTO_CACHE_SIZE; a++) {
|
||||||
|
if (a->ip == ip) return (uint8_t *) &a->addr;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sendto_cache_add(struct mg_tcpip_if *ifp, uint32_t ip,
|
||||||
|
const uint8_t *addr) {
|
||||||
|
struct mg_tcpip_sendto_entry *a;
|
||||||
|
for (a = ifp->sendto; a < ifp->sendto + MG_TCPIP_SENDTO_CACHE_SIZE; a++) {
|
||||||
|
if (a->ip == ip) return; // already cached, skip
|
||||||
|
if (a->ip == 0) break; // not present, first empty slot
|
||||||
|
}
|
||||||
|
if (a == ifp->sendto + MG_TCPIP_SENDTO_CACHE_SIZE) { // no room
|
||||||
|
memmove(ifp->sendto, ifp->sendto + 1,
|
||||||
|
sizeof(ifp->sendto[0]) * (MG_TCPIP_SENDTO_CACHE_SIZE - 1));
|
||||||
|
a--; // make room and point to last entry (just made)
|
||||||
|
}
|
||||||
|
a->ip = ip; // populate this entry
|
||||||
|
memcpy(&a->addr, addr, sizeof(a->addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sendto_cache_clear(struct mg_tcpip_if *ifp) {
|
||||||
|
memset(ifp->sendto, 0, sizeof(ifp->sendto));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// RFC826, ARP assumes Ethernet MAC addresses
|
// RFC826, ARP assumes Ethernet MAC addresses
|
||||||
static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||||
if (pkt->arp->op == mg_htons(1) && pkt->arp->tpa == ifp->ip) {
|
if (pkt->arp->op == mg_htons(1) && pkt->arp->tpa == ifp->ip) {
|
||||||
@@ -9884,30 +9965,57 @@ static void proto_error(struct mg_connection *c, bool r, const char *f, ...) {
|
|||||||
static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||||
struct mg_connection *c = getpeer(ifp->mgr, pkt, true);
|
struct mg_connection *c = getpeer(ifp->mgr, pkt, true);
|
||||||
struct connstate *s;
|
struct connstate *s;
|
||||||
|
struct mg_addr rem;
|
||||||
uint8_t *l2addr;
|
uint8_t *l2addr;
|
||||||
|
size_t len = pkt->pay.len;
|
||||||
if (c == NULL) return false; // No UDP listener on this port
|
if (c == NULL) return false; // No UDP listener on this port
|
||||||
s = (struct connstate *) (c + 1);
|
s = (struct connstate *) (c + 1);
|
||||||
c->rem.port = pkt->udp->sport;
|
|
||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
if (c->loc.is_ip6) { // matching of v4/v6 to dest is done bt getpeer()
|
if (c->loc.is_ip6) { // matching of v4/v6 to dest is done bt getpeer()
|
||||||
if (!udp6csum_ok(pkt->ip6, pkt->udp)) return false;
|
if (!udp6csum_ok(pkt->ip6, pkt->udp)) return false;
|
||||||
c->rem.addr.ip6[0] = pkt->ip6->src[0],
|
if (c->is_bsd && c->is_client &&
|
||||||
c->rem.addr.ip6[1] = pkt->ip6->src[1], c->rem.is_ip6 = true;
|
(c->rem.port != pkt->udp->sport ||
|
||||||
|
!MG_IP6MATCH(c->rem.addr.ip6, pkt->ip6->src)))
|
||||||
|
return true;
|
||||||
|
rem.addr.ip6[0] = pkt->ip6->src[0],
|
||||||
|
rem.addr.ip6[1] = pkt->ip6->src[1], rem.is_ip6 = true;
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
if (!udpcsum_ok(pkt->ip, pkt->udp)) return false;
|
if (!udpcsum_ok(pkt->ip, pkt->udp)) return false;
|
||||||
c->rem.addr.ip4 = pkt->ip->src;
|
if (c->is_bsd && c->is_client &&
|
||||||
|
(c->rem.port != pkt->udp->sport || c->rem.addr.ip4 != pkt->ip->src))
|
||||||
|
return true;
|
||||||
|
rem.addr.ip4 = pkt->ip->src, rem.is_ip6 = false;
|
||||||
}
|
}
|
||||||
if ((l2addr = get_return_l2addr(ifp, &c->rem, true, pkt)) == NULL)
|
rem.port = pkt->udp->sport;
|
||||||
|
if (!c->is_bsd || c->is_client) c->rem = rem;
|
||||||
|
if ((l2addr = get_return_l2addr(ifp, &rem, true, pkt)) == NULL)
|
||||||
return false; // safety net for lousy networks
|
return false; // safety net for lousy networks
|
||||||
memcpy(s->mac, l2addr, sizeof(s->mac));
|
if (!c->is_bsd || c->is_client) memcpy(s->mac, l2addr, sizeof(s->mac));
|
||||||
|
#if MG_ENABLE_BSD_SOCKETS
|
||||||
|
if (c->is_bsd && !rem.is_ip6 && MG_IPISLOCAL(ifp, rem.addr.ip4))
|
||||||
|
sendto_cache_add(ifp, rem.addr.ip4, l2addr);
|
||||||
|
#endif
|
||||||
|
#if MG_ENABLE_BSD_SOCKETS
|
||||||
|
if (c->is_bsd) len += sizeof(struct mg_bsd_udp_frame);
|
||||||
|
#endif
|
||||||
if (c->recv.len >= MG_MAX_RECV_SIZE) {
|
if (c->recv.len >= MG_MAX_RECV_SIZE) {
|
||||||
proto_error(c, false, "max_recv_buf_size reached");
|
proto_error(c, false, "max_recv_buf_size reached");
|
||||||
} else if (c->recv.size - c->recv.len < pkt->pay.len &&
|
} else if (c->recv.size - c->recv.len < len &&
|
||||||
!mg_iobuf_resize(&c->recv, c->recv.len + pkt->pay.len)) {
|
!mg_iobuf_resize(&c->recv, c->recv.len + len)) {
|
||||||
proto_error(c, false, "oom");
|
proto_error(c, false, "oom");
|
||||||
} else {
|
} else {
|
||||||
|
#if MG_ENABLE_BSD_SOCKETS
|
||||||
|
if (c->is_bsd) {
|
||||||
|
struct mg_bsd_udp_frame *h =
|
||||||
|
(struct mg_bsd_udp_frame *) &c->recv.buf[c->recv.len];
|
||||||
|
h->ip = rem.addr.ip4;
|
||||||
|
h->port = rem.port;
|
||||||
|
h->len = (uint16_t) pkt->pay.len;
|
||||||
|
c->recv.len += sizeof(*h);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len);
|
memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len);
|
||||||
c->recv.len += pkt->pay.len;
|
c->recv.len += pkt->pay.len;
|
||||||
mg_call(c, MG_EV_READ, &pkt->pay.len);
|
mg_call(c, MG_EV_READ, &pkt->pay.len);
|
||||||
@@ -10960,6 +11068,9 @@ void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) {
|
|||||||
|
|
||||||
static void close_conn(struct mg_connection *c) {
|
static void close_conn(struct mg_connection *c) {
|
||||||
struct connstate *s = (struct connstate *) (c + 1);
|
struct connstate *s = (struct connstate *) (c + 1);
|
||||||
|
#if MG_ENABLE_BSD_SOCKETS
|
||||||
|
if (c->is_bsd && c->is_udp) sendto_cache_clear(c->mgr->ifp);
|
||||||
|
#endif
|
||||||
mg_iobuf_free(&s->txq);
|
mg_iobuf_free(&s->txq);
|
||||||
mg_close_conn(c);
|
mg_close_conn(c);
|
||||||
}
|
}
|
||||||
@@ -10981,6 +11092,11 @@ static void send_syn(struct mg_connection *c) {
|
|||||||
static void l2addr_resolved(struct mg_connection *c) {
|
static void l2addr_resolved(struct mg_connection *c) {
|
||||||
if (c->is_udp) {
|
if (c->is_udp) {
|
||||||
c->is_connecting = 0;
|
c->is_connecting = 0;
|
||||||
|
#if MG_ENABLE_BSD_SOCKETS
|
||||||
|
if (c->is_bsd)
|
||||||
|
sendto_cache_add(c->mgr->ifp, c->rem.addr.ip4,
|
||||||
|
((struct connstate *) (c + 1))->mac);
|
||||||
|
#endif
|
||||||
mg_call(c, MG_EV_CONNECT, NULL);
|
mg_call(c, MG_EV_CONNECT, NULL);
|
||||||
} else {
|
} else {
|
||||||
send_syn(c);
|
send_syn(c);
|
||||||
@@ -10995,20 +11111,25 @@ void mg_connect_resolved(struct mg_connection *c) {
|
|||||||
s->retransmit = ifp->enable_tcp_retransmit;
|
s->retransmit = ifp->enable_tcp_retransmit;
|
||||||
s->txq.align = MG_TCPIP_TXQ_ALIGN;
|
s->txq.align = MG_TCPIP_TXQ_ALIGN;
|
||||||
c->is_resolving = 0;
|
c->is_resolving = 0;
|
||||||
if (ifp->eport < MG_EPHEMERAL_PORT_BASE) ifp->eport = MG_EPHEMERAL_PORT_BASE;
|
if (c->loc.port == 0) {
|
||||||
|
if (ifp->eport < MG_EPHEMERAL_PORT_BASE)
|
||||||
|
ifp->eport = MG_EPHEMERAL_PORT_BASE;
|
||||||
c->loc.port = mg_htons(ifp->eport++);
|
c->loc.port = mg_htons(ifp->eport++);
|
||||||
|
} // otherwise retain an existing bound port
|
||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
if (c->rem.is_ip6) {
|
if (c->rem.is_ip6) {
|
||||||
if (c->rem.addr.ip6[0] == ifp->ip6ll[0]) { // same local LAN, use ll
|
if (c->loc.addr.ip6[0] == 0 && c->loc.addr.ip6[1] == 0 &&
|
||||||
|
c->rem.addr.ip6[0] == ifp->ip6ll[0]) { // same local LAN, use ll
|
||||||
c->loc.addr.ip6[0] = ifp->ip6ll[0], c->loc.addr.ip6[1] = ifp->ip6ll[1];
|
c->loc.addr.ip6[0] = ifp->ip6ll[0], c->loc.addr.ip6[1] = ifp->ip6ll[1];
|
||||||
} else { // use global address
|
} else if (c->loc.addr.ip6[0] == 0 && c->loc.addr.ip6[1] == 0) {
|
||||||
|
// Use global address
|
||||||
c->loc.addr.ip6[0] = ifp->ip6[0], c->loc.addr.ip6[1] = ifp->ip6[1];
|
c->loc.addr.ip6[0] = ifp->ip6[0], c->loc.addr.ip6[1] = ifp->ip6[1];
|
||||||
}
|
} // otherwise retain an existing bound address
|
||||||
c->loc.is_ip6 = true;
|
c->loc.is_ip6 = true;
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
c->loc.addr.ip4 = ifp->ip;
|
if (c->loc.addr.ip4 == 0) c->loc.addr.ip4 = ifp->ip;
|
||||||
}
|
}
|
||||||
MG_DEBUG(("%lu %M -> %M", c->id, mg_print_ip_port, &c->loc, mg_print_ip_port,
|
MG_DEBUG(("%lu %M -> %M", c->id, mg_print_ip_port, &c->loc, mg_print_ip_port,
|
||||||
&c->rem));
|
&c->rem));
|
||||||
@@ -11039,11 +11160,20 @@ void mg_connect_resolved(struct mg_connection *c) {
|
|||||||
uint32_t rem_ip = c->rem.addr.ip4;
|
uint32_t rem_ip = c->rem.addr.ip4;
|
||||||
if (ifp->ip && MG_IPISLOCAL(ifp, rem_ip) &&
|
if (ifp->ip && MG_IPISLOCAL(ifp, rem_ip) &&
|
||||||
rem_ip != ifp->gw) { // skip if gw (onstatechange -> ARP)
|
rem_ip != ifp->gw) { // skip if gw (onstatechange -> ARP)
|
||||||
// If we're in the same LAN, fire an ARP lookup.
|
// We're in the same LAN
|
||||||
|
#if MG_ENABLE_BSD_SOCKETS
|
||||||
|
if (c->is_bsd && c->is_udp && // see if we already have L2 data
|
||||||
|
(l2addr = sendto_cache_find(ifp, rem_ip)) != NULL) {
|
||||||
|
memcpy(s->mac, l2addr, sizeof(s->mac));
|
||||||
|
l2addr_resolved(c);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{ // fire an ARP lookup
|
||||||
MG_DEBUG(("%lu ARP lookup...", c->id));
|
MG_DEBUG(("%lu ARP lookup...", c->id));
|
||||||
mg_tcpip_arp_request(ifp, rem_ip, NULL);
|
mg_tcpip_arp_request(ifp, rem_ip, NULL);
|
||||||
settmout(c, MIP_TTYPE_ARP);
|
settmout(c, MIP_TTYPE_ARP);
|
||||||
c->is_arplooking = 1;
|
c->is_arplooking = 1;
|
||||||
|
}
|
||||||
} else if (ifp->gw_ready) {
|
} else if (ifp->gw_ready) {
|
||||||
memcpy(s->mac, ifp->gwmac, sizeof(s->mac));
|
memcpy(s->mac, ifp->gwmac, sizeof(s->mac));
|
||||||
l2addr_resolved(c);
|
l2addr_resolved(c);
|
||||||
@@ -11063,13 +11193,36 @@ bool mg_open_listener(struct mg_connection *c, const char *url) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void write_conn(struct mg_connection *c) {
|
static void write_conn(struct mg_connection *c) {
|
||||||
long len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len)
|
long len;
|
||||||
|
size_t off = 0;
|
||||||
|
bool empty = false;
|
||||||
|
#if MG_ENABLE_BSD_SOCKETS
|
||||||
|
struct mg_bsd_udp_frame *h;
|
||||||
|
if (c->is_bsd && c->is_udp) {
|
||||||
|
if (c->is_connecting) return;
|
||||||
|
h = (struct mg_bsd_udp_frame *) c->send.buf;
|
||||||
|
assert(c->send.len >= sizeof(*h));
|
||||||
|
assert(h->len <= c->send.len - sizeof(*h));
|
||||||
|
c->rem.port = h->port;
|
||||||
|
c->rem.is_ip6 = false;
|
||||||
|
if (!c->is_client && c->rem.addr.ip4 != h->ip) {
|
||||||
|
c->rem.addr.ip4 = h->ip;
|
||||||
|
mg_connect_resolved(c);
|
||||||
|
if (c->is_connecting) return;
|
||||||
|
}
|
||||||
|
len = mg_io_send(c, h + 1, h->len);
|
||||||
|
off = sizeof(*h), empty = h->len == 0;
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len)
|
||||||
: mg_io_send(c, c->send.buf, c->send.len);
|
: mg_io_send(c, c->send.buf, c->send.len);
|
||||||
|
}
|
||||||
// TODO(): mg_tls_send() may return 0 forever on steady OOM
|
// TODO(): mg_tls_send() may return 0 forever on steady OOM
|
||||||
if (len == MG_IO_ERR) {
|
if (len == MG_IO_ERR) {
|
||||||
proto_error(c, false, "tx err");
|
proto_error(c, false, "tx err");
|
||||||
} else if (len > 0) {
|
} else if (len > 0 || empty) {
|
||||||
mg_iobuf_del(&c->send, 0, (size_t) len);
|
mg_iobuf_del(&c->send, 0, off + (size_t) len);
|
||||||
mg_call(c, MG_EV_WRITE, &len);
|
mg_call(c, MG_EV_WRITE, &len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11429,9 +11582,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);
|
||||||
@@ -11464,6 +11615,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
|
||||||
|
|
||||||
@@ -15365,7 +15531,11 @@ static long recv_raw(struct mg_connection *c, void *buf, size_t len) {
|
|||||||
union usa usa;
|
union usa usa;
|
||||||
socklen_t slen = tousa(&c->rem, &usa);
|
socklen_t slen = tousa(&c->rem, &usa);
|
||||||
n = recvfrom(FD(c), (char *) buf, len, 0, &usa.sa, &slen);
|
n = recvfrom(FD(c), (char *) buf, len, 0, &usa.sa, &slen);
|
||||||
if (n > 0) tomgaddr(&usa, &c->rem, slen != sizeof(usa.sin));
|
if (n > 0) {
|
||||||
|
struct mg_addr rem;
|
||||||
|
tomgaddr(&usa, &rem, slen != sizeof(usa.sin));
|
||||||
|
c->rem = rem;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
n = recv(FD(c), (char *) buf, len, MSG_NONBLOCKING);
|
n = recv(FD(c), (char *) buf, len, MSG_NONBLOCKING);
|
||||||
}
|
}
|
||||||
@@ -29391,8 +29561,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
|
||||||
@@ -34519,7 +34690,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
|
||||||
@@ -34937,6 +35108,7 @@ struct mg_tcpip_driver mg_tcpip_driver_stm32f = {
|
|||||||
|
|
||||||
#if MG_ENABLE_TCPIP && (MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_MCXN || \
|
#if MG_ENABLE_TCPIP && (MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_MCXN || \
|
||||||
MG_ENABLE_DRIVER_STM32N)
|
MG_ENABLE_DRIVER_STM32N)
|
||||||
|
// STM32C: vendor modded single-queue Synopsys v5.4, same settings as STM32H
|
||||||
// STM32H: vendor modded single-queue Synopsys v4.2
|
// STM32H: vendor modded single-queue Synopsys v4.2
|
||||||
// STM32N: dual-queue GbE Synopsys v5.2 with no hash table option, 64-bit AXI
|
// STM32N: dual-queue GbE Synopsys v5.2 with no hash table option, 64-bit AXI
|
||||||
// MCXNx4x: dual-queue Synopsys v5.2 with no hash table option
|
// MCXNx4x: dual-queue Synopsys v5.2 with no hash table option
|
||||||
|
|||||||
+57
-14
@@ -2724,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);
|
||||||
@@ -4220,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");
|
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
@@ -4618,6 +4620,17 @@ struct mg_tcpip_driver {
|
|||||||
typedef void (*mg_tcpip_event_handler_t)(struct mg_tcpip_if *ifp, int ev,
|
typedef void (*mg_tcpip_event_handler_t)(struct mg_tcpip_if *ifp, int ev,
|
||||||
void *ev_data);
|
void *ev_data);
|
||||||
|
|
||||||
|
#if MG_ENABLE_BSD_SOCKETS
|
||||||
|
#ifndef MG_TCPIP_SENDTO_CACHE_SIZE
|
||||||
|
#define MG_TCPIP_SENDTO_CACHE_SIZE 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct mg_tcpip_sendto_entry {
|
||||||
|
uint32_t ip;
|
||||||
|
struct mg_l2addr addr;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
// Events delivered to mg_tcpip_if.fn (user) and mg_tcpip_if.pfn (driver).
|
// Events delivered to mg_tcpip_if.fn (user) and mg_tcpip_if.pfn (driver).
|
||||||
// Each entry shows the ev_data type to cast inside the handler.
|
// Each entry shows the ev_data type to cast inside the handler.
|
||||||
@@ -4680,6 +4693,9 @@ struct mg_tcpip_if {
|
|||||||
enum mg_l2type l2type; // Layer-2 type: Ethernet, PPP, etc. (see l2.h)
|
enum mg_l2type l2type; // Layer-2 type: Ethernet, PPP, etc. (see l2.h)
|
||||||
union mg_l2data l2data; // Layer-2 config and state (see l2.h)
|
union mg_l2data l2data; // Layer-2 config and state (see l2.h)
|
||||||
char *dns4_url; // Active DNS server URL (set from DHCP or config)
|
char *dns4_url; // Active DNS server URL (set from DHCP or config)
|
||||||
|
#if MG_ENABLE_BSD_SOCKETS
|
||||||
|
struct mg_tcpip_sendto_entry sendto[MG_TCPIP_SENDTO_CACHE_SIZE]; // Local IPv4 neighbors
|
||||||
|
#endif
|
||||||
uint64_t now; // Current time in ms (updated each mg_mgr_poll)
|
uint64_t now; // Current time in ms (updated each mg_mgr_poll)
|
||||||
uint64_t timer_1000ms; // Timestamp of last 1-second tick
|
uint64_t timer_1000ms; // Timestamp of last 1-second tick
|
||||||
uint64_t lease_expire; // DHCP lease expiry time in ms
|
uint64_t lease_expire; // DHCP lease expiry time in ms
|
||||||
@@ -4891,10 +4907,37 @@ void mg_bsd_poll(struct mg_mgr *);
|
|||||||
#ifndef EPIPE
|
#ifndef EPIPE
|
||||||
#define EPIPE 32
|
#define EPIPE 32
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef EOPNOTSUPP
|
||||||
|
#define EOPNOTSUPP 95
|
||||||
|
#endif
|
||||||
|
#ifndef EISCONN
|
||||||
|
#define EISCONN 106
|
||||||
|
#endif
|
||||||
|
#ifndef EDESTADDRREQ
|
||||||
|
#define EDESTADDRREQ 121
|
||||||
|
#endif
|
||||||
|
#ifndef EMSGSIZE
|
||||||
|
#define EMSGSIZE 90
|
||||||
|
#endif
|
||||||
#ifndef EPROTONOSUPPORT
|
#ifndef EPROTONOSUPPORT
|
||||||
#define EPROTONOSUPPORT 93
|
#define EPROTONOSUPPORT 93
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__DCC__)
|
||||||
|
#pragma pack(1)
|
||||||
|
#else
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
#endif
|
||||||
|
struct mg_bsd_udp_frame {
|
||||||
|
uint32_t ip;
|
||||||
|
uint16_t port, len;
|
||||||
|
};
|
||||||
|
#if defined(__DCC__)
|
||||||
|
#pragma pack(0)
|
||||||
|
#else
|
||||||
|
#pragma pack(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
#if MG_ENABLE_BSD_PROTOTYPES
|
#if MG_ENABLE_BSD_PROTOTYPES
|
||||||
typedef unsigned int socklen_t;
|
typedef unsigned int socklen_t;
|
||||||
typedef unsigned long nfds_t;
|
typedef unsigned long nfds_t;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "bsd.h"
|
#include "bsd.h"
|
||||||
#include "dns.h"
|
#include "dns.h"
|
||||||
|
#include "util.h"
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
#if MG_ENABLE_BSD_SOCKETS && MG_ENABLE_FREERTOS
|
#if MG_ENABLE_BSD_SOCKETS && MG_ENABLE_FREERTOS
|
||||||
@@ -110,6 +111,57 @@ static struct mg_connection *bsd_new_conn(struct mg_bsd_sock *s) {
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mirror net_builtin.c::trim_len() logic
|
||||||
|
static bool bsd_udp_too_big(struct mg_connection *c, size_t len) {
|
||||||
|
struct mg_tcpip_if *ifp = c->mgr->ifp;
|
||||||
|
size_t hlen = c->rem.is_ip6 ? 48 : 32; // IP plus UDP headers
|
||||||
|
size_t cap = ifp->mtu - hlen;
|
||||||
|
size_t room = ifp->tx.len - (ifp->framesize - ifp->l2mtu) - hlen;
|
||||||
|
|
||||||
|
return len > cap || len > room;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool bsd_udp_send(struct mg_connection *c, uint32_t ip, uint16_t port,
|
||||||
|
const void *buf, size_t len) {
|
||||||
|
struct mg_bsd_udp_frame *h;
|
||||||
|
size_t off = c->send.len, total = sizeof(*h) + len;
|
||||||
|
|
||||||
|
if (mg_iobuf_add(&c->send, off, NULL, total) != total) return false;
|
||||||
|
h = (struct mg_bsd_udp_frame *) (c->send.buf + off);
|
||||||
|
h->ip = ip;
|
||||||
|
h->port = port;
|
||||||
|
h->len = (uint16_t) len;
|
||||||
|
memcpy(h + 1, buf, len);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t bsd_recv(struct mg_bsd_sock *s, void *buf, size_t len,
|
||||||
|
int flags, struct sockaddr *src, socklen_t *addrlen) {
|
||||||
|
struct mg_connection *c = s->c;
|
||||||
|
size_t n;
|
||||||
|
|
||||||
|
if (!s->is_udp) {
|
||||||
|
n = c->recv.len < len ? c->recv.len : len;
|
||||||
|
memcpy(buf, c->recv.buf, n);
|
||||||
|
if (!(flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, n);
|
||||||
|
} else {
|
||||||
|
struct mg_bsd_udp_frame *h = (struct mg_bsd_udp_frame *) c->recv.buf;
|
||||||
|
assert(c->recv.len >= sizeof(*h));
|
||||||
|
assert(h->len <= c->recv.len - sizeof(*h));
|
||||||
|
n = h->len < len ? h->len : len;
|
||||||
|
memcpy(buf, h + 1, n);
|
||||||
|
if (src != NULL && addrlen != NULL) {
|
||||||
|
struct sockaddr_in *sa = (struct sockaddr_in *) src;
|
||||||
|
sa->sin_family = AF_INET;
|
||||||
|
sa->sin_port = h->port;
|
||||||
|
sa->sin_addr.s_addr = h->ip;
|
||||||
|
*addrlen = sizeof(*sa);
|
||||||
|
}
|
||||||
|
if (!(flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, sizeof(*h) + h->len);
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
#if MG_ENABLE_FREERTOS
|
#if MG_ENABLE_FREERTOS
|
||||||
// Complete operations blocked on c before Mongoose destroys it.
|
// Complete operations blocked on c before Mongoose destroys it.
|
||||||
static void bsd_finish_waiters(struct mg_bsd_sock *s, bool error) {
|
static void bsd_finish_waiters(struct mg_bsd_sock *s, bool error) {
|
||||||
@@ -290,9 +342,9 @@ static int bsd_ready(struct mg_bsd_sock *s) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (s->c->recv.len > 0 || s->c->is_draining) ready |= BSD_READ;
|
if (s->c->recv.len > 0 || s->c->is_draining) ready |= BSD_READ;
|
||||||
if (!s->is_udp && !s->c->is_connecting && !s->c->is_closing &&
|
if ((s->is_udp || !s->c->is_connecting) && !s->c->is_closing &&
|
||||||
!s->c->is_draining)
|
!s->c->is_draining)
|
||||||
ready |= BSD_WRITE; // Established TCP socket
|
ready |= BSD_WRITE; // UDP socket or established TCP socket
|
||||||
return ready;
|
return ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,7 +358,7 @@ static void bsd_close(struct mg_bsd_sock *s) {
|
|||||||
if (t->orphan && t->listener == s) {
|
if (t->orphan && t->listener == s) {
|
||||||
if (t->c != NULL) {
|
if (t->c != NULL) {
|
||||||
t->c->fn_data = NULL;
|
t->c->fn_data = NULL;
|
||||||
mg_close_conn(t->c);
|
t->c->is_draining = 1;
|
||||||
t->c = NULL;
|
t->c = NULL;
|
||||||
}
|
}
|
||||||
memset(t, 0, sizeof(*t)); // Release orphan
|
memset(t, 0, sizeof(*t)); // Release orphan
|
||||||
@@ -314,7 +366,7 @@ static void bsd_close(struct mg_bsd_sock *s) {
|
|||||||
}
|
}
|
||||||
if (s->c != NULL) {
|
if (s->c != NULL) {
|
||||||
s->c->fn_data = NULL;
|
s->c->fn_data = NULL;
|
||||||
mg_close_conn(s->c);
|
s->c->is_draining = 1;
|
||||||
s->c = NULL;
|
s->c = NULL;
|
||||||
}
|
}
|
||||||
memset(s, 0, sizeof(*s)); // Release socket
|
memset(s, 0, sizeof(*s)); // Release socket
|
||||||
@@ -493,8 +545,8 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
|||||||
cmd->result = -1, cmd->err = ECONNREFUSED;
|
cmd->result = -1, cmd->err = ECONNREFUSED;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!c->is_connecting) {
|
if (s->is_udp || !c->is_connecting) {
|
||||||
cmd->result = 0;
|
cmd->result = 0; // UDP may still resolve ARP; TCP waits for SYN/SYN-ACK
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (s->nonblock) {
|
if (s->nonblock) {
|
||||||
@@ -518,11 +570,8 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (c->recv.len > 0) {
|
if (c->recv.len > 0) {
|
||||||
size_t n = cmd->op_recv.len;
|
cmd->result = (int) bsd_recv(s, cmd->op_recv.buf, cmd->op_recv.len,
|
||||||
if (c->recv.len < n) n = c->recv.len;
|
cmd->op_recv.flags, NULL, NULL);
|
||||||
memcpy(cmd->op_recv.buf, c->recv.buf, n);
|
|
||||||
if (!(cmd->op_recv.flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, n);
|
|
||||||
cmd->result = (int) n;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (s->err != 0) {
|
if (s->err != 0) {
|
||||||
@@ -543,10 +592,17 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
|||||||
case BSD_OP_SEND:
|
case BSD_OP_SEND:
|
||||||
if ((s = bsd_get(cmd->op_send.fd)) == NULL) {
|
if ((s = bsd_get(cmd->op_send.fd)) == NULL) {
|
||||||
cmd->result = -1, cmd->err = EBADF;
|
cmd->result = -1, cmd->err = EBADF;
|
||||||
} else if ((c = s->c) == NULL || s->err != 0 || c->is_closing ||
|
} else if (s->err != 0 || s->closed ||
|
||||||
c->is_draining) {
|
((c = s->c) != NULL && (c->is_closing || c->is_draining))) {
|
||||||
cmd->result = -1, cmd->err = EPIPE;
|
cmd->result = -1, cmd->err = EPIPE;
|
||||||
} else if (mg_send(c, cmd->op_send.buf, cmd->op_send.len)) {
|
} else if (c == NULL || (s->is_udp && !c->is_client)) {
|
||||||
|
cmd->result = -1, cmd->err = s->is_udp ? EDESTADDRREQ : EPIPE;
|
||||||
|
} else if (s->is_udp && bsd_udp_too_big(c, cmd->op_send.len)) {
|
||||||
|
cmd->result = -1, cmd->err = EMSGSIZE;
|
||||||
|
} else if ((s->is_udp ? bsd_udp_send(c, c->rem.addr.ip4, c->rem.port,
|
||||||
|
cmd->op_send.buf, cmd->op_send.len)
|
||||||
|
: mg_send(c, cmd->op_send.buf,
|
||||||
|
cmd->op_send.len))) {
|
||||||
cmd->result = (int) cmd->op_send.len;
|
cmd->result = (int) cmd->op_send.len;
|
||||||
} else if (s->nonblock || (cmd->op_send.flags & MSG_DONTWAIT)) {
|
} else if (s->nonblock || (cmd->op_send.flags & MSG_DONTWAIT)) {
|
||||||
cmd->result = -1, cmd->err = EAGAIN;
|
cmd->result = -1, cmd->err = EAGAIN;
|
||||||
@@ -561,16 +617,20 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
|||||||
case BSD_OP_SENDTO:
|
case BSD_OP_SENDTO:
|
||||||
if ((s = bsd_get(cmd->op_sendto.fd)) == NULL) {
|
if ((s = bsd_get(cmd->op_sendto.fd)) == NULL) {
|
||||||
cmd->result = -1, cmd->err = EBADF;
|
cmd->result = -1, cmd->err = EBADF;
|
||||||
|
} else if (!s->is_udp) {
|
||||||
|
cmd->result = -1, cmd->err = EOPNOTSUPP;
|
||||||
} else if ((c = s->c) == NULL || s->err != 0 || c->is_closing ||
|
} else if ((c = s->c) == NULL || s->err != 0 || c->is_closing ||
|
||||||
c->is_draining) {
|
c->is_draining) {
|
||||||
cmd->result = -1, cmd->err = EPIPE;
|
cmd->result = -1, cmd->err = EPIPE;
|
||||||
|
} else if (c->is_client) {
|
||||||
|
cmd->result = -1, cmd->err = EISCONN;
|
||||||
} else {
|
} else {
|
||||||
const struct sockaddr_in *sa =
|
const struct sockaddr_in *sa =
|
||||||
(const struct sockaddr_in *) cmd->op_sendto.addr;
|
(const struct sockaddr_in *) cmd->op_sendto.addr;
|
||||||
c->rem.port = sa->sin_port;
|
if (bsd_udp_too_big(c, cmd->op_sendto.len)) {
|
||||||
c->rem.addr.ip4 = sa->sin_addr.s_addr;
|
cmd->result = -1, cmd->err = EMSGSIZE;
|
||||||
c->rem.is_ip6 = false;
|
} else if (bsd_udp_send(c, sa->sin_addr.s_addr, sa->sin_port,
|
||||||
if (mg_send(c, cmd->op_sendto.buf, cmd->op_sendto.len)) {
|
cmd->op_sendto.buf, cmd->op_sendto.len)) {
|
||||||
cmd->result = (int) cmd->op_sendto.len;
|
cmd->result = (int) cmd->op_sendto.len;
|
||||||
} else if (s->nonblock || (cmd->op_sendto.flags & MSG_DONTWAIT)) {
|
} else if (s->nonblock || (cmd->op_sendto.flags & MSG_DONTWAIT)) {
|
||||||
cmd->result = -1, cmd->err = EAGAIN;
|
cmd->result = -1, cmd->err = EAGAIN;
|
||||||
@@ -587,6 +647,9 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
|||||||
if ((s = bsd_get(cmd->op_recvfrom.fd)) == NULL) {
|
if ((s = bsd_get(cmd->op_recvfrom.fd)) == NULL) {
|
||||||
cmd->result = -1, cmd->err = EBADF;
|
cmd->result = -1, cmd->err = EBADF;
|
||||||
return true;
|
return true;
|
||||||
|
} else if (!s->is_udp) {
|
||||||
|
cmd->result = -1, cmd->err = EOPNOTSUPP;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
if ((c = s->c) == NULL) {
|
if ((c = s->c) == NULL) {
|
||||||
cmd->result = s->closed && s->err == 0 ? 0 : -1;
|
cmd->result = s->closed && s->err == 0 ? 0 : -1;
|
||||||
@@ -594,18 +657,11 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (c->recv.len > 0) {
|
if (c->recv.len > 0) {
|
||||||
size_t n = cmd->op_recvfrom.len;
|
cmd->result = (int) bsd_recv(s, cmd->op_recvfrom.buf,
|
||||||
if (c->recv.len < n) n = c->recv.len;
|
cmd->op_recvfrom.len,
|
||||||
memcpy(cmd->op_recvfrom.buf, c->recv.buf, n);
|
cmd->op_recvfrom.flags,
|
||||||
if (!(cmd->op_recvfrom.flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, n);
|
cmd->op_recvfrom.src,
|
||||||
if (cmd->op_recvfrom.src != NULL && cmd->op_recvfrom.addrlen != NULL) {
|
cmd->op_recvfrom.addrlen);
|
||||||
struct sockaddr_in *sa = (struct sockaddr_in *) cmd->op_recvfrom.src;
|
|
||||||
sa->sin_family = AF_INET;
|
|
||||||
sa->sin_port = c->rem.port;
|
|
||||||
sa->sin_addr.s_addr = c->rem.addr.ip4;
|
|
||||||
*cmd->op_recvfrom.addrlen = sizeof(*sa);
|
|
||||||
}
|
|
||||||
cmd->result = (int) n;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (s->err != 0) {
|
if (s->err != 0) {
|
||||||
|
|||||||
@@ -55,10 +55,37 @@ void mg_bsd_poll(struct mg_mgr *);
|
|||||||
#ifndef EPIPE
|
#ifndef EPIPE
|
||||||
#define EPIPE 32
|
#define EPIPE 32
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef EOPNOTSUPP
|
||||||
|
#define EOPNOTSUPP 95
|
||||||
|
#endif
|
||||||
|
#ifndef EISCONN
|
||||||
|
#define EISCONN 106
|
||||||
|
#endif
|
||||||
|
#ifndef EDESTADDRREQ
|
||||||
|
#define EDESTADDRREQ 121
|
||||||
|
#endif
|
||||||
|
#ifndef EMSGSIZE
|
||||||
|
#define EMSGSIZE 90
|
||||||
|
#endif
|
||||||
#ifndef EPROTONOSUPPORT
|
#ifndef EPROTONOSUPPORT
|
||||||
#define EPROTONOSUPPORT 93
|
#define EPROTONOSUPPORT 93
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__DCC__)
|
||||||
|
#pragma pack(1)
|
||||||
|
#else
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
#endif
|
||||||
|
struct mg_bsd_udp_frame {
|
||||||
|
uint32_t ip;
|
||||||
|
uint16_t port, len;
|
||||||
|
};
|
||||||
|
#if defined(__DCC__)
|
||||||
|
#pragma pack(0)
|
||||||
|
#else
|
||||||
|
#pragma pack(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
#if MG_ENABLE_BSD_PROTOTYPES
|
#if MG_ENABLE_BSD_PROTOTYPES
|
||||||
typedef unsigned int socklen_t;
|
typedef unsigned int socklen_t;
|
||||||
typedef unsigned long nfds_t;
|
typedef unsigned long nfds_t;
|
||||||
|
|||||||
+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
|
||||||
|
|||||||
+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;
|
||||||
}
|
}
|
||||||
|
|||||||
+119
-17
@@ -609,6 +609,36 @@ static void l2addr_resolved(struct mg_connection *c);
|
|||||||
static uint8_t *get_return_l2addr(struct mg_tcpip_if *ifp, struct mg_addr *rem,
|
static uint8_t *get_return_l2addr(struct mg_tcpip_if *ifp, struct mg_addr *rem,
|
||||||
bool is_udp, struct pkt *pkt);
|
bool is_udp, struct pkt *pkt);
|
||||||
|
|
||||||
|
#if MG_ENABLE_BSD_SOCKETS
|
||||||
|
static uint8_t *sendto_cache_find(struct mg_tcpip_if *ifp, uint32_t ip) {
|
||||||
|
struct mg_tcpip_sendto_entry *a;
|
||||||
|
for (a = ifp->sendto; a < ifp->sendto + MG_TCPIP_SENDTO_CACHE_SIZE; a++) {
|
||||||
|
if (a->ip == ip) return (uint8_t *) &a->addr;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sendto_cache_add(struct mg_tcpip_if *ifp, uint32_t ip,
|
||||||
|
const uint8_t *addr) {
|
||||||
|
struct mg_tcpip_sendto_entry *a;
|
||||||
|
for (a = ifp->sendto; a < ifp->sendto + MG_TCPIP_SENDTO_CACHE_SIZE; a++) {
|
||||||
|
if (a->ip == ip) return; // already cached, skip
|
||||||
|
if (a->ip == 0) break; // not present, first empty slot
|
||||||
|
}
|
||||||
|
if (a == ifp->sendto + MG_TCPIP_SENDTO_CACHE_SIZE) { // no room
|
||||||
|
memmove(ifp->sendto, ifp->sendto + 1,
|
||||||
|
sizeof(ifp->sendto[0]) * (MG_TCPIP_SENDTO_CACHE_SIZE - 1));
|
||||||
|
a--; // make room and point to last entry (just made)
|
||||||
|
}
|
||||||
|
a->ip = ip; // populate this entry
|
||||||
|
memcpy(&a->addr, addr, sizeof(a->addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sendto_cache_clear(struct mg_tcpip_if *ifp) {
|
||||||
|
memset(ifp->sendto, 0, sizeof(ifp->sendto));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// RFC826, ARP assumes Ethernet MAC addresses
|
// RFC826, ARP assumes Ethernet MAC addresses
|
||||||
static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||||
if (pkt->arp->op == mg_htons(1) && pkt->arp->tpa == ifp->ip) {
|
if (pkt->arp->op == mg_htons(1) && pkt->arp->tpa == ifp->ip) {
|
||||||
@@ -1214,30 +1244,57 @@ static void proto_error(struct mg_connection *c, bool r, const char *f, ...) {
|
|||||||
static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||||
struct mg_connection *c = getpeer(ifp->mgr, pkt, true);
|
struct mg_connection *c = getpeer(ifp->mgr, pkt, true);
|
||||||
struct connstate *s;
|
struct connstate *s;
|
||||||
|
struct mg_addr rem;
|
||||||
uint8_t *l2addr;
|
uint8_t *l2addr;
|
||||||
|
size_t len = pkt->pay.len;
|
||||||
if (c == NULL) return false; // No UDP listener on this port
|
if (c == NULL) return false; // No UDP listener on this port
|
||||||
s = (struct connstate *) (c + 1);
|
s = (struct connstate *) (c + 1);
|
||||||
c->rem.port = pkt->udp->sport;
|
|
||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
if (c->loc.is_ip6) { // matching of v4/v6 to dest is done bt getpeer()
|
if (c->loc.is_ip6) { // matching of v4/v6 to dest is done bt getpeer()
|
||||||
if (!udp6csum_ok(pkt->ip6, pkt->udp)) return false;
|
if (!udp6csum_ok(pkt->ip6, pkt->udp)) return false;
|
||||||
c->rem.addr.ip6[0] = pkt->ip6->src[0],
|
if (c->is_bsd && c->is_client &&
|
||||||
c->rem.addr.ip6[1] = pkt->ip6->src[1], c->rem.is_ip6 = true;
|
(c->rem.port != pkt->udp->sport ||
|
||||||
|
!MG_IP6MATCH(c->rem.addr.ip6, pkt->ip6->src)))
|
||||||
|
return true;
|
||||||
|
rem.addr.ip6[0] = pkt->ip6->src[0],
|
||||||
|
rem.addr.ip6[1] = pkt->ip6->src[1], rem.is_ip6 = true;
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
if (!udpcsum_ok(pkt->ip, pkt->udp)) return false;
|
if (!udpcsum_ok(pkt->ip, pkt->udp)) return false;
|
||||||
c->rem.addr.ip4 = pkt->ip->src;
|
if (c->is_bsd && c->is_client &&
|
||||||
|
(c->rem.port != pkt->udp->sport || c->rem.addr.ip4 != pkt->ip->src))
|
||||||
|
return true;
|
||||||
|
rem.addr.ip4 = pkt->ip->src, rem.is_ip6 = false;
|
||||||
}
|
}
|
||||||
if ((l2addr = get_return_l2addr(ifp, &c->rem, true, pkt)) == NULL)
|
rem.port = pkt->udp->sport;
|
||||||
|
if (!c->is_bsd || c->is_client) c->rem = rem;
|
||||||
|
if ((l2addr = get_return_l2addr(ifp, &rem, true, pkt)) == NULL)
|
||||||
return false; // safety net for lousy networks
|
return false; // safety net for lousy networks
|
||||||
memcpy(s->mac, l2addr, sizeof(s->mac));
|
if (!c->is_bsd || c->is_client) memcpy(s->mac, l2addr, sizeof(s->mac));
|
||||||
|
#if MG_ENABLE_BSD_SOCKETS
|
||||||
|
if (c->is_bsd && !rem.is_ip6 && MG_IPISLOCAL(ifp, rem.addr.ip4))
|
||||||
|
sendto_cache_add(ifp, rem.addr.ip4, l2addr);
|
||||||
|
#endif
|
||||||
|
#if MG_ENABLE_BSD_SOCKETS
|
||||||
|
if (c->is_bsd) len += sizeof(struct mg_bsd_udp_frame);
|
||||||
|
#endif
|
||||||
if (c->recv.len >= MG_MAX_RECV_SIZE) {
|
if (c->recv.len >= MG_MAX_RECV_SIZE) {
|
||||||
proto_error(c, false, "max_recv_buf_size reached");
|
proto_error(c, false, "max_recv_buf_size reached");
|
||||||
} else if (c->recv.size - c->recv.len < pkt->pay.len &&
|
} else if (c->recv.size - c->recv.len < len &&
|
||||||
!mg_iobuf_resize(&c->recv, c->recv.len + pkt->pay.len)) {
|
!mg_iobuf_resize(&c->recv, c->recv.len + len)) {
|
||||||
proto_error(c, false, "oom");
|
proto_error(c, false, "oom");
|
||||||
} else {
|
} else {
|
||||||
|
#if MG_ENABLE_BSD_SOCKETS
|
||||||
|
if (c->is_bsd) {
|
||||||
|
struct mg_bsd_udp_frame *h =
|
||||||
|
(struct mg_bsd_udp_frame *) &c->recv.buf[c->recv.len];
|
||||||
|
h->ip = rem.addr.ip4;
|
||||||
|
h->port = rem.port;
|
||||||
|
h->len = (uint16_t) pkt->pay.len;
|
||||||
|
c->recv.len += sizeof(*h);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len);
|
memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len);
|
||||||
c->recv.len += pkt->pay.len;
|
c->recv.len += pkt->pay.len;
|
||||||
mg_call(c, MG_EV_READ, &pkt->pay.len);
|
mg_call(c, MG_EV_READ, &pkt->pay.len);
|
||||||
@@ -2290,6 +2347,9 @@ void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) {
|
|||||||
|
|
||||||
static void close_conn(struct mg_connection *c) {
|
static void close_conn(struct mg_connection *c) {
|
||||||
struct connstate *s = (struct connstate *) (c + 1);
|
struct connstate *s = (struct connstate *) (c + 1);
|
||||||
|
#if MG_ENABLE_BSD_SOCKETS
|
||||||
|
if (c->is_bsd && c->is_udp) sendto_cache_clear(c->mgr->ifp);
|
||||||
|
#endif
|
||||||
mg_iobuf_free(&s->txq);
|
mg_iobuf_free(&s->txq);
|
||||||
mg_close_conn(c);
|
mg_close_conn(c);
|
||||||
}
|
}
|
||||||
@@ -2311,6 +2371,11 @@ static void send_syn(struct mg_connection *c) {
|
|||||||
static void l2addr_resolved(struct mg_connection *c) {
|
static void l2addr_resolved(struct mg_connection *c) {
|
||||||
if (c->is_udp) {
|
if (c->is_udp) {
|
||||||
c->is_connecting = 0;
|
c->is_connecting = 0;
|
||||||
|
#if MG_ENABLE_BSD_SOCKETS
|
||||||
|
if (c->is_bsd)
|
||||||
|
sendto_cache_add(c->mgr->ifp, c->rem.addr.ip4,
|
||||||
|
((struct connstate *) (c + 1))->mac);
|
||||||
|
#endif
|
||||||
mg_call(c, MG_EV_CONNECT, NULL);
|
mg_call(c, MG_EV_CONNECT, NULL);
|
||||||
} else {
|
} else {
|
||||||
send_syn(c);
|
send_syn(c);
|
||||||
@@ -2325,20 +2390,25 @@ void mg_connect_resolved(struct mg_connection *c) {
|
|||||||
s->retransmit = ifp->enable_tcp_retransmit;
|
s->retransmit = ifp->enable_tcp_retransmit;
|
||||||
s->txq.align = MG_TCPIP_TXQ_ALIGN;
|
s->txq.align = MG_TCPIP_TXQ_ALIGN;
|
||||||
c->is_resolving = 0;
|
c->is_resolving = 0;
|
||||||
if (ifp->eport < MG_EPHEMERAL_PORT_BASE) ifp->eport = MG_EPHEMERAL_PORT_BASE;
|
if (c->loc.port == 0) {
|
||||||
|
if (ifp->eport < MG_EPHEMERAL_PORT_BASE)
|
||||||
|
ifp->eport = MG_EPHEMERAL_PORT_BASE;
|
||||||
c->loc.port = mg_htons(ifp->eport++);
|
c->loc.port = mg_htons(ifp->eport++);
|
||||||
|
} // otherwise retain an existing bound port
|
||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
if (c->rem.is_ip6) {
|
if (c->rem.is_ip6) {
|
||||||
if (c->rem.addr.ip6[0] == ifp->ip6ll[0]) { // same local LAN, use ll
|
if (c->loc.addr.ip6[0] == 0 && c->loc.addr.ip6[1] == 0 &&
|
||||||
|
c->rem.addr.ip6[0] == ifp->ip6ll[0]) { // same local LAN, use ll
|
||||||
c->loc.addr.ip6[0] = ifp->ip6ll[0], c->loc.addr.ip6[1] = ifp->ip6ll[1];
|
c->loc.addr.ip6[0] = ifp->ip6ll[0], c->loc.addr.ip6[1] = ifp->ip6ll[1];
|
||||||
} else { // use global address
|
} else if (c->loc.addr.ip6[0] == 0 && c->loc.addr.ip6[1] == 0) {
|
||||||
|
// Use global address
|
||||||
c->loc.addr.ip6[0] = ifp->ip6[0], c->loc.addr.ip6[1] = ifp->ip6[1];
|
c->loc.addr.ip6[0] = ifp->ip6[0], c->loc.addr.ip6[1] = ifp->ip6[1];
|
||||||
}
|
} // otherwise retain an existing bound address
|
||||||
c->loc.is_ip6 = true;
|
c->loc.is_ip6 = true;
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
c->loc.addr.ip4 = ifp->ip;
|
if (c->loc.addr.ip4 == 0) c->loc.addr.ip4 = ifp->ip;
|
||||||
}
|
}
|
||||||
MG_DEBUG(("%lu %M -> %M", c->id, mg_print_ip_port, &c->loc, mg_print_ip_port,
|
MG_DEBUG(("%lu %M -> %M", c->id, mg_print_ip_port, &c->loc, mg_print_ip_port,
|
||||||
&c->rem));
|
&c->rem));
|
||||||
@@ -2369,11 +2439,20 @@ void mg_connect_resolved(struct mg_connection *c) {
|
|||||||
uint32_t rem_ip = c->rem.addr.ip4;
|
uint32_t rem_ip = c->rem.addr.ip4;
|
||||||
if (ifp->ip && MG_IPISLOCAL(ifp, rem_ip) &&
|
if (ifp->ip && MG_IPISLOCAL(ifp, rem_ip) &&
|
||||||
rem_ip != ifp->gw) { // skip if gw (onstatechange -> ARP)
|
rem_ip != ifp->gw) { // skip if gw (onstatechange -> ARP)
|
||||||
// If we're in the same LAN, fire an ARP lookup.
|
// We're in the same LAN
|
||||||
|
#if MG_ENABLE_BSD_SOCKETS
|
||||||
|
if (c->is_bsd && c->is_udp && // see if we already have L2 data
|
||||||
|
(l2addr = sendto_cache_find(ifp, rem_ip)) != NULL) {
|
||||||
|
memcpy(s->mac, l2addr, sizeof(s->mac));
|
||||||
|
l2addr_resolved(c);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{ // fire an ARP lookup
|
||||||
MG_DEBUG(("%lu ARP lookup...", c->id));
|
MG_DEBUG(("%lu ARP lookup...", c->id));
|
||||||
mg_tcpip_arp_request(ifp, rem_ip, NULL);
|
mg_tcpip_arp_request(ifp, rem_ip, NULL);
|
||||||
settmout(c, MIP_TTYPE_ARP);
|
settmout(c, MIP_TTYPE_ARP);
|
||||||
c->is_arplooking = 1;
|
c->is_arplooking = 1;
|
||||||
|
}
|
||||||
} else if (ifp->gw_ready) {
|
} else if (ifp->gw_ready) {
|
||||||
memcpy(s->mac, ifp->gwmac, sizeof(s->mac));
|
memcpy(s->mac, ifp->gwmac, sizeof(s->mac));
|
||||||
l2addr_resolved(c);
|
l2addr_resolved(c);
|
||||||
@@ -2393,13 +2472,36 @@ bool mg_open_listener(struct mg_connection *c, const char *url) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void write_conn(struct mg_connection *c) {
|
static void write_conn(struct mg_connection *c) {
|
||||||
long len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len)
|
long len;
|
||||||
|
size_t off = 0;
|
||||||
|
bool empty = false;
|
||||||
|
#if MG_ENABLE_BSD_SOCKETS
|
||||||
|
struct mg_bsd_udp_frame *h;
|
||||||
|
if (c->is_bsd && c->is_udp) {
|
||||||
|
if (c->is_connecting) return;
|
||||||
|
h = (struct mg_bsd_udp_frame *) c->send.buf;
|
||||||
|
assert(c->send.len >= sizeof(*h));
|
||||||
|
assert(h->len <= c->send.len - sizeof(*h));
|
||||||
|
c->rem.port = h->port;
|
||||||
|
c->rem.is_ip6 = false;
|
||||||
|
if (!c->is_client && c->rem.addr.ip4 != h->ip) {
|
||||||
|
c->rem.addr.ip4 = h->ip;
|
||||||
|
mg_connect_resolved(c);
|
||||||
|
if (c->is_connecting) return;
|
||||||
|
}
|
||||||
|
len = mg_io_send(c, h + 1, h->len);
|
||||||
|
off = sizeof(*h), empty = h->len == 0;
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len)
|
||||||
: mg_io_send(c, c->send.buf, c->send.len);
|
: mg_io_send(c, c->send.buf, c->send.len);
|
||||||
|
}
|
||||||
// TODO(): mg_tls_send() may return 0 forever on steady OOM
|
// TODO(): mg_tls_send() may return 0 forever on steady OOM
|
||||||
if (len == MG_IO_ERR) {
|
if (len == MG_IO_ERR) {
|
||||||
proto_error(c, false, "tx err");
|
proto_error(c, false, "tx err");
|
||||||
} else if (len > 0) {
|
} else if (len > 0 || empty) {
|
||||||
mg_iobuf_del(&c->send, 0, (size_t) len);
|
mg_iobuf_del(&c->send, 0, off + (size_t) len);
|
||||||
mg_call(c, MG_EV_WRITE, &len);
|
mg_call(c, MG_EV_WRITE, &len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,17 @@ struct mg_tcpip_driver {
|
|||||||
typedef void (*mg_tcpip_event_handler_t)(struct mg_tcpip_if *ifp, int ev,
|
typedef void (*mg_tcpip_event_handler_t)(struct mg_tcpip_if *ifp, int ev,
|
||||||
void *ev_data);
|
void *ev_data);
|
||||||
|
|
||||||
|
#if MG_ENABLE_BSD_SOCKETS
|
||||||
|
#ifndef MG_TCPIP_SENDTO_CACHE_SIZE
|
||||||
|
#define MG_TCPIP_SENDTO_CACHE_SIZE 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct mg_tcpip_sendto_entry {
|
||||||
|
uint32_t ip;
|
||||||
|
struct mg_l2addr addr;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
// Events delivered to mg_tcpip_if.fn (user) and mg_tcpip_if.pfn (driver).
|
// Events delivered to mg_tcpip_if.fn (user) and mg_tcpip_if.pfn (driver).
|
||||||
// Each entry shows the ev_data type to cast inside the handler.
|
// Each entry shows the ev_data type to cast inside the handler.
|
||||||
@@ -88,6 +99,9 @@ struct mg_tcpip_if {
|
|||||||
enum mg_l2type l2type; // Layer-2 type: Ethernet, PPP, etc. (see l2.h)
|
enum mg_l2type l2type; // Layer-2 type: Ethernet, PPP, etc. (see l2.h)
|
||||||
union mg_l2data l2data; // Layer-2 config and state (see l2.h)
|
union mg_l2data l2data; // Layer-2 config and state (see l2.h)
|
||||||
char *dns4_url; // Active DNS server URL (set from DHCP or config)
|
char *dns4_url; // Active DNS server URL (set from DHCP or config)
|
||||||
|
#if MG_ENABLE_BSD_SOCKETS
|
||||||
|
struct mg_tcpip_sendto_entry sendto[MG_TCPIP_SENDTO_CACHE_SIZE]; // Local IPv4 neighbors
|
||||||
|
#endif
|
||||||
uint64_t now; // Current time in ms (updated each mg_mgr_poll)
|
uint64_t now; // Current time in ms (updated each mg_mgr_poll)
|
||||||
uint64_t timer_1000ms; // Timestamp of last 1-second tick
|
uint64_t timer_1000ms; // Timestamp of last 1-second tick
|
||||||
uint64_t lease_expire; // DHCP lease expiry time in ms
|
uint64_t lease_expire; // DHCP lease expiry time in ms
|
||||||
|
|||||||
@@ -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");
|
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
|
|||||||
+5
-1
@@ -299,7 +299,11 @@ static long recv_raw(struct mg_connection *c, void *buf, size_t len) {
|
|||||||
union usa usa;
|
union usa usa;
|
||||||
socklen_t slen = tousa(&c->rem, &usa);
|
socklen_t slen = tousa(&c->rem, &usa);
|
||||||
n = recvfrom(FD(c), (char *) buf, len, 0, &usa.sa, &slen);
|
n = recvfrom(FD(c), (char *) buf, len, 0, &usa.sa, &slen);
|
||||||
if (n > 0) tomgaddr(&usa, &c->rem, slen != sizeof(usa.sin));
|
if (n > 0) {
|
||||||
|
struct mg_addr rem;
|
||||||
|
tomgaddr(&usa, &rem, slen != sizeof(usa.sin));
|
||||||
|
c->rem = rem;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
n = recv(FD(c), (char *) buf, len, MSG_NONBLOCKING);
|
n = recv(FD(c), (char *) buf, len, MSG_NONBLOCKING);
|
||||||
}
|
}
|
||||||
|
|||||||
+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
|
||||||
|
|||||||
+9
-6
@@ -13,7 +13,7 @@ else
|
|||||||
C_WARN ?= -Wno-deprecated
|
C_WARN ?= -Wno-deprecated
|
||||||
endif
|
endif
|
||||||
|
|
||||||
.PHONY: all bsd bsd_test bsd_client bsd_nortos bsd_nortos_test shm_tap_bridge \
|
.PHONY: all bsd bsd_test bsd_client bsd_udp bsd_nortos bsd_nortos_test shm_tap_bridge \
|
||||||
shm_port_bridge bsd_s390 bsd_nortos_s390 clean
|
shm_port_bridge bsd_s390 bsd_nortos_s390 clean
|
||||||
|
|
||||||
TEST_SOURCES = bsd_shm_test.c
|
TEST_SOURCES = bsd_shm_test.c
|
||||||
@@ -47,11 +47,11 @@ DOCKER ?= $(DOCKER_BIN) run --platform linux/amd64 --net=host --rm $(ENV) -v $(R
|
|||||||
CFLAGS_MONGOOSE += -DMG_ARCH=MG_ARCH_CUSTOM
|
CFLAGS_MONGOOSE += -DMG_ARCH=MG_ARCH_CUSTOM
|
||||||
|
|
||||||
|
|
||||||
all bsd: shm_tap_bridge bsd_client FreeRTOS-Kernel bsd_test
|
all bsd: shm_tap_bridge bsd_client bsd_udp FreeRTOS-Kernel bsd_test
|
||||||
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./bsd_test $(ARGS)
|
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./bsd_test $(ARGS)
|
||||||
|
|
||||||
# also
|
# also
|
||||||
bsd_nortos: shm_tap_bridge bsd_client bsd_nortos_test
|
bsd_nortos: shm_tap_bridge bsd_client bsd_udp bsd_nortos_test
|
||||||
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./bsd_nortos_test $(ARGS)
|
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./bsd_nortos_test $(ARGS)
|
||||||
|
|
||||||
# requires shm_tap_bridge to be running
|
# requires shm_tap_bridge to be running
|
||||||
@@ -70,6 +70,9 @@ bsd_nortos_test:
|
|||||||
bsd_client: bsd_client.c
|
bsd_client: bsd_client.c
|
||||||
$(CC) bsd_client.c -pthread -o $@
|
$(CC) bsd_client.c -pthread -o $@
|
||||||
|
|
||||||
|
bsd_udp: bsd_udp.c
|
||||||
|
$(CC) bsd_udp.c -o $@
|
||||||
|
|
||||||
# bridge shm queue <--> TAP interface
|
# bridge shm queue <--> TAP interface
|
||||||
shm_tap_bridge: shm_tap_bridge.c shm_queue.h
|
shm_tap_bridge: shm_tap_bridge.c shm_queue.h
|
||||||
$(CC) shm_tap_bridge.c -pthread -o $@
|
$(CC) shm_tap_bridge.c -pthread -o $@
|
||||||
@@ -79,7 +82,7 @@ bsd_s390: CFLAGS_MONGOOSE += -DMG_ENABLE_BSD_PROTOTYPES=0
|
|||||||
bsd_s390: ASAN=
|
bsd_s390: ASAN=
|
||||||
bsd_s390: CC = $(DOCKER) mdashnet/s390 cc
|
bsd_s390: CC = $(DOCKER) mdashnet/s390 cc
|
||||||
bsd_s390: RUN = $(DOCKER) --init mdashnet/s390
|
bsd_s390: RUN = $(DOCKER) --init mdashnet/s390
|
||||||
bsd_s390: shm_port_bridge bsd_client FreeRTOS-Kernel bsd_test
|
bsd_s390: shm_port_bridge bsd_client bsd_udp FreeRTOS-Kernel bsd_test
|
||||||
$(RUN) sh -c './shm_port_bridge & pid=$$!; sleep 1; ./bsd_test; rc=$$?; kill $$pid >/dev/null 2>&1; wait $$pid >/dev/null 2>&1; exit $$rc'
|
$(RUN) sh -c './shm_port_bridge & pid=$$!; sleep 1; ./bsd_test; rc=$$?; kill $$pid >/dev/null 2>&1; wait $$pid >/dev/null 2>&1; exit $$rc'
|
||||||
|
|
||||||
#also
|
#also
|
||||||
@@ -87,14 +90,14 @@ bsd_nortos_s390: CFLAGS_MONGOOSE += -DMG_ENABLE_BSD_PROTOTYPES=0
|
|||||||
bsd_nortos_s390: ASAN=
|
bsd_nortos_s390: ASAN=
|
||||||
bsd_nortos_s390: CC = $(DOCKER) mdashnet/s390 cc
|
bsd_nortos_s390: CC = $(DOCKER) mdashnet/s390 cc
|
||||||
bsd_nortos_s390: RUN = $(DOCKER) --init mdashnet/s390
|
bsd_nortos_s390: RUN = $(DOCKER) --init mdashnet/s390
|
||||||
bsd_nortos_s390: shm_port_bridge bsd_client bsd_nortos_test
|
bsd_nortos_s390: shm_port_bridge bsd_client bsd_udp bsd_nortos_test
|
||||||
$(RUN) sh -c './shm_port_bridge & pid=$$!; sleep 1; ./bsd_nortos_test; rc=$$?; kill $$pid >/dev/null 2>&1; wait $$pid >/dev/null 2>&1; exit $$rc'
|
$(RUN) sh -c './shm_port_bridge & pid=$$!; sleep 1; ./bsd_nortos_test; rc=$$?; kill $$pid >/dev/null 2>&1; wait $$pid >/dev/null 2>&1; exit $$rc'
|
||||||
|
|
||||||
shm_port_bridge: shm_port_bridge.c shm_queue.h
|
shm_port_bridge: shm_port_bridge.c shm_queue.h
|
||||||
$(CC) shm_port_bridge.c $(CFLAGS) -pthread -o $@
|
$(CC) shm_port_bridge.c $(CFLAGS) -pthread -o $@
|
||||||
|
|
||||||
clean: # Cleanup. Delete built program and all build artifacts
|
clean: # Cleanup. Delete built program and all build artifacts
|
||||||
rm -rf bsd_test bsd_client shm_port_bridge *.o *.obj FreeRTOS-Kernel
|
rm -rf bsd_test bsd_client bsd_udp shm_port_bridge *.o *.obj FreeRTOS-Kernel
|
||||||
|
|
||||||
FreeRTOS-Kernel: # FreeRTOS sources
|
FreeRTOS-Kernel: # FreeRTOS sources
|
||||||
git clone -q -c advice.detachedHead=false --depth 1 -b V11.2.0 https://github.com/FreeRTOS/FreeRTOS-Kernel $@
|
git clone -q -c advice.detachedHead=false --depth 1 -b V11.2.0 https://github.com/FreeRTOS/FreeRTOS-Kernel $@
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define PORT 1235
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
struct sockaddr_in sa;
|
||||||
|
struct timeval timeout;
|
||||||
|
char buf[8];
|
||||||
|
ssize_t n;
|
||||||
|
int fd, rogue;
|
||||||
|
|
||||||
|
if (argc != 2) return 1;
|
||||||
|
memset(&sa, 0, sizeof(sa));
|
||||||
|
sa.sin_family = AF_INET;
|
||||||
|
sa.sin_port = htons(PORT);
|
||||||
|
if (inet_pton(AF_INET, argv[1], &sa.sin_addr) != 1) return 1;
|
||||||
|
timeout.tv_sec = 5, timeout.tv_usec = 0;
|
||||||
|
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ||
|
||||||
|
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != 0 ||
|
||||||
|
connect(fd, (struct sockaddr *) &sa, sizeof(sa)) != 0 ||
|
||||||
|
send(fd, "hello", 5, 0) != 5 || recv(fd, buf, sizeof(buf), 0) != 5 ||
|
||||||
|
memcmp(buf, "ready", 5) != 0)
|
||||||
|
goto fail;
|
||||||
|
if ((rogue = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ||
|
||||||
|
setsockopt(rogue, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) !=
|
||||||
|
0 ||
|
||||||
|
send(fd, "one", 3, 0) != 3 ||
|
||||||
|
sendto(rogue, "two2", 4, 0, (struct sockaddr *) &sa, sizeof(sa)) != 4 ||
|
||||||
|
recv(fd, buf, sizeof(buf), 0) != 4 || memcmp(buf, "peer", 4) != 0 ||
|
||||||
|
recv(rogue, buf, sizeof(buf), 0) != 5 || memcmp(buf, "rogue", 5) != 0 ||
|
||||||
|
send(fd, "truncate", 8, 0) != 8 || send(fd, "peek", 4, 0) != 4 ||
|
||||||
|
recv(fd, buf, sizeof(buf), 0) != 4 || memcmp(buf, "more", 4) != 0 ||
|
||||||
|
recv(fd, buf, sizeof(buf), 0) != 3 || memcmp(buf, "one", 3) != 0 ||
|
||||||
|
recv(fd, buf, sizeof(buf), 0) != 4 || memcmp(buf, "two2", 4) != 0 ||
|
||||||
|
recv(fd, buf, sizeof(buf), 0) != 0 ||
|
||||||
|
sendto(rogue, "rogue", 5, 0, (struct sockaddr *) &sa, sizeof(sa)) != 5 ||
|
||||||
|
recv(fd, buf, sizeof(buf), 0) != 7 || memcmp(buf, "checked", 7) != 0 ||
|
||||||
|
send(fd, "echo", 4, 0) != 4 || recv(fd, buf, sizeof(buf), 0) != 4 ||
|
||||||
|
memcmp(buf, "echo", 4) != 0)
|
||||||
|
goto fail_close_rogue;
|
||||||
|
close(rogue);
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail_close_rogue:
|
||||||
|
close(rogue);
|
||||||
|
fail:
|
||||||
|
close(fd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
+230
-9
@@ -33,6 +33,175 @@ static int s_abort = 0;
|
|||||||
#define DASHBOARD(x) \
|
#define DASHBOARD(x) \
|
||||||
printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false" : "true")
|
printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false" : "true")
|
||||||
|
|
||||||
|
#define UDP_PORT 1235
|
||||||
|
|
||||||
|
#if MG_ENABLE_FREERTOS
|
||||||
|
static volatile bool s_tx_held, s_tx_waits;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int open_udp(void) {
|
||||||
|
struct sockaddr_in sa;
|
||||||
|
char buf;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
memset(&sa, 0, sizeof(sa));
|
||||||
|
sa.sin_family = AF_INET;
|
||||||
|
sa.sin_port = htons(UDP_PORT);
|
||||||
|
sa.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
ASSERT((fd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0);
|
||||||
|
ASSERT(bind(fd, (struct sockaddr *) &sa, sizeof(sa)) == 0);
|
||||||
|
ASSERT(send(fd, &buf, sizeof(buf), 0) < 0 && errno == EDESTADDRREQ);
|
||||||
|
ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void check_udp_oversize(struct mg_mgr *mgr, int fd,
|
||||||
|
const struct sockaddr *dst, socklen_t dstlen) {
|
||||||
|
static char buf[1600];
|
||||||
|
size_t len = mgr->ifp->mtu + 1;
|
||||||
|
|
||||||
|
ASSERT(len <= sizeof(buf));
|
||||||
|
if (dst == NULL) {
|
||||||
|
ASSERT(send(fd, buf, len, 0) < 0 && errno == EMSGSIZE);
|
||||||
|
} else {
|
||||||
|
ASSERT(sendto(fd, buf, len, 0, dst, dstlen) < 0 && errno == EMSGSIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void udp_exchange(struct mg_mgr *mgr, int fd,
|
||||||
|
void (*progress)(struct mg_mgr *)) {
|
||||||
|
struct sockaddr_in peer, other, rogue = {0};
|
||||||
|
socklen_t peerlen, otherlen;
|
||||||
|
uint64_t deadline;
|
||||||
|
struct timeval zero = {0, 0};
|
||||||
|
struct pollfd p;
|
||||||
|
fd_set r, w, e;
|
||||||
|
bool one;
|
||||||
|
char buf[8];
|
||||||
|
ssize_t n;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
peerlen = sizeof(peer);
|
||||||
|
n = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) &peer, &peerlen);
|
||||||
|
if (n >= 0) break;
|
||||||
|
ASSERT(errno == EAGAIN);
|
||||||
|
progress(mgr);
|
||||||
|
}
|
||||||
|
ASSERT(n == 5 && memcmp(buf, "hello", 5) == 0);
|
||||||
|
ASSERT(sendto(fd, "ready", 5, 0, (struct sockaddr *) &peer, peerlen) ==
|
||||||
|
5);
|
||||||
|
deadline = mg_millis() + 100;
|
||||||
|
while (mg_millis() < deadline) progress(mgr); // Queue both UDP datagrams
|
||||||
|
do {
|
||||||
|
otherlen = sizeof(other);
|
||||||
|
n = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) &other,
|
||||||
|
&otherlen);
|
||||||
|
if (n < 0) {
|
||||||
|
ASSERT(errno == EAGAIN);
|
||||||
|
progress(mgr);
|
||||||
|
}
|
||||||
|
} while (n < 0);
|
||||||
|
one = n == 3;
|
||||||
|
if (one) {
|
||||||
|
ASSERT(memcmp(buf, "one", 3) == 0 && other.sin_port == peer.sin_port);
|
||||||
|
} else {
|
||||||
|
ASSERT(n == 4 && memcmp(buf, "two2", 4) == 0 &&
|
||||||
|
other.sin_port != peer.sin_port);
|
||||||
|
}
|
||||||
|
if (other.sin_port != peer.sin_port) rogue = other;
|
||||||
|
do {
|
||||||
|
otherlen = sizeof(other);
|
||||||
|
n = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) &other,
|
||||||
|
&otherlen);
|
||||||
|
if (n < 0) {
|
||||||
|
ASSERT(errno == EAGAIN);
|
||||||
|
progress(mgr);
|
||||||
|
}
|
||||||
|
} while (n < 0);
|
||||||
|
if (one) {
|
||||||
|
ASSERT(n == 4 && memcmp(buf, "two2", 4) == 0 &&
|
||||||
|
other.sin_port != peer.sin_port);
|
||||||
|
} else {
|
||||||
|
ASSERT(n == 3 && memcmp(buf, "one", 3) == 0 &&
|
||||||
|
other.sin_port == peer.sin_port);
|
||||||
|
}
|
||||||
|
if (other.sin_port != peer.sin_port) rogue = other;
|
||||||
|
check_udp_oversize(mgr, fd, (struct sockaddr *) &peer, peerlen);
|
||||||
|
ASSERT(sendto(fd, "peer", 4, 0, (struct sockaddr *) &peer, peerlen) == 4);
|
||||||
|
ASSERT(sendto(fd, "rogue", 5, 0, (struct sockaddr *) &rogue,
|
||||||
|
sizeof(rogue)) == 5);
|
||||||
|
deadline = mg_millis() + 100;
|
||||||
|
while (mg_millis() < deadline) progress(mgr);
|
||||||
|
ASSERT(connect(fd, (struct sockaddr *) &peer, peerlen) == 0);
|
||||||
|
ASSERT(sendto(fd, "no", 2, 0, (struct sockaddr *) &peer, peerlen) < 0 &&
|
||||||
|
errno == EISCONN);
|
||||||
|
check_udp_oversize(mgr, fd, NULL, 0);
|
||||||
|
ASSERT(send(fd, "more", 4, 0) == 4);
|
||||||
|
deadline = mg_millis() + 100;
|
||||||
|
while (mg_millis() < deadline) progress(mgr); // Queue both UDP datagrams
|
||||||
|
do {
|
||||||
|
p.fd = fd, p.events = POLLIN | POLLOUT, p.revents = 0;
|
||||||
|
ASSERT(poll(&p, 1, 0) == 1);
|
||||||
|
if (!(p.revents & POLLIN)) progress(mgr);
|
||||||
|
} while (!(p.revents & POLLIN));
|
||||||
|
ASSERT(p.revents == (POLLIN | POLLOUT));
|
||||||
|
FD_ZERO(&r);
|
||||||
|
FD_ZERO(&w);
|
||||||
|
FD_ZERO(&e);
|
||||||
|
FD_SET(fd, &r);
|
||||||
|
FD_SET(fd, &w);
|
||||||
|
FD_SET(fd, &e);
|
||||||
|
ASSERT(select(fd + 1, &r, &w, &e, &zero) == 1);
|
||||||
|
ASSERT(FD_ISSET(fd, &r) && FD_ISSET(fd, &w) && !FD_ISSET(fd, &e));
|
||||||
|
do {
|
||||||
|
n = recv(fd, buf, 3, 0);
|
||||||
|
if (n < 0) {
|
||||||
|
ASSERT(errno == EAGAIN);
|
||||||
|
progress(mgr);
|
||||||
|
}
|
||||||
|
} while (n < 0);
|
||||||
|
ASSERT(n == 3 && memcmp(buf, "tru", 3) == 0);
|
||||||
|
do {
|
||||||
|
n = recv(fd, buf, sizeof(buf), MSG_PEEK);
|
||||||
|
if (n < 0) {
|
||||||
|
ASSERT(errno == EAGAIN);
|
||||||
|
progress(mgr);
|
||||||
|
}
|
||||||
|
} while (n < 0);
|
||||||
|
ASSERT(n == 4 && memcmp(buf, "peek", 4) == 0);
|
||||||
|
ASSERT(recv(fd, buf, sizeof(buf), 0) == 4 && memcmp(buf, "peek", 4) == 0);
|
||||||
|
p.events = POLLIN | POLLOUT, p.revents = 0;
|
||||||
|
ASSERT(poll(&p, 1, 0) == 1 && p.revents == POLLOUT);
|
||||||
|
#if MG_ENABLE_FREERTOS
|
||||||
|
s_tx_waits = false, s_tx_held = true;
|
||||||
|
#endif
|
||||||
|
ASSERT(send(fd, "one", 3, 0) == 3);
|
||||||
|
ASSERT(send(fd, "two2", 4, 0) == 4);
|
||||||
|
ASSERT(send(fd, "", 0, 0) == 0);
|
||||||
|
#if MG_ENABLE_FREERTOS
|
||||||
|
while (!s_tx_waits) progress(mgr); // MG_IO_WAIT keeps all records queued
|
||||||
|
s_tx_held = false;
|
||||||
|
#endif
|
||||||
|
deadline = mg_millis() + 100;
|
||||||
|
while (mg_millis() < deadline) progress(mgr); // Let rogue arrive
|
||||||
|
ASSERT(recv(fd, buf, sizeof(buf), MSG_DONTWAIT) < 0 && errno == EAGAIN);
|
||||||
|
while ((n = send(fd, "checked", 7, 0)) < 0) {
|
||||||
|
ASSERT(errno == EAGAIN);
|
||||||
|
progress(mgr);
|
||||||
|
}
|
||||||
|
ASSERT(n == 7);
|
||||||
|
for (;;) {
|
||||||
|
n = recv(fd, buf, sizeof(buf), 0);
|
||||||
|
if (n >= 0) break;
|
||||||
|
ASSERT(errno == EAGAIN);
|
||||||
|
progress(mgr);
|
||||||
|
}
|
||||||
|
ASSERT(n == 4 && memcmp(buf, "echo", 4) == 0);
|
||||||
|
ASSERT(send(fd, "echo", 4, 0) == 4);
|
||||||
|
progress(mgr); // Drain the last queued datagram before close
|
||||||
|
ASSERT(close(fd) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
static pid_t start_client(const char *ip, const char *connections) {
|
static pid_t start_client(const char *ip, const char *connections) {
|
||||||
char *cmd[4];
|
char *cmd[4];
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
@@ -46,6 +215,18 @@ static pid_t start_client(const char *ip, const char *connections) {
|
|||||||
return pid;
|
return pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static pid_t start_udp(const char *ip) {
|
||||||
|
char *cmd[3];
|
||||||
|
pid_t pid;
|
||||||
|
extern char **environ;
|
||||||
|
|
||||||
|
cmd[0] = (char *) "./bsd_udp";
|
||||||
|
cmd[1] = (char *) ip;
|
||||||
|
cmd[2] = NULL;
|
||||||
|
ASSERT(posix_spawn(&pid, cmd[0], NULL, NULL, cmd, environ) == 0);
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
static bool client_done(pid_t pid, int *status) {
|
static bool client_done(pid_t pid, int *status) {
|
||||||
pid_t r = waitpid(pid, status, WNOHANG);
|
pid_t r = waitpid(pid, status, WNOHANG);
|
||||||
ASSERT(r >= 0);
|
ASSERT(r >= 0);
|
||||||
@@ -60,12 +241,17 @@ static void check_client(int status) {
|
|||||||
static int open_listener(int backlog, bool nonblock) {
|
static int open_listener(int backlog, bool nonblock) {
|
||||||
int lfd = socket(AF_INET, SOCK_STREAM, 0);
|
int lfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
struct sockaddr_in sa;
|
struct sockaddr_in sa;
|
||||||
|
char buf;
|
||||||
|
|
||||||
memset(&sa, 0, sizeof(sa));
|
memset(&sa, 0, sizeof(sa));
|
||||||
sa.sin_family = AF_INET;
|
sa.sin_family = AF_INET;
|
||||||
sa.sin_port = htons(1234);
|
sa.sin_port = htons(1234);
|
||||||
sa.sin_addr.s_addr = INADDR_ANY;
|
sa.sin_addr.s_addr = INADDR_ANY;
|
||||||
ASSERT(lfd >= 0);
|
ASSERT(lfd >= 0);
|
||||||
|
ASSERT(sendto(lfd, &buf, sizeof(buf), 0, (struct sockaddr *) &sa,
|
||||||
|
sizeof(sa)) < 0 && errno == EOPNOTSUPP);
|
||||||
|
ASSERT(recvfrom(lfd, &buf, sizeof(buf), 0, NULL, NULL) < 0 &&
|
||||||
|
errno == EOPNOTSUPP);
|
||||||
ASSERT(bind(lfd, (struct sockaddr *) &sa, sizeof(sa)) == 0);
|
ASSERT(bind(lfd, (struct sockaddr *) &sa, sizeof(sa)) == 0);
|
||||||
ASSERT(listen(lfd, backlog) == 0);
|
ASSERT(listen(lfd, backlog) == 0);
|
||||||
if (nonblock) ASSERT(fcntl(lfd, F_SETFL, O_NONBLOCK) == 0);
|
if (nonblock) ASSERT(fcntl(lfd, F_SETFL, O_NONBLOCK) == 0);
|
||||||
@@ -130,11 +316,12 @@ static volatile bool s_select_waiting, s_select_done;
|
|||||||
static volatile bool s_poll_waiting, s_poll_done;
|
static volatile bool s_poll_waiting, s_poll_done;
|
||||||
static volatile bool s_ready_waiting, s_ready_done;
|
static volatile bool s_ready_waiting, s_ready_done;
|
||||||
static volatile bool s_backlog_ready, s_backlog_full, s_backlog_accept;
|
static volatile bool s_backlog_ready, s_backlog_full, s_backlog_accept;
|
||||||
static volatile bool s_tx_held, s_tx_waits, s_tx_fail, s_tx_error;
|
static volatile bool s_tx_fail, s_tx_error;
|
||||||
static volatile bool s_send_waiting;
|
static volatile bool s_send_waiting;
|
||||||
static volatile bool s_send_done, s_send_expect_error;
|
static volatile bool s_send_done, s_send_expect_error;
|
||||||
static volatile bool s_recv_start, s_recv_waiting, s_recv_done;
|
static volatile bool s_recv_start, s_recv_waiting, s_recv_done;
|
||||||
static volatile bool s_recv_expect_error;
|
static volatile bool s_recv_expect_error;
|
||||||
|
static volatile bool s_udp_ready, s_udp_done;
|
||||||
static size_t (*s_tx)(const void *, size_t, struct mg_tcpip_if *);
|
static size_t (*s_tx)(const void *, size_t, struct mg_tcpip_if *);
|
||||||
|
|
||||||
struct client_data {
|
struct client_data {
|
||||||
@@ -152,6 +339,7 @@ static void select_task(void *args);
|
|||||||
static void readiness_task(void *args);
|
static void readiness_task(void *args);
|
||||||
static void backlog_task(void *args);
|
static void backlog_task(void *args);
|
||||||
static void tx_gate_task(void *args);
|
static void tx_gate_task(void *args);
|
||||||
|
static void udp_task(void *args);
|
||||||
|
|
||||||
struct atask_args {
|
struct atask_args {
|
||||||
bool listener_nonblock;
|
bool listener_nonblock;
|
||||||
@@ -306,6 +494,21 @@ static void tx_gate_task(void *args) {
|
|||||||
(void) args;
|
(void) args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void udp_delay(struct mg_mgr *mgr) {
|
||||||
|
(void) mgr;
|
||||||
|
vTaskDelay(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void udp_task(void *args) {
|
||||||
|
struct mg_mgr *mgr = (struct mg_mgr *) args;
|
||||||
|
int fd = open_udp();
|
||||||
|
|
||||||
|
s_udp_ready = true;
|
||||||
|
udp_exchange(mgr, fd, udp_delay);
|
||||||
|
s_udp_done = true;
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
static void recv_wait_task(void *args) {
|
static void recv_wait_task(void *args) {
|
||||||
char buf;
|
char buf;
|
||||||
int fd = (int) (uintptr_t) args;
|
int fd = (int) (uintptr_t) args;
|
||||||
@@ -519,12 +722,22 @@ static void task2(void *args) {
|
|||||||
struct mg_mgr *mgr = (struct mg_mgr *)args; // Event manager
|
struct mg_mgr *mgr = (struct mg_mgr *)args; // Event manager
|
||||||
char *ip;
|
char *ip;
|
||||||
int status;
|
int status;
|
||||||
pid_t first, second;
|
pid_t first, second, udp;
|
||||||
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(500)); // 500 ms
|
vTaskDelay(pdMS_TO_TICKS(500)); // 500 ms
|
||||||
s_error = false;
|
s_error = false;
|
||||||
s_wait = false;
|
s_wait = false;
|
||||||
ip = mg_mprintf("%M", mg_print_ip4, &mgr->ifp->ip);
|
ip = mg_mprintf("%M", mg_print_ip4, &mgr->ifp->ip);
|
||||||
|
s_udp_ready = s_udp_done = false;
|
||||||
|
ASSERT(xTaskCreate(udp_task, "udp", 512, mgr, configMAX_PRIORITIES - 1,
|
||||||
|
NULL) == pdPASS);
|
||||||
|
while (!s_udp_ready) vTaskDelay(1);
|
||||||
|
udp = start_udp(ip);
|
||||||
|
while (!client_done(udp, &status)) vTaskDelay(1);
|
||||||
|
check_client(status);
|
||||||
|
while (!s_udp_done) vTaskDelay(1);
|
||||||
|
DASHBOARD("udp");
|
||||||
|
|
||||||
s_poll_waiting = s_poll_done = false;
|
s_poll_waiting = s_poll_done = false;
|
||||||
ASSERT(xTaskCreate(poll_task, "poll", 512, NULL,
|
ASSERT(xTaskCreate(poll_task, "poll", 512, NULL,
|
||||||
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||||
@@ -555,7 +768,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 +782,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
|
||||||
@@ -859,6 +1065,20 @@ static void nortos_echo(struct mg_mgr *mgr, struct nortos_echo_client *clients,
|
|||||||
nortos_wait_client(mgr, pid);
|
nortos_wait_client(mgr, pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void udp_poll(struct mg_mgr *mgr) {
|
||||||
|
mg_mgr_poll(mgr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nortos_udp(struct mg_mgr *mgr, const char *ip) {
|
||||||
|
pid_t pid;
|
||||||
|
int fd = open_udp();
|
||||||
|
|
||||||
|
pid = start_udp(ip);
|
||||||
|
udp_exchange(mgr, fd, udp_poll);
|
||||||
|
nortos_wait_client(mgr, pid);
|
||||||
|
DASHBOARD("nortos_udp");
|
||||||
|
}
|
||||||
|
|
||||||
static void nortos_test(struct mg_mgr *mgr) {
|
static void nortos_test(struct mg_mgr *mgr) {
|
||||||
static const char bootstrap = '\0';
|
static const char bootstrap = '\0';
|
||||||
static const char grow[512] = {0};
|
static const char grow[512] = {0};
|
||||||
@@ -960,6 +1180,7 @@ static void nortos_test(struct mg_mgr *mgr) {
|
|||||||
ASSERT(close(fd) == 0);
|
ASSERT(close(fd) == 0);
|
||||||
mg_mgr_poll(mgr, 0);
|
mg_mgr_poll(mgr, 0);
|
||||||
DASHBOARD("nortos_nonblocking_connect");
|
DASHBOARD("nortos_nonblocking_connect");
|
||||||
|
nortos_udp(mgr, ip);
|
||||||
mg_free(ip);
|
mg_free(ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+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);
|
||||||
|
|||||||
@@ -1,4 +1 @@
|
|||||||
#define MG_ARCH MG_ARCH_ESP32
|
#define MG_ARCH MG_ARCH_ESP32
|
||||||
|
|
||||||
#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());
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
struct echo_server {
|
struct echo_server {
|
||||||
@@ -43,12 +42,12 @@ int echo_init(uint16_t port) {
|
|||||||
|
|
||||||
if (fd < 0 || !echo_nonblock(fd) ||
|
if (fd < 0 || !echo_nonblock(fd) ||
|
||||||
bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0 || listen(fd, 3) < 0) {
|
bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0 || listen(fd, 3) < 0) {
|
||||||
printf("BSD echo init: %d\n", errno);
|
MG_ERROR(("BSD echo init: %d", errno));
|
||||||
if (fd >= 0) close(fd);
|
if (fd >= 0) close(fd);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
s_server.sock = fd;
|
s_server.sock = fd;
|
||||||
printf("BSD echo server on port %hu\n", port);
|
MG_INFO(("BSD echo server on port %hu", port));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +79,7 @@ void echo_poll(void) {
|
|||||||
if (fd >= 0) {
|
if (fd >= 0) {
|
||||||
c = (struct echo_client *) calloc(1, sizeof(*c));
|
c = (struct echo_client *) calloc(1, sizeof(*c));
|
||||||
if (c == NULL || !echo_nonblock(fd)) {
|
if (c == NULL || !echo_nonblock(fd)) {
|
||||||
printf("BSD echo accept: %d\n", c == NULL ? ENOMEM : errno);
|
MG_ERROR(("BSD echo accept: %d", c == NULL ? ENOMEM : errno));
|
||||||
close(fd);
|
close(fd);
|
||||||
free(c);
|
free(c);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# Copyright (c) 2026 Cesanta Software Limited
|
||||||
|
# Environment setup: https://mongoose.ws/docs/getting-started/build-environment/
|
||||||
|
|
||||||
|
MGDIR = ../../../..
|
||||||
|
CFLAGS = -W -Wall -Wextra -Wundef -Wshadow -Wdouble-promotion
|
||||||
|
CFLAGS += -Wformat-truncation -fno-common -Wconversion -Wno-sign-conversion
|
||||||
|
CFLAGS += -g3 -Os -ffunction-sections -fdata-sections
|
||||||
|
CFLAGS += -I. -I$(MGDIR) -Icmsis_core/CMSIS/Core/Include -Icmsis_h7/Include
|
||||||
|
CFLAGS += -mcpu=cortex-m7 -mthumb -mfloat-abi=hard -mfpu=fpv5-d16 $(CFLAGS_EXTRA)
|
||||||
|
LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs nosys.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map
|
||||||
|
|
||||||
|
SOURCES = main.c hal.c udp.c
|
||||||
|
SOURCES += cmsis_h7/Source/Templates/gcc/startup_stm32h723xx.s
|
||||||
|
SOURCES += $(MGDIR)/mongoose.c
|
||||||
|
|
||||||
|
all build example: firmware.bin
|
||||||
|
|
||||||
|
firmware.elf: cmsis_core cmsis_h7 hal.h link.ld Makefile $(SOURCES) $(MGDIR)/mongoose.h mongoose_config.h
|
||||||
|
arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(CFLAGS_EXTRA) $(LDFLAGS) -o $@
|
||||||
|
|
||||||
|
firmware.bin: firmware.elf
|
||||||
|
arm-none-eabi-objcopy -O binary $< $@
|
||||||
|
@echo
|
||||||
|
@echo "To flash, run 'make flash', or use STM32CubeProgrammer"
|
||||||
|
|
||||||
|
flash: firmware.bin
|
||||||
|
STM32_Programmer_CLI -c port=swd -w firmware.elf -hardRst
|
||||||
|
|
||||||
|
cmsis_core:
|
||||||
|
git clone -q -c advice.detachedHead=false --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@
|
||||||
|
|
||||||
|
cmsis_h7:
|
||||||
|
git clone -q -c advice.detachedHead=false --depth 1 -b v1.10.6 https://github.com/STMicroelectronics/cmsis_device_h7 $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf firmware.* cmsis_*
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
// Copyright (c) 2026 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
|
||||||
|
#include "hal.h"
|
||||||
|
|
||||||
|
bool hal_timer_expired(volatile uint64_t *t, uint64_t period, uint64_t now) {
|
||||||
|
uint64_t diff = now - *t; // Wrap-safe elapsed time since last expiry
|
||||||
|
if (period == 0) return false; // Avoid division by zero
|
||||||
|
if (diff < period) return false; // Period has not elapsed yet
|
||||||
|
*t += (diff / period) * period; // Preserve cadence, skip missed periods
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static volatile uint64_t s_ticks; // Milliseconds since boot
|
||||||
|
void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms
|
||||||
|
s_ticks++;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t hal_get_tick(void) {
|
||||||
|
return s_ticks;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t SystemCoreClock = 160000000;
|
||||||
|
void SystemInit(void) { // Called automatically by startup code
|
||||||
|
hal_system_init(); // Enable FPU
|
||||||
|
hal_clock_init();
|
||||||
|
SysTick_Config(SystemCoreClock / 1000); // Sys tick every 1ms
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExitRun0Mode(void) {
|
||||||
|
}
|
||||||
|
|
||||||
|
struct stat;
|
||||||
|
__attribute__((weak)) int _fstat(int fd, struct stat *st) {
|
||||||
|
(void) fd, (void) st;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern unsigned char _end[]; // End of data section, start of heap. See link.ld
|
||||||
|
static unsigned char *s_current_heap_end = _end;
|
||||||
|
|
||||||
|
size_t hal_ram_used(void) {
|
||||||
|
return (size_t) (s_current_heap_end - _end);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t hal_ram_free(void) {
|
||||||
|
unsigned char endofstack;
|
||||||
|
return (size_t) (&endofstack - s_current_heap_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *_sbrk(int incr) {
|
||||||
|
unsigned char *prev_heap;
|
||||||
|
unsigned char *heap_end = (unsigned char *) ((size_t) &heap_end - 256);
|
||||||
|
prev_heap = s_current_heap_end;
|
||||||
|
// Check how much space we got from the heap end to the stack end
|
||||||
|
if (s_current_heap_end + incr > heap_end) return (void *) -1;
|
||||||
|
s_current_heap_end += incr;
|
||||||
|
return prev_heap;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) int _open(const char *path) {
|
||||||
|
(void) path;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) int _close(int fd) {
|
||||||
|
(void) fd;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) int _isatty(int fd) {
|
||||||
|
(void) fd;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) int _lseek(int fd, int ptr, int dir) {
|
||||||
|
(void) fd, (void) ptr, (void) dir;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) void _exit(int status) {
|
||||||
|
(void) status;
|
||||||
|
for (;;) asm volatile("BKPT #0");
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) void _kill(int pid, int sig) {
|
||||||
|
(void) pid, (void) sig;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) int _getpid(void) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) int _write(int fd, char *ptr, int len) {
|
||||||
|
(void) fd, (void) ptr, (void) len;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) int _read(int fd, char *ptr, int len) {
|
||||||
|
(void) fd, (void) ptr, (void) len;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) int _link(const char *a, const char *b) {
|
||||||
|
(void) a, (void) b;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) int _unlink(const char *a) {
|
||||||
|
(void) a;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) int _stat(const char *path, struct stat *st) {
|
||||||
|
(void) path, (void) st;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) int mkdir(const char *path, int mode) {
|
||||||
|
(void) path, (void) mode;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) void _init(void) {
|
||||||
|
}
|
||||||
@@ -0,0 +1,225 @@
|
|||||||
|
// Copyright (c) 2022-2026 Cesanta Software Limited
|
||||||
|
// Datasheet: RM0468, devboard manual: UM2407
|
||||||
|
// https://www.st.com/resource/en/reference_manual/rm0468-stm32h723733-stm32h725735-and-stm32h730-value-line-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
|
||||||
|
// https://www.st.com/resource/en/user_manual/um2407-stm32h7-nucleo144-boards-mb1364-stmicroelectronics.pdf
|
||||||
|
// Alternate functions: https://www.st.com/resource/en/datasheet/stm32h723.pdf
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stm32h723xx.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
extern bool hal_timer_expired(volatile uint64_t *t, uint64_t prd, uint64_t now);
|
||||||
|
extern uint64_t hal_get_tick(void);
|
||||||
|
extern size_t hal_ram_free(void);
|
||||||
|
extern size_t hal_ram_used(void);
|
||||||
|
extern uint32_t SystemCoreClock;
|
||||||
|
|
||||||
|
#define BIT(x) (1UL << (x))
|
||||||
|
#define CLRSET(reg, clear, set) ((reg) = ((reg) & ~(clear)) | (set))
|
||||||
|
#define PIN(bank, num) ((((bank) - 'A') << 8) | (num))
|
||||||
|
#define PINNO(pin) (pin & 255)
|
||||||
|
#define PINBANK(pin) (pin >> 8)
|
||||||
|
|
||||||
|
#define HAL_ETH_PINS PIN('A', 1), PIN('A', 2), PIN('A', 7), \
|
||||||
|
PIN('B', 13), PIN('C', 1), PIN('C', 4), \
|
||||||
|
PIN('C', 5), PIN('G', 11), PIN('G', 13)
|
||||||
|
|
||||||
|
// System clock (2.1, Figure 1; 8.5, Figure 45; 8.5.5, Figure 47; 8.5.6, Figure
|
||||||
|
// 49; 8.5.8 Table 56; datasheet) CPU_FREQUENCY <= 550 MHz; hclk = CPU_FREQUENCY
|
||||||
|
// / HAL_HPRE ; hclk <= 275 MHz; APB clocks <= 137.5 MHz. D1 domain bus matrix (and
|
||||||
|
// so flash) runs at hclk frequency. Configure flash latency (WS) in accordance
|
||||||
|
// to hclk freq (4.3.8, Table 16) The Ethernet controller is in D2 domain and
|
||||||
|
// runs at hclk frequency
|
||||||
|
enum {
|
||||||
|
HAL_D1CPRE = 1, // actual divisor value
|
||||||
|
HAL_HPRE = 2, // actual divisor value
|
||||||
|
HAL_D1PPRE = 4, // register values, divisor value = BIT(value - 3) = / 2
|
||||||
|
HAL_D2PPRE1 = 4,
|
||||||
|
HAL_D2PPRE2 = 4,
|
||||||
|
HAL_D3PPRE = 4
|
||||||
|
};
|
||||||
|
// PLL1_P: odd division factors are not allowed (8.7.12) (except for '1')
|
||||||
|
enum { PLL1_HSI = 64, PLL1_M = 32, PLL1_N = 225, PLL1_P = 1 };
|
||||||
|
#define HAL_FLASH_LATENCY 0x33 // WRHIGHFREQ LATENCY
|
||||||
|
#define SYS_FREQUENCY ((PLL1_HSI * PLL1_N / PLL1_M / PLL1_P / HAL_D1CPRE) * 1000000)
|
||||||
|
#define AHB_FREQUENCY (SYS_FREQUENCY / HAL_HPRE)
|
||||||
|
#define APB2_FREQUENCY (AHB_FREQUENCY / (BIT(HAL_D2PPRE2 - 3)))
|
||||||
|
#define APB1_FREQUENCY (AHB_FREQUENCY / (BIT(HAL_D2PPRE1 - 3)))
|
||||||
|
|
||||||
|
|
||||||
|
enum { HAL_GPIO_MODE_INPUT, HAL_GPIO_MODE_OUTPUT, HAL_GPIO_MODE_AF, HAL_GPIO_MODE_ANALOG };
|
||||||
|
enum { HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_OTYPE_OPEN_DRAIN };
|
||||||
|
enum { HAL_GPIO_SPEED_LOW, HAL_GPIO_SPEED_MEDIUM, HAL_GPIO_SPEED_HIGH, HAL_GPIO_SPEED_INSANE };
|
||||||
|
enum { HAL_GPIO_PULL_NONE, HAL_GPIO_PULL_UP, HAL_GPIO_PULL_DOWN };
|
||||||
|
|
||||||
|
#define HAL_GPIO(N) ((GPIO_TypeDef *) (0x40000000 + 0x18020000UL + 0x400 * (N)))
|
||||||
|
|
||||||
|
static GPIO_TypeDef *hal_gpio_bank(uint16_t pin) {
|
||||||
|
return HAL_GPIO(PINBANK(pin));
|
||||||
|
}
|
||||||
|
static inline void hal_gpio_toggle(uint16_t pin) {
|
||||||
|
GPIO_TypeDef *gpio = hal_gpio_bank(pin);
|
||||||
|
uint32_t mask = BIT(PINNO(pin));
|
||||||
|
gpio->BSRR = mask << (gpio->ODR & mask ? 16 : 0);
|
||||||
|
}
|
||||||
|
static inline int hal_gpio_read(uint16_t pin) {
|
||||||
|
return hal_gpio_bank(pin)->IDR & BIT(PINNO(pin)) ? 1 : 0;
|
||||||
|
}
|
||||||
|
static inline void hal_gpio_write(uint16_t pin, bool val) {
|
||||||
|
GPIO_TypeDef *gpio = hal_gpio_bank(pin);
|
||||||
|
gpio->BSRR = BIT(PINNO(pin)) << (val ? 0 : 16);
|
||||||
|
}
|
||||||
|
static inline void hal_gpio_init(uint16_t pin, uint8_t mode, uint8_t type,
|
||||||
|
uint8_t speed, uint8_t pull, uint8_t af) {
|
||||||
|
GPIO_TypeDef *gpio = hal_gpio_bank(pin);
|
||||||
|
uint8_t n = (uint8_t) (PINNO(pin));
|
||||||
|
RCC->AHB4ENR |= BIT(PINBANK(pin)); // Enable GPIO clock
|
||||||
|
CLRSET(gpio->OTYPER, 1UL << n, ((uint32_t) type) << n);
|
||||||
|
CLRSET(gpio->OSPEEDR, 3UL << (n * 2), ((uint32_t) speed) << (n * 2));
|
||||||
|
CLRSET(gpio->PUPDR, 3UL << (n * 2), ((uint32_t) pull) << (n * 2));
|
||||||
|
CLRSET(gpio->AFR[n >> 3], 15UL << ((n & 7) * 4),
|
||||||
|
((uint32_t) af) << ((n & 7) * 4));
|
||||||
|
CLRSET(gpio->MODER, 3UL << (n * 2), ((uint32_t) mode) << (n * 2));
|
||||||
|
}
|
||||||
|
static inline void hal_gpio_input(uint16_t pin) {
|
||||||
|
hal_gpio_init(pin, HAL_GPIO_MODE_INPUT, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_HIGH,
|
||||||
|
HAL_GPIO_PULL_NONE, 0);
|
||||||
|
}
|
||||||
|
static inline void hal_gpio_output(uint16_t pin) {
|
||||||
|
hal_gpio_init(pin, HAL_GPIO_MODE_OUTPUT, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_HIGH,
|
||||||
|
HAL_GPIO_PULL_NONE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// D2 Kernel clock (8.7.21) USART1 defaults to pclk2 (APB2), while USART2,3
|
||||||
|
// default to pclk1 (APB1). Even if using other kernel clocks, the APBx clocks
|
||||||
|
// must be enabled for CPU access, as the kernel clock drives the BRR, not the
|
||||||
|
// APB bus interface
|
||||||
|
static inline void hal_uart_init(USART_TypeDef *uart, uint16_t tx,
|
||||||
|
uint16_t rx, unsigned long baud) {
|
||||||
|
uint8_t af = 7; // Alternate function
|
||||||
|
uint32_t freq = 0; // Bus frequency. UART1 is on APB2, rest on APB1
|
||||||
|
|
||||||
|
if (uart == USART1) freq = APB2_FREQUENCY, RCC->APB2ENR |= BIT(4);
|
||||||
|
if (uart == USART2) freq = APB1_FREQUENCY, RCC->APB1LENR |= BIT(17);
|
||||||
|
if (uart == USART3) freq = APB1_FREQUENCY, RCC->APB1LENR |= BIT(18);
|
||||||
|
|
||||||
|
#if 0 // CONSTANT BAUD RATE FOR REMOTE DEBUGGING WHILE SETTING THE PLL
|
||||||
|
CLRSET(RCC->D2CCIP2R, 7 << 3, 3 << 3); // use HSI for UART1
|
||||||
|
freq = 64000000;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
hal_gpio_init(tx, HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_HIGH, 0, af);
|
||||||
|
hal_gpio_init(rx, HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_HIGH, 0, af);
|
||||||
|
uart->CR1 = 0; // Disable this UART
|
||||||
|
uart->BRR = freq / baud; // Set baud rate
|
||||||
|
uart->CR1 = BIT(0) | BIT(2) | BIT(3); // Set UE, RE, TE
|
||||||
|
}
|
||||||
|
static inline void hal_uart_write_byte(USART_TypeDef *uart, uint8_t byte) {
|
||||||
|
uart->TDR = byte;
|
||||||
|
while ((uart->ISR & USART_ISR_TXE_TXFNF) == 0) (void) 0;
|
||||||
|
}
|
||||||
|
static inline void hal_uart_write_buf(USART_TypeDef *uart, char *buf, size_t len) {
|
||||||
|
while (len-- > 0) hal_uart_write_byte(uart, *(uint8_t *) buf++);
|
||||||
|
}
|
||||||
|
static inline int hal_uart_read_ready(USART_TypeDef *uart) {
|
||||||
|
return uart->ISR & BIT(5); // If RXNE bit is set, data is ready
|
||||||
|
}
|
||||||
|
static inline uint8_t hal_uart_read_byte(USART_TypeDef *uart) {
|
||||||
|
return (uint8_t) (uart->RDR & 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
// RNG clock (rng_clk) defaults to HSI48 and is characterized at this frequency.
|
||||||
|
// Enable at SystemInit. (STM32CubeMX) It won't work with a 275MHz (AHB)
|
||||||
|
static inline void hal_rng_init(void) {
|
||||||
|
RCC->AHB2ENR |= RCC_AHB2ENR_RNGEN; // Enable RNG AHB clock (hal_rng_hclk)
|
||||||
|
// 39.6.2 Table 320 init
|
||||||
|
RNG->CR = RNG_CR_CONDRST | (0x0F << RNG_CR_RNG_CONFIG1_Pos) |
|
||||||
|
(0 << RNG_CR_RNG_CONFIG2_Pos) | (0xD << RNG_CR_RNG_CONFIG3_Pos);
|
||||||
|
RNG->HTCR = 0x17590abc;
|
||||||
|
RNG->HTCR = 0xaa74;
|
||||||
|
RNG->CR &= ~RNG_CR_CONDRST ;
|
||||||
|
while (RNG->CR & RNG_CR_CONDRST) (void) 0; // 39.7.1
|
||||||
|
RNG->CR |= RNG_CR_RNGEN; // Enable RNG
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t hal_rng_read(void) {
|
||||||
|
while ((RNG->SR & RNG_SR_DRDY) == 0) (void) 0;
|
||||||
|
return RNG->DR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hw pull-ups on PHY RXD0,1,DV to enable autonegotiation
|
||||||
|
static inline void hal_ethernet_init(void) {
|
||||||
|
// Initialise Ethernet. Enable MAC GPIO pins, see
|
||||||
|
// https://www.st.com/resource/en/user_manual/um2407-stm32h7-nucleo144-boards-mb1364-stmicroelectronics.pdf
|
||||||
|
uint16_t pins[] = {HAL_ETH_PINS};
|
||||||
|
for (size_t i = 0; i < sizeof(pins) / sizeof(pins[0]); i++) {
|
||||||
|
hal_gpio_init(pins[i], HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_INSANE,
|
||||||
|
HAL_GPIO_PULL_NONE, 11); // 11 is the Ethernet function
|
||||||
|
}
|
||||||
|
NVIC_EnableIRQ(ETH_IRQn); // Setup Ethernet IRQ handler
|
||||||
|
CLRSET(SYSCFG->PMCR, 7 << 21, 4 << 21); // Use RMII (12.3.1)
|
||||||
|
RCC->AHB1ENR |= BIT(15) | BIT(16) | BIT(17); // Enable Ethernet clocks
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int hal_div2prescval(unsigned int div) {
|
||||||
|
// 0 --> /1; 8 --> /2 ... 11 --> /16; 12 --> /64 ... 15 --> /512
|
||||||
|
if (div == 1) return 0;
|
||||||
|
if (div > 16) div /= 2;
|
||||||
|
unsigned int val = 7;
|
||||||
|
while (div >>= 1) ++val;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int hal_pllrge(unsigned int f) {
|
||||||
|
unsigned int val = 0;
|
||||||
|
while (f >>= 1) ++val;
|
||||||
|
return val - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable RTC/BKP APB clock and backup domain write access.
|
||||||
|
// Must be called before accessing RTC backup registers or IWDG.
|
||||||
|
static inline void hal_backup_domain_init(void) {
|
||||||
|
RCC->APB4ENR |= RCC_APB4ENR_RTCAPBEN; // enable RTC/BKP APB clock
|
||||||
|
PWR->CR1 |= PWR_CR1_DBP; // enable backup domain write access
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void hal_clock_init(void) {
|
||||||
|
hal_backup_domain_init();
|
||||||
|
PWR->CR3 |= PWR_CR3_LDOEN; // select LDO (reset value)
|
||||||
|
while ((PWR->CSR1 & PWR_CSR1_ACTVOSRDY) == 0) (void) 0; // ACTVOSRDY
|
||||||
|
PWR->D3CR &= ~PWR_D3CR_VOS_Msk; // Select VOS0
|
||||||
|
while ((PWR->D3CR & PWR_D3CR_VOSRDY) == 0) (void) 0; // VOSRDY
|
||||||
|
CLRSET(
|
||||||
|
RCC->D1CFGR, (0x0F << 8) | (7 << 4) | (0x0F << 0),
|
||||||
|
(hal_div2prescval(HAL_D1CPRE) << 8) | (HAL_D1PPRE << 4) | (hal_div2prescval(HAL_HPRE) << 0));
|
||||||
|
RCC->D2CFGR = (HAL_D2PPRE2 << 8) | (HAL_D2PPRE1 << 4);
|
||||||
|
RCC->D3CFGR = (HAL_D3PPRE << 4);
|
||||||
|
CLRSET(RCC->PLLCFGR, 3 << 2,
|
||||||
|
hal_pllrge(PLL1_HSI / PLL1_M)
|
||||||
|
<< 2); // keep reset config (DIVP1EN, !PLL1VCOSEL), PLL1RGE
|
||||||
|
CLRSET(RCC->PLL1DIVR, (0x7F << 9) | (0x1FF << 0),
|
||||||
|
((PLL1_P - 1) << 9) | ((PLL1_N - 1) << 0)); // Set PLL1_P PLL1_N
|
||||||
|
CLRSET(RCC->PLLCKSELR, 0x3F << 4,
|
||||||
|
PLL1_M << 4); // Set PLL1_M (source defaults to HSI)
|
||||||
|
RCC->CR |= RCC_CR_PLL1ON | RCC_CR_HSI48ON; // Enable PLL1 and HSI48 (for RNG)
|
||||||
|
while ((RCC->CR & RCC_CR_PLL1RDY) == 0) (void) 0; // Wait until done
|
||||||
|
RCC->CFGR |= (3 << 0); // Set clock source to PLL1
|
||||||
|
while ((RCC->CFGR & (7 << 3)) != (3 << 3)) (void) 0; // Wait until done
|
||||||
|
FLASH->ACR = HAL_FLASH_LATENCY; // default is larger
|
||||||
|
RCC->APB4ENR |= RCC_APB4ENR_SYSCFGEN; // Enable SYSCFG
|
||||||
|
while ((RCC->CR & RCC_CR_HSI48RDY) == 0) (void) 0; // Make sure HSI48 is ready
|
||||||
|
SystemCoreClock = SYS_FREQUENCY; // Update SystemCoreClock global var
|
||||||
|
SysTick_Config(SystemCoreClock / 1000); // Sys tick every 1ms
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline void hal_system_init(void) {
|
||||||
|
SCB->CPACR |= ((3UL << 10 * 2) | (3UL << 11 * 2)); // Enable FPU
|
||||||
|
__DSB();
|
||||||
|
__ISB();
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
ENTRY(Reset_Handler);
|
||||||
|
MEMORY {
|
||||||
|
flash(rx) : ORIGIN = 0x08000000, LENGTH = 1024k
|
||||||
|
|
||||||
|
dtcmram (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
|
||||||
|
ram_d1 (rwx) : ORIGIN = 0x24000000, LENGTH = 320K
|
||||||
|
ram_d2 (rwx) : ORIGIN = 0x30000000, LENGTH = 32K
|
||||||
|
ram_d3 (rwx) : ORIGIN = 0x38000000, LENGTH = 16K
|
||||||
|
itcmram (rwx) : ORIGIN = 0x00000000, LENGTH = 64K
|
||||||
|
}
|
||||||
|
_estack = ORIGIN(ram_d1) + LENGTH(ram_d1);
|
||||||
|
|
||||||
|
SECTIONS {
|
||||||
|
.vectors : { KEEP(*(.isr_vector)) } > flash
|
||||||
|
.text : { *(.text* .text.*) } > flash
|
||||||
|
.rodata : { *(.rodata*) } > flash
|
||||||
|
|
||||||
|
/* Note the .iram section, for functions copied to RAM. Required for MG_IRAM OTA support */
|
||||||
|
.data : { _sdata = .; *(.first_data) *(.iram .iram* .iram.*) *(.data SORT(.data.*)) _edata = .; } > ram_d1 AT > flash
|
||||||
|
_sidata = LOADADDR(.data);
|
||||||
|
|
||||||
|
.bss : { _sbss = .; *(.bss SORT(.bss.*) COMMON) _ebss = .; } > ram_d1
|
||||||
|
|
||||||
|
.eth_ram : { *(.eth_ram .eth_ram*) } > ram_d1 AT > flash
|
||||||
|
|
||||||
|
/* Health record. NOLOAD and in unused DTCM, so startup code neither copies
|
||||||
|
nor zeroes it and the contents survive a warm reset */
|
||||||
|
.mg_health (NOLOAD) : { KEEP(*(.mg_health .mg_health*)) } > dtcmram
|
||||||
|
|
||||||
|
. = ALIGN(8);
|
||||||
|
_end = .;
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
// Copyright (c) 2026 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
|
||||||
|
#include "hal.h"
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
int udp_init(uint16_t port);
|
||||||
|
void udp_poll(void);
|
||||||
|
|
||||||
|
#ifndef UART_DEBUG
|
||||||
|
#define UART_DEBUG USART3
|
||||||
|
#define UART_DEBUG_TX_PIN PIN('D', 8)
|
||||||
|
#define UART_DEBUG_RX_PIN PIN('D', 9)
|
||||||
|
#else
|
||||||
|
#define UART_DEBUG_TX_PIN PIN('A', 9)
|
||||||
|
#define UART_DEBUG_RX_PIN PIN('A', 10)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define LED1 PIN('B', 0)
|
||||||
|
#define LED2 PIN('E', 1)
|
||||||
|
#define LED3 PIN('B', 14)
|
||||||
|
|
||||||
|
static void log_fn(char ch, void *param) {
|
||||||
|
hal_uart_write_buf(param, &ch, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void blink_task(void) {
|
||||||
|
static uint64_t blink_timer = 0;
|
||||||
|
if (hal_timer_expired(&blink_timer, 500, hal_get_tick())) {
|
||||||
|
hal_gpio_toggle(LED2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t mg_millis(void) {
|
||||||
|
return hal_get_tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mg_random(void *buf, size_t len) {
|
||||||
|
for (size_t n = 0; n < len; n += sizeof(uint32_t)) {
|
||||||
|
uint32_t r = hal_rng_read();
|
||||||
|
memcpy((char *) buf + n, &r, n + sizeof(r) > len ? len - n : sizeof(r));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
|
||||||
|
hal_clock_init();
|
||||||
|
hal_uart_init(UART_DEBUG, UART_DEBUG_TX_PIN, UART_DEBUG_RX_PIN, 115200);
|
||||||
|
mg_log_set_fn(log_fn, UART_DEBUG);
|
||||||
|
hal_rng_init();
|
||||||
|
hal_ethernet_init();
|
||||||
|
hal_gpio_output(LED1);
|
||||||
|
hal_gpio_output(LED2);
|
||||||
|
hal_gpio_output(LED3);
|
||||||
|
|
||||||
|
MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000));
|
||||||
|
mg_mgr_init(&mgr);
|
||||||
|
udp_init(1234);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
mg_mgr_poll(&mgr, 0);
|
||||||
|
udp_poll();
|
||||||
|
blink_task();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "hal.h"
|
||||||
|
|
||||||
|
// See https://mongoose.ws/docs/getting-started/build-options/
|
||||||
|
#define MG_ARCH MG_ARCH_ARMGCC
|
||||||
|
#define MG_TLS MG_TLS_BUILTIN
|
||||||
|
#define MG_OTA MG_OTA_STM32H7
|
||||||
|
|
||||||
|
#define MG_ENABLE_TCPIP 1
|
||||||
|
#define MG_ENABLE_BSD_SOCKETS 1
|
||||||
|
#define MG_ENABLE_CUSTOM_MILLIS 1
|
||||||
|
#define MG_ENABLE_CUSTOM_RANDOM 1
|
||||||
|
#define MG_ENABLE_DRIVER_STM32H 1
|
||||||
|
#define MG_ENABLE_PACKED_FS 1
|
||||||
|
#define MG_ETH_RAM __attribute__((section(".eth_ram")))
|
||||||
|
|
||||||
|
// #define MG_DRIVER_MDC_CR 4 // RMII MDC clock divider, from 0 to 5
|
||||||
|
// #define MG_TCPIP_PHY_ADDR 0 // PHY address
|
||||||
|
|
||||||
|
// For static IP configuration, define MG_TCPIP_{IP,MASK,GW}
|
||||||
|
// By default, those are set to zero, meaning that DHCP is used
|
||||||
|
//
|
||||||
|
// #define MG_TCPIP_IP MG_IPV4(192, 168, 0, 10) // IP
|
||||||
|
// #define MG_TCPIP_GW MG_IPV4(192, 168, 0, 1) // Gateway
|
||||||
|
// #define MG_TCPIP_MASK MG_IPV4(255, 255, 255, 0) // Netmask
|
||||||
|
|
||||||
|
// Construct MAC address from the MCU unique ID
|
||||||
|
#define MGUID ((uint32_t *) UID_BASE)
|
||||||
|
#define MG_SET_MAC_ADDRESS(mac) \
|
||||||
|
do { \
|
||||||
|
mac[0] = 2; \
|
||||||
|
mac[1] = MGUID[0] & 255; \
|
||||||
|
mac[2] = (MGUID[0] >> 10) & 255; \
|
||||||
|
mac[3] = (MGUID[0] >> 19) & 255; \
|
||||||
|
mac[4] = MGUID[1] & 255; \
|
||||||
|
mac[5] = MGUID[2] & 255; \
|
||||||
|
} while (0)
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
// Copyright (c) 2026 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
struct udp_server {
|
||||||
|
int fd;
|
||||||
|
bool pending;
|
||||||
|
struct sockaddr_in peer;
|
||||||
|
socklen_t peerlen;
|
||||||
|
size_t len;
|
||||||
|
char buf[128];
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct udp_server s_server = {.fd = -1};
|
||||||
|
|
||||||
|
static int udp_nonblock(int fd) {
|
||||||
|
int flags = fcntl(fd, F_GETFL, 0);
|
||||||
|
return flags >= 0 && fcntl(fd, F_SETFL, flags | O_NONBLOCK) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int udp_init(uint16_t port) {
|
||||||
|
struct sockaddr_in sa = {
|
||||||
|
.sin_family = AF_INET,
|
||||||
|
.sin_port = htons(port),
|
||||||
|
.sin_addr = {.s_addr = INADDR_ANY},
|
||||||
|
};
|
||||||
|
int fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
|
||||||
|
if (fd < 0 || !udp_nonblock(fd) ||
|
||||||
|
bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
|
||||||
|
MG_ERROR(("BSD UDP init: %d", errno));
|
||||||
|
if (fd >= 0) close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
s_server.fd = fd;
|
||||||
|
MG_INFO(("BSD UDP echo server on port %hu", port));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void udp_poll(void) {
|
||||||
|
struct udp_server *s = &s_server;
|
||||||
|
struct pollfd p;
|
||||||
|
ssize_t n;
|
||||||
|
|
||||||
|
if (s->fd < 0) return;
|
||||||
|
p.fd = s->fd, p.events = s->pending ? POLLOUT : POLLIN, p.revents = 0;
|
||||||
|
if (poll(&p, 1, 0) <= 0) return;
|
||||||
|
if (s->pending) {
|
||||||
|
n = sendto(s->fd, s->buf, s->len, 0, (struct sockaddr *) &s->peer,
|
||||||
|
s->peerlen);
|
||||||
|
if (n == (ssize_t) s->len) {
|
||||||
|
s->pending = false;
|
||||||
|
} else if (n < 0 && errno != EAGAIN) {
|
||||||
|
MG_ERROR(("BSD UDP send: %d", errno));
|
||||||
|
s->pending = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s->peerlen = sizeof(s->peer);
|
||||||
|
n = recvfrom(s->fd, s->buf, sizeof(s->buf), 0,
|
||||||
|
(struct sockaddr *) &s->peer, &s->peerlen);
|
||||||
|
if (n >= 0) {
|
||||||
|
s->len = (size_t) n, s->pending = true;
|
||||||
|
} else if (errno != EAGAIN) {
|
||||||
|
MG_ERROR(("BSD UDP recv: %d", errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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