diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 514f9a82..63a8b8eb 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -159,6 +159,21 @@ jobs:
- if: ${{ env.GO == 1 }}
run: make -C examples/stm32/nucleo-h743zi-baremetal test VCON_API_KEY=${{secrets.VCON_API_KEY}}
+ test_tm4c:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ with: { fetch-depth: 2 }
+ - run: if ./test/match_changed_files.sh "^src|examples/ti/ek-tm4c" ; then echo GO=1 >> $GITHUB_ENV ; fi
+ - if: ${{ env.GO == 1 }}
+ run: sudo apt -y update; sudo apt -y install gcc-arm-none-eabi
+ - run: echo GO=0 > $GITHUB_ENV ; if ./test/match_changed_files.sh "^src|examples/ti/ek-tm4c1294xl-baremetal" ; then echo GO=1 > $GITHUB_ENV ; fi
+ - if: ${{ env.GO == 1 }}
+ run: make -C examples/ti/ek-tm4c1294xl-baremetal test VCON_API_KEY=${{secrets.VCON_API_KEY}}
+ - run: echo GO=0 > $GITHUB_ENV ; if ./test/match_changed_files.sh "^src|examples/ti/ek-tm4c1294xl-freertos" ; then echo GO=1 > $GITHUB_ENV ; fi
+ - if: ${{ env.GO == 1 }}
+ run: make -C examples/ti/ek-tm4c1294xl-freertos test VCON_API_KEY=${{secrets.VCON_API_KEY}}
+
test_pico_w5500:
runs-on: ubuntu-latest
steps:
@@ -213,7 +228,7 @@ jobs:
- path: infineon/infineon-xmc4700_4800-lwip-rtx-rtos
- path: ti/ti-ek-tm4c1294xl-http-server
- path: ti/ek-tm4c1294xl-baremetal
- - path: ti/ek-tm4c1294xl-freertos-mip
+ - path: ti/ek-tm4c1294xl-freertos
- path: rp2040/pico-rmii
- path: rp2040/pico-rndis-dashboard
- path: rp2040/pico-w
diff --git a/README.md b/README.md
index fd3382e7..d54adffd 100644
--- a/README.md
+++ b/README.md
@@ -31,8 +31,7 @@ robust, and easy. Features include:
[bare metal example on Nucleo-F429ZI](examples/stm32/nucleo-f429zi-baremetal)
is only 6 files
- For comparison, a CubeIDE generated HTTP example is 400+ files
-- Detailed [user guide, API reference](https://mongoose.ws/documentation/) and
- [tons of tutorials](https://mongoose.ws/tutorials/) available
+- Detailed [user guide, API reference and tons of tutorials](https://mongoose.ws/documentation/) available
# Commercial use
diff --git a/docs/README.md b/docs/README.md
deleted file mode 100644
index 0f9b512f..00000000
--- a/docs/README.md
+++ /dev/null
@@ -1,4310 +0,0 @@
-# User Guide
-
-
-## Introduction
-
-Mongoose is a networking library for C/C++. It implements event-driven,
-non-blocking APIs for TCP, UDP, HTTP, WebSocket, MQTT. It connects devices
-and brings them online. Since 2004, a number of open source and commercial
-products have utilized it. It even runs on the
-International Space Station! Mongoose makes embedded network programming fast,
-robust, and easy.
-
-Mongoose works on Windows, Linux, Mac, and on a many embedded architectures
-such as STM32, NXP, TI, ESP32, and so on. It can run on top of the existing
-OS and TCP/IP stack like FreeRTOS and lwIP, as well as on a bare metal,
-utilising Mongoose's built-in TCP/IP stack and network drivers.
-
-## How to build and run examples
-
-The easiest way to start with Mongoose is to try to build and run examples.
-- Follow the [Build Tools](/tutorials/tools) tutorial to setup your
-development environment
-- If you're on Windows 10/11, make sure to enable "Developer mode"
-- Start a terminal / command prompt and execute the following commands:
- ```sh
- git clone https://github.com/cesanta/mongoose
- cd mongoose/examples/http-server
- make
- ```
-
-The above commands download Mongoose Library source code, then go into the
-HTTP server example directory, and execute `make` command. It uses a
-`Makefile` configuration file which is present in every example directory.
-It builds an example executable and runs it.
-
-Now start your browser, and point it to http://localhost:8000
-
-## 2-minute integration guide
-
-In order to integrate Mongoose into an existing C/C++ application or firmware,
-please use the following steps:
-
-**Step 1.** Copy
-[mongoose.c](https://raw.githubusercontent.com/cesanta/mongoose/master/mongoose.c)
-and
-[mongoose.h](https://raw.githubusercontent.com/cesanta/mongoose/master/mongoose.h)
-into the source code tree
-
-**Step 2.** Add the following lines in your `main.c` file:
-
-```c
-#include "mongoose.h"
-...
-
-static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
- if (ev == MG_EV_HTTP_MSG) {
- struct mg_http_message *hm = (struct mg_http_message *) ev_data;
- if (mg_http_match_uri(hm, "/api/hello")) { // On /api/hello requests,
- mg_http_reply(c, 200, "", "{%Q:%d}\n", "status", 1); // Send dynamic JSON response
- } else { // For all other URIs,
- struct mg_http_serve_opts opts = {.root_dir = "."}; // Serve files
- mg_http_serve_dir(c, hm, &opts); // From root_dir
- }
- }
-}
-...
-
-int main() {
- ...
-
- struct mg_mgr mgr;
- mg_mgr_init(&mgr); // Init manager
- mg_http_listen(&mgr, "http://0.0.0.0:8000", fn, &mgr); // Setup listener
- for (;;) mg_mgr_poll(&mgr, 1000); // Infinite event loop
-
- return 0;
-}
-```
-
-**Step 3.** Add `mongoose.c` to the build and recompile - and that is it!
-
-> NOTE: If you're building for some embedded systems, you may need to add
-> some extra compilation flags, for example on FreeRTOS it would be
-> `-DMG_ARCH=MG_ARCH_FREERTOS`. See [Build options](#build-options) below.
-
-
-`mg_mgr_poll()` iterates over all connections, accepts new connections, sends and
-receives data, closes connections, and calls event handler functions for the
-respective events.
-
-Each connection has two event handler functions: `c->fn` and `c->pfn`. The
-`c->fn` is a user-specified event handler function. The `c->pfn` is a
-protocol-specific handler function that is set implicitly. For example, a
-`mg_http_listen()` sets `c->pfn` to a Mongoose's HTTP event handler. A
-protocol-specific handler is called before a user-specific handler. It parses
-incoming data and may invoke protocol-specific events like `MG_EV_HTTP_MSG`.
-
-> NOTE: Since Mongoose's core is not protected against concurrent accesses,
-> make sure that all `mg_*` API functions are called from the same thread or
-> RTOS task.
-
-## Connections and event manager
-
-Mongoose has two basic data structures:
-
-- `struct mg_mgr` - An event manager that holds all active connections
-- `struct mg_connection` - A single connection descriptor
-
-Connections could be listening, outbound, or inbound. Outbound
-connections are created by the `mg_connect()` call. Listening connections are
-created by the `mg_listen()` call. Inbound connections are those accepted by a
-listening connection. Each connection is described by a `struct mg_connection`
-structure, which has a number of fields. All fields are exposed to the
-application by design, to give an application full visibility into
-Mongoose's internals.
-
-## Send and receive buffers
-
-Each connection has a send and receive buffer:
-- `struct mg_connection::send` - Data to be sent to a peer
-- `struct mg_connection::recv` - Data received from a peer
-
-When data arrives, Mongoose appends received data to the `recv` and triggers a
-`MG_EV_READ` event. The user may send data back by calling one of the output
-functions, like `mg_send()`, `mg_printf()` or a protocol-specific function like
-`mg_ws_send`. Output functions append data to the `send` buffer. When Mongoose
-successfully writes data to the socket, it discards data from struct `mg_connection::send`
-and sends an `MG_EV_WRITE` event.
-
-## Event handler function
-
-Each connection has an event handler function associated with it, which
-must be implemented by the user. Event handler is the key element of
-Mongoose, since it defines the connection's behavior. See below
-for an example of an event handler function:
-
-```c
-// Event handler function defines connection's behavior
-static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
- if (ev == MG_EV_READ) {
- mg_send(c, c->recv.buf, c->recv.len); // Implement echo server
- c->recv.len = 0; // Delete received data
- }
-}
-```
-
-- `struct mg_connection *c` - A connection that received an event
-- `int ev` - An event number, defined in mongoose.h. For example, when data
- arrives on an inbound connection, `ev` would be `MG_EV_READ`
-- `void *ev_data` - Points to the event-specific data, and it has a different
- meaning for different events. For example, for an `MG_EV_READ` event,
- `ev_data` is a `long *` pointing to the number of bytes received from a remote
- peer and saved into the `c->recv` IO buffer. The exact meaning of `ev_data` is
- described for each event. Protocol-specific events usually have `ev_data`
- pointing to structures that hold protocol-specific information
-- `void *fn_data` - A user-defined pointer for the connection, which is a
- placeholder for application-specific data. This `fn_data` pointer is set
- during the `*_listen()` or `*_connect()` call, and it is stored in the
- `c->fn_data`. Listening connections copy the value of `c->fn_data` to the
- newly accepted connection, so all accepted connections initially share the
- same `fn_data` pointer. It is fine to update/replace that pointer for
- any connection at any time by setting `c->fn_data = new_value;`
-
-## Events
-
-Below is the list of events triggered by Mongoose, taken as-is from `mongoose.h`.
-For each event, a comment describes the meaning of the `ev_data` pointer passed
-to an event handler:
-
-```c
-enum {
- MG_EV_ERROR, // Error char *error_message
- MG_EV_OPEN, // Connection created NULL
- MG_EV_POLL, // mg_mgr_poll iteration uint64_t *milliseconds
- MG_EV_RESOLVE, // Host name is resolved NULL
- MG_EV_CONNECT, // Connection established NULL
- MG_EV_ACCEPT, // Connection accepted NULL
- MG_EV_TLS_HS, // TLS handshake succeeded NULL
- MG_EV_READ, // Data received from socket long *bytes_read
- MG_EV_WRITE, // Data written to socket long *bytes_written
- MG_EV_CLOSE, // Connection closed NULL
- MG_EV_HTTP_MSG, // HTTP request/response struct mg_http_message *
- MG_EV_HTTP_CHUNK, // HTTP chunk (partial msg) struct mg_http_message *
- MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message *
- MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message *
- MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message *
- MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message *
- MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message *
- MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code
- MG_EV_SNTP_TIME, // SNTP time received uint64_t *milliseconds
- MG_EV_USER, // Starting ID for user events
-};
-```
-
-## Connection flags
-
-`struct mg_connection` has a bitfield with connection flags. Flags are binary:
-they can be either 0 or 1. Some flags are set by Mongoose and must be not
-changed by an application code. For example, the `is_udp` flag tells the application if
-that connection is UDP or not. Some flags can be changed by application, for
-example, the `is_draining` flag, if set by an application, tells Mongoose to send
-the remaining data to a peer, and when everything is sent, close the connection.
-
- NOTE: User-changeable flags are:
-`is_hexdumping`, `is_draining`, `is_closing`.
-
-This is taken from `mongoose.h` as-is:
-
-```c
-struct mg_connection {
- ...
- unsigned is_listening : 1; // Listening connection
- unsigned is_client : 1; // Outbound (client) connection
- unsigned is_accepted : 1; // Accepted (server) connection
- unsigned is_resolving : 1; // Non-blocking DNS resolv is in progress
- unsigned is_connecting : 1; // Non-blocking connect is in progress
- unsigned is_tls : 1; // TLS-enabled connection
- unsigned is_tls_hs : 1; // TLS handshake is in progress
- unsigned is_udp : 1; // UDP connection
- unsigned is_websocket : 1; // WebSocket connection
- unsigned is_hexdumping : 1; // Hexdump in/out traffic
- unsigned is_draining : 1; // Send remaining data, then close and free
- unsigned is_closing : 1; // Close and free the connection immediately
- unsigned is_full : 1; // Stop reads, until cleared
- unsigned is_resp : 1; // Response is still being generated
- unsigned is_readable : 1; // Connection is ready to read
- unsigned is_writable : 1; // Connection is ready to write
-};
-```
-
-## Best practices
-
-- Debug log. To increase debug verbosity, call `mg_log_set()`:
- ```c
- mg_log_set(MG_LL_DEBUG);
- mg_mgr_init(&mgr);
- ```
- The `MG_INFO()`, `MG_DEBUG()` logging macros use `putchar()` by default,
- i.e. they use standard C `stdout` stream. That works fine on the traditional
- OS. In the embedded environment, in order to see debug output, two ways
- are possible: IO retargeting or Mongoose log redirection. IO retargeting
- can already be implemented by an embedded SDK - for example ESP32 SDK
- redirects `printf()` to the UART0. Otherwise, IO retargeting can be
- implemented manually, see
- [guide](https://github.com/cpq/bare-metal-programming-guide#redirect-printf-to-uart)
- for more details.
- The alternative way is to redirect Mongoose logs:
- ```c
- void log_fn(char ch, void *param) {
- output_a_single_character_to_UART(ch);
- }
- ...
- mg_log_set_fn(log_fn, param); // Use our custom log function
- ```
-- If you need to perform any sort of initialisation of your connection,
- do it by catching `MG_EV_OPEN` event. That event is sent immediately
- after a connection has been allocated and added to the event manager,
- but before anything else:
- ```c
- static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
- if (ev == MG_EV_OPEN) {
- ... // Do your initialisation
- }
- ```
-- If you need to keep some connection-specific data, you have two options.
- First, use `c->fn_data` pointer. That pointer is passed to the event handler
- as last parameter:
- ```c
- static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
- if (ev == MG_EV_OPEN) {
- c->fn_data = malloc(123); // Change our fn_data
- } else if (ev == MG_EV_CLOSE) {
- free(fn_data); // Don't forget to free!
- }
- ...
- }
-
- // Every accepted connection inherit NULL pointer as c->fn_data, but we change
- // it per-connection to something else
- mg_http_listen(&mgr, "http://localhost:1234", fn, NULL);
- ```
- Another option is to use the `c->data` buffer, which can
- hold some amount of connection-specific data without extra memory allocation:
- ```c
- static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
- if (ev == MG_EV_WS_OPEN) {
- c->data[0] = 'W'; // Established websocket connection, store something
- ...
- ```
-- If you need to close the connection, set `c->is_draining = 1;` in your
- event handler function. That tells the event manager to send all remaining
- data in a send buffer ("drain" the connection), then close the connection.
- If you need to close
- the connection immediately without draining, use `c->is_closing = 1;`
-- Use `mg_http_reply()` function to create HTTP responses. That function
- properly sets the `Content-Length` header, which is important. Of course
- you can create responses manually, e.g. with `mg_printf()` function,
- but be sure to set the `Content-Length` header:
- ```c
- mg_printf(c, "HTTP/1.1 200 OK\r\Content-Length: %d\r\n\r\n%s", 2, "hi");
- ```
- Alternatively, use chunked transfer enconding:
- ```c
- mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
- mg_http_printf_chunk(c, "%s", "foo");
- mg_http_printf_chunk(c, "%s", "bar");
- mg_http_printf_chunk(c, ""); // Don't forget the last empty chunk
- ```
- NOTE: if you are not using
- `mg_http_reply()` or `mg_http_*_chunk()`, make sure to set
- `c->is_resp = 0;` when your event handler finished writing response.
-- On embedded environment, make sure that serving task has enough stack:
- give it 2k for simple RESTful serving, or 4-8k for complex dynamic/static
- serving. In certain environments, it is necessary to adjust heap size, too.
- By default, IO buffer allocation size `MG_IO_SIZE` is 2048: change it to 512
- to trim run-time per-connection memory consumption.
-
-## Architecture diagram
-
-In the Operating System environment, Mongoose uses BSD sockets API provided
-by the OS's TCP/IP stack:
-
-
-
-In the embedded bare metal environment, Mongoose can utilise its own built-in
-stack with network drivers - i.e. it can run directly on top of the hardware:
-
-
-
-## Build options
-
-Mongoose source code ships in two files:
-- [mongoose.h](https://github.com/cesanta/mongoose/blob/master/mongoose.h) - API definitions
-- [mongoose.c](https://github.com/cesanta/mongoose/blob/master/mongoose.c) - Implementation
-
-Therefore, to integrate Mongoose into an application, simply copy these two
-files to the application's source tree. The `mongoose.c` and `mongoose.h` files
-are actually an amalgamation - non-amalgamated sources can be found at
-https://github.com/cesanta/mongoose/tree/master/src
-
-Mongoose has 3 types of build constants (preprocessor definitions) that affect
-the build: a target architecture/OS, target network stack, and tunables. In
-order to set the option during build time, use the `-D OPTION` compiler flag:
-
-```sh
-$ cc app.c mongoose.c # Use defaults!
-$ cc app.c mongoose.c -D MG_ENABLE_IPV6=1 # Build with IPv6 enabled
-$ cc app.c mongoose.c -D MG_ARCH=MG_ARCH_RTX # Set architecture
-$ cc app.c mongoose.c -D MG_ENABLE_SSI=0 -D MG_IO_SIZE=8192 # Multiple options
-```
-
-The list of supported architectures is defined in the
-[arch.h](https://github.com/cesanta/mongoose/blob/master/src/arch.h) header
-file. Normally, there is no need to explicitly specify the architecture. The
-architecture is guessed during the build, so setting it is not usually
-required.
-
-| Name | Description |
-| ---- | ----------- |
-|MG_ARCH_UNIX | All UNIX-like systems like Linux, MacOS, FreeBSD, etc |
-|MG_ARCH_WIN32 | Windows systems |
-|MG_ARCH_ESP32 | Espressif's ESP32 |
-|MG_ARCH_ESP8266 | Espressif's ESP8266 |
-|MG_ARCH_FREERTOS | All systems with FreeRTOS kernel|
-|MG_ARCH_AZURERTOS | Microsoft Azure RTOS |
-|MG_ARCH_RTX | Keil RTX |
-|MG_ARCH_ZEPHYR | Zephyr RTOS |
-|MG_ARCH_TIRTOS | TI RTOS |
-|MG_ARCH_RP2040 | RP2040 SDK |
-|MG_ARCH_NEWLIB | Bare ARM GCC |
-|MG_ARCH_CUSTOM | A custom architecture, discussed in the next section |
-
-The network stack constants are listed below. Note that if a network stack
-is not specified, then it is assumed that the target architecture supports
-standard BSD socket API.
-
-| Name | Default | Description |
-| ---- | -------- | ----------- |
-|MG_ENABLE_LWIP | 0 | lwIP network stack |
-|MG_ENABLE_FREERTOS_TCP | 0 | Amazon FreeRTOS-Plus-TCP network stack |
-|MG_ENABLE_RL | 0 | Keil MDK network stack |
-|MG_ENABLE_TCPIP | 0 | Built-in Mongoose network stack |
-
-The other class of build constants is defined in
-[src/config.h](https://github.com/cesanta/mongoose/blob/master/src/config.h)
-together with their default values. These are tunables that include/exclude
-a certain functionality or change relevant parameters.
-
-Here is a list of build constants and their default values:
-
-| Name | Default | Description |
-| ---- | ------- | ----------- |
-|MG_ENABLE_SOCKET | 1 | Use BSD socket low-level API |
-|MG_ENABLE_MBEDTLS | 0 | Enable mbedTLS library |
-|MG_ENABLE_OPENSSL | 0 | Enable OpenSSL library |
-|MG_ENABLE_IPV6 | 0 | Enable IPv6 |
-|MG_ENABLE_MD5 | 0 | Use native MD5 implementation |
-|MG_ENABLE_SSI | 1 | Enable serving SSI files by `mg_http_serve_dir()` |
-|MG_ENABLE_CUSTOM_RANDOM | 0 | Provide custom RNG function `mg_random()` |
-|MG_ENABLE_CUSTOM_TLS | 0 | Enable custom TLS library |
-|MG_ENABLE_CUSTOM_MILLIS | 0 | Enable custom `mg_millis()` function |
-|MG_ENABLE_PACKED_FS | 0 | Enable embedded FS support |
-|MG_ENABLE_FATFS | 0 | Enable embedded FAT FS support |
-|MG_ENABLE_LINES | undefined | If defined, show source file names in logs |
-|MG_IO_SIZE | 2048 | Granularity of the send/recv IO buffer growth |
-|MG_MAX_RECV_SIZE | (3 * 1024 * 1024) | Maximum recv buffer size |
-|MG_MAX_HTTP_HEADERS | 40 | Maximum number of HTTP headers |
-|MG_HTTP_INDEX | "index.html" | Index file for HTML directory |
-|MG_FATFS_ROOT | "/" | FAT FS root directory |
-
-NOTE: the `MG_IO_SIZE` constant also sets
-maximum UDP message size, see
-[issues/907](https://github.com/cesanta/mongoose/issues/907) for details. If the
-application uses large UDP messages, increase the `MG_IO_SIZE` limit
-accordingly.
-
-## Custom build
-
-A custom build should be used for cases not covered by the
-existing architecture options (e.g., an embedded architecture that
-uses some proprietary RTOS and network stack). In order to build on such
-systems, follow the outline below:
-
-1. Add `-DMG_ARCH=MG_ARCH_CUSTOM` to your build flags.
-2. Create a file called `mongoose_custom.h`, with defines and includes that
- are relevant to your platform. Mongoose uses `bool` type, `MG_DIRSEP` define,
- and optionally other structures like `DIR *` depending on the functionality
- you have enabled - see previous section. Below is an example:
- ```c
- #include
- #include
-
- #define MG_DIRSEP '/'
- #define MG_INT64_FMT "%lld"
- ```
- You can also add a `MG_ARCH` definition:
- ```c
- #define MG_ARCH MG_ARCH_CUSTOM
- ```
-3. This step is optional, and only required if you intend to use a custom
- TCP/IP stack. To do that, you should:
- * Disable BSD socket API: in the `mongoose_custom.h`, add
- ```c
- #define MG_ENABLE_SOCKET 0
- ```
- * Add an implementation of several internal API functions, like
- `mg_send()`, `mg_mgr_poll()`, etc. For the reference, take
- a look at the stub
- "do nothing" implementation at
- [test/mongoose_custom.c](https://github.com/cesanta/mongoose/blob/master/test/mongoose_custom.c)
- and the experimental builtin bare metal TCP/IP stack implementation
- at
- [src/tcpip/](https://github.com/cesanta/mongoose/blob/master/src/tcpip/)
-
-## Minimal HTTP server
-
-This example is a simple HTTP server that serves both static and dynamic content:
-
-```c
-#include "mongoose.h"
-
-static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
- if (ev == MG_EV_HTTP_MSG) {
- struct mg_http_message *hm = (struct mg_http_message *) ev_data;
- if (mg_http_match_uri(hm, "/api/hello")) { // On /api/hello requests,
- mg_http_reply(c, 200, "", "{%Q:%d}\n", "status", 1); // Send dynamic JSON response
- } else { // For all other URIs,
- struct mg_http_serve_opts opts = {.root_dir = "."}; // Serve files
- mg_http_serve_dir(c, hm, &opts); // From root_dir
- }
- }
-}
-
-int main(int argc, char *argv[]) {
- struct mg_mgr mgr;
- mg_mgr_init(&mgr); // Init manager
- mg_http_listen(&mgr, "http://0.0.0.0:8000", fn, &mgr); // Setup listener
- for (;;) mg_mgr_poll(&mgr, 1000); // Event loop
- mg_mgr_free(&mgr); // Cleanup
- return 0;
-}
-```
-
-## Minimal TCP echo server
-
-This example is a simple TCP echo server that listens on port 1234:
-
-```c
-#include "mongoose.h"
-
-static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
- if (ev == MG_EV_READ) {
- mg_send(c, c->recv.buf, c->recv.len); // Echo received data back
- mg_iobuf_del(&c->recv, 0, c->recv.len); // And discard it
- }
-}
-
-int main(int argc, char *argv[]) {
- struct mg_mgr mgr;
- mg_mgr_init(&mgr); // Init manager
- mg_listen(&mgr, "tcp://0.0.0.0:1234", cb, &mgr); // Setup listener
- for (;;) mg_mgr_poll(&mgr, 1000); // Event loop
- mg_mgr_free(&mgr); // Cleanup
- return 0;
-}
-```
-
-
-# API Reference
-
-## Core
-
-### struct mg\_addr
-
-```c
-struct mg_addr {
- uint16_t port; // TCP or UDP port in network byte order
- uint32_t ip; // IP address in network byte order
- uint8_t ip6[16]; // IPv6 address
- bool is_ip6; // True when address is IPv6 address
-};
-```
-
-This structure contains network address; it can be considered as a Mongoose equivalent for sockets `sockaddr` structure.
-
-### struct mg\_mgr
-
-```c
-struct mg_mgr {
- struct mg_connection *conns; // List of active connections
- struct mg_dns dns4; // DNS for IPv4
- struct mg_dns dns6; // DNS for IPv6
- int dnstimeout; // DNS resolve timeout in milliseconds
- unsigned long nextid; // Next connection ID
- void *userdata; // Arbitrary user data pointer
-};
-```
-Event management structure that holds a list of active connections, together
-with some housekeeping information.
-
-
-### struct mg\_connection
-
-```c
-struct mg_connection {
- struct mg_connection *next; // Linkage in struct mg_mgr :: connections
- struct mg_mgr *mgr; // Our container
- struct mg_addr loc; // Local address
- struct mg_addr rem; // Remote address
- void *fd; // Connected socket, or LWIP data
- unsigned long id; // Auto-incrementing unique connection ID
- struct mg_iobuf recv; // Incoming data
- struct mg_iobuf send; // Outgoing data
- mg_event_handler_t fn; // User-specified event handler function
- void *fn_data; // User-specified function parameter
- mg_event_handler_t pfn; // Protocol-specific handler function
- void *pfn_data; // Protocol-specific function parameter
- char data[MG_DATA_SIZE]; // Arbitrary connection data, MG_DATA_SIZE defaults to 32 bytes
- void *tls; // TLS specific data
- unsigned is_listening : 1; // Listening connection
- unsigned is_client : 1; // Outbound (client) connection
- unsigned is_accepted : 1; // Accepted (server) connection
- unsigned is_resolving : 1; // Non-blocking DNS resolve is in progress
- unsigned is_connecting : 1; // Non-blocking connect is in progress
- unsigned is_tls : 1; // TLS-enabled connection
- unsigned is_tls_hs : 1; // TLS handshake is in progress
- unsigned is_udp : 1; // UDP connection
- unsigned is_websocket : 1; // WebSocket connection
- unsigned is_hexdumping : 1; // Hexdump in/out traffic
- unsigned is_draining : 1; // Send remaining data, then close and free
- unsigned is_closing : 1; // Close and free the connection immediately
- unsigned is_full : 1; // Stop reads, until cleared
- unsigned is_resp : 1; // Response is still being generated
- unsigned is_readable : 1; // Connection is ready to read
- unsigned is_writable : 1; // Connection is ready to write
-};
-```
-
-A connection - either a listening connection, or an accepted connection,
-or an outbound connection.
-
-### mg\_mgr\_init()
-
-```c
-void mg_mgr_init(struct mg_mgr *mgr);
-```
-
-Initialize event manager structure:
-- Set a list of active connections to NULL
-- Set default DNS servers for IPv4 and IPv6
-- Set default DNS lookup timeout
-
-Parameters:
-- `mgr` - a pointer to `mg_mgr` structure that needs to be initialized
-
-Return value: none
-
-Usage example:
-
-```c
-struct mg_mgr mgr;
-mg_mgr_init(&mgr);
-```
-
-
-
-### mg\_mgr\_poll()
-
-```c
-void mg_mgr_poll(struct mg_mgr *mgr, int ms);
-```
-
-Perform a single poll iteration. For each connection in the `mgr->conns` list:
-- See if there is incoming data. If there is, read it into the `c->recv` buffer, send `MG_EV_READ` event
-- See if there is data in the `c->send` buffer, and write it, send `MG_EV_WRITE` event
-- If a connection is listening, accept an incoming connection if any, and send `MG_EV_ACCEPT` event to it
-- Send `MG_EV_POLL` event
-
-Parameters:
-- `mgr` - an event manager to use
-- `ms` - a timeout in milliseconds
-
-Return value: none
-
-Usage example:
-
-```c
-while (running == true) mg_mgr_poll(&mgr, 1000 /* 1 sec */);
-```
-
-### mg\_mgr\_free()
-
-```c
-void mg_mgr_free(struct mg_mgr *mgr);
-```
-
-Close all connections, and free all resources.
-
-Parameters:
-- `mgr` - an event manager to cleanup
-
-Return value: none
-
-Usage example:
-
-```c
-struct mg_mgr mgr;
-mg_mgr_init(&mgr);
-while (running == true) mg_mgr_poll(&mgr, 1000); // Event loop
-mg_mgr_free(&mgr);
-```
-
-### mg\_listen()
-
-```c
-struct mg_connection *mg_listen(struct mg_mgr *mgr, const char *url,
- mg_event_handler_t fn, void *fn_data);
-```
-
-Create a listening connection, append this connection to `mgr->conns`.
-
-Parameters:
-- `mgr` - an event manager to use
-- `url` - a URL. Specifies local IP address and port to listen on, e.g.
- `tcp://127.0.0.1:1234` or `udp://0.0.0.0:9000`
-- `fn` - an event handler function
-- `fn_data` - an arbitrary pointer, which will be passed as `fn_data` when an
- event handler is called. This pointer is also stored in a connection
- structure as `c->fn_data`
-
-Return value: created connection, or `NULL` on error.
-
-Usage example:
-
-```c
-struct mg_connection *c = mg_listen(&mgr, "tcp://127.0.0.1:8080", fn, NULL);
-```
-
-### mg\_connect()
-
-```c
-struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *url,
- mg_event_handler_t fn, void *fn_data);
-```
-
-Create an outbound connection, append this connection to `mgr->conns`.
-
-Parameters:
-- `mgr` - An event manager to use
-- `url` - A URL, specifies remote IP address/port to connect to, e.g. `http://a.com`
-- `fn` - An event handler function
-- `fn_data` - An arbitrary pointer, which will be passed as `fn_data` when an
- event handler is called. This pointer is also stored in a connection
- structure as `c->fn_data`
-
-Return value: created connection, or `NULL` on error.
-
-Note: This function does not connect to peer, it allocates required resources and
-starts connect process. Once peer is really connected, `MG_EV_CONNECT` event is sent
-to connection event handler.
-
-Usage example:
-
-```c
-struct mg_connection *c = mg_connect(&mgr, "http://example.org", fn, NULL);
-```
-
-### mg\_send()
-
-```c
-int mg_send(struct mg_connection *c, const void *data, size_t size);
-```
-
-Append `data` of size `size` to the `c->send` buffer. Return number of bytes
-appended.
-
-Note: This function does not push data to the network. It only appends data to
-the output buffer. The data is being sent when `mg_mgr_poll()` is called. If
-`mg_send()` is called multiple times, the output buffer grows.
-
-Parameters:
-- `c` - A connection pointer
-- `data` - A pointer to data to append to the send buffer
-- `size` - A data size
-
-Return value: `true` if data appended successfully and `false` otherwise
-
-Usage example:
-
-```c
-mg_send(c, "hi", 2); // Append string "hi" to the output buffer
-```
-
-### mg\_printf(), mg\_vprintf()
-
-```c
-int mg_printf(struct mg_connection *, const char *fmt, ...);
-int mg_vprintf(struct mg_connection *, const char *fmt, va_list *ap);
-```
-
-Same as `mg_send()`, but formats data using `printf()` semantics. Return
-number of bytes appended to the output buffer.
-
-NOTE: See [mg\_snprintf](#mg_snprintf-mg_vsnprintf)
-for the list of supported format specifiers
-
-Parameters:
-- `c` - a connection pointer
-- `fmt` - a format string in `printf` semantics
-
-Return value: number of bytes appended to the output buffer.
-
-Usage example:
-
-```c
-mg_printf(c, "Hello, %s!", "world"); // Add "Hello, world!" to output buffer
-```
-
-
-### mg\_wrapfd()
-
-```c
-struct mg_connection *mg_wrapfd(struct mg_mgr *mgr, int fd,
- mg_event_handler_t fn, void *fn_data);
-```
-
-Wrap a given file descriptor `fd` into a connection, and add that connection
-to the event manager. An `fd` descriptor must support `send()`, `recv()`,
-`select()` syscalls, and be non-blocking. Mongoose will treat it as a TCP
-socket. The `c->rem` and `c->loc` addresses will be empty.
-
-Parameters:
-- `fd` - A file descriptor to wrap
-- `mgr` - An event manager
-- `fn` - A pointer to event handler function
-- `ud` - A user data pointer. It will be passed to `fn` as `fn_data` parameter
-
-Return value: Pointer to the created connection or `NULL` in case of error
-
-### mg\_mkpipe()
-
-```c
-int mg_mkpipe(struct mg_mgr *mgr, mg_event_handler_t fn, void *fn_data, bool udp);
-```
-
-Create two interconnected sockets for inter-thread communication. One socket
-is wrapped into a Mongoose connection and is added to the event manager.
-Another socket is returned, and supposed to be passed to a worker thread.
-When a worker thread `send()`s to socket any data, that wakes up `mgr` and
-`fn` event handler reveives `MG_EV_READ` event. Also, `fn` can send any
-data to a worker thread, which can be `recv()`ed by a worker thread.
-If a socketpair is UDP, then it is guaranteed to send a
-
-Parameters:
-- `mgr` - an event manager
-- `fn` - a pointer to event handler function
-- `fn_data` - a user data pointer. It will be passed to `fn` as `fn_data` parameter
-- `udp` - tells to create UDP or TCP socketpair.
-
-Return value: created socket, or `-1` on error
-
-Usage example: see [examples/multi-threaded](https://github.com/cesanta/mongoose/tree/master/examples/multi-threaded).
-
-### mg\_hello()
-
-```c
-void mg_hello(const char *url);
-```
-
-A convenience function that starts a simple web server on a given listening
-URL. This function does not return until a "/quit" request is received. A
-server handles the following URIs:
-
-- `/quit` - quit the server, and exit the function
-- `/debug` - set debug level, expect `{"level": 3}` as a POST payload
-- For all other URIs, `hi` is returned as a response
-
-Parameters:
-- `url` - a listening URL, for example `http://0.0.0.0:8000`
-
-
-## HTTP
-
-### struct mg\_http\_header
-
-```c
-struct mg_http_header {
- struct mg_str name; // Header name
- struct mg_str value; // Header value
-};
-```
-
-Structure represents HTTP header, like `Content-Type: text/html`.
-`Content-Type` is a header name and `text/html` is a header value.
-
-### struct mg\_http\_message
-
-```c
-struct mg_http_message {
- struct mg_str method, uri, query, proto; // Request/response line
- struct mg_http_header headers[MG_MAX_HTTP_HEADERS]; // Headers
- struct mg_str body; // Body
- struct mg_str message; // Request line + headers + body
-};
-```
-
-Structure represents the HTTP message.
-
-
-
-### mg\_http\_listen()
-
-```c
-struct mg_connection *mg_http_listen(struct mg_mgr *mgr, const char *url,
- mg_event_handler_t fn, void *fn_data);
-```
-
-Create HTTP listener.
-
-Parameters:
-- `mgr` - An event manager
-- `url` - A URL, specifies local IP address and port to listen on, e.g. `http://0.0.0.0:8000`
-- `fn` - An event handler function
-- `fn_data` - An arbitrary pointer, which will be passed as `fn_data` when an
- event handler is called. This pointer is also stored in a connection
- structure as `c->fn_data`
-
-Return value: Pointer to created connection or `NULL` in case of error
-
-Usage example:
-
-```c
-struct mg_connection *c = mg_http_listen(&mgr, "0.0.0.0:8000", fn, arg);
-if (c == NULL) fatal_error("Cannot create listener");
-```
-
-### mg\_http\_connect()
-
-```c
-struct mg_connection *mg_http_connect(struct mg_mgr *, const char *url,
- mg_event_handler_t fn, void *fn_data);
-```
-
-Create HTTP client connection.
-
-Note: This function does not connect to peer; it allocates required resources and
-starts connect process. Once peer is really connected `MG_EV_CONNECT` event is
-sent to connection event handler.
-
-Parameters:
-- `mgr` - An event manager
-- `url` - A URL, specifies remote URL, e.g. `http://google.com`
-- `fn` - An event handler function
-- `fn_data` - An arbitrary pointer, which will be passed as `fn_data` when an
- event handler is called. This pointer is also stored in a connection
- structure as `c->fn_data`
-
-Return value: Pointer to created connection or `NULL` in case of error
-
-Usage example:
-
-```c
-struct mg_connection *c = mg_http_connect(&mgr, "http://google.com", fn, NULL);
-if (c == NULL) fatal_error("Cannot create connection");
-```
-
-### mg\_http\_status()
-
-```c
-int mg_http_status(const struct mg_http_message *hm);
-```
-
-Get status code of the HTTP response.
-Parameters:
-- `hm` - Parsed HTTP response
-
-Return value: status code, e.g. `200` for success.
-
-### mg\_http\_get\_request\_len()
-
-```c
-int mg_http_get_request_len(const unsigned char *buf, size_t buf_len);
-```
-
-Get length of request.
-
-The length of request is a number of bytes till the end of HTTP headers. It does
-not include length of HTTP body.
-
-Parameters:
-- `buf` - A pointer to a buffer with request
-- `buf_len` - Buffer length
-
-Return value: -1 on error, 0 if a message is incomplete, or the length of request
-
-Usage example:
-
-```c
-const char *buf = "GET /test \n\nGET /foo\n\n";
-int req_len = mg_http_get_request_len(buf, strlen(buf)); // req_len == 12
-```
-
-
-
-### mg\_http\_parse()
-
-```c
-int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm);
-```
-
-Parse string request into `mg_http_message` structure
-
-Parameters:
-- `s` - A request string
-- `len` - A request string length
-- `hm` - A pointer to a structure to store parsed request
-
-Return value: request length (see `mg_http_get_request_len()`)
-
-Usage example:
-
-```c
-struct mg_http_message hm;
-const char *buf = "GET / HTTP/1.0\n\n";
-if (mg_http_parse(buf, strlen(buf), &hm) > 0) { /* success */ }
-```
-
-### mg\_http\_printf\_chunk()
-
-```c
-void mg_http_printf_chunk(struct mg_connection *c, const char *fmt, ...);
-```
-
-Write a chunk of data in chunked encoding format, using `printf()` semantic.
-
-Parameters:
-- `c` - A connection pointer
-- `fmt` - A string, format specified in `printf` semantics
-
-Return value: None
-
-Usage example:
-
-```c
-mg_http_printf_chunk(c, "Hello, %s!", "world");
-```
-
-### mg\_http\_write\_chunk()
-
-```c
-void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len);
-```
-
-Write a chunk of data in chunked encoding format.
-
-Parameters:
-- `c` - A connection pointer
-- `buf` - Data to write
-- `len` - Data length
-
-Return value: None
-
-Usage example:
-
-```c
-mg_http_write_chunk(c, "hi", 2);
-```
-
-### mg\_http\_delete\_chunk()
-
-```c
-void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm);
-```
-
-Remove chunk specified from input buffer.
-
-Parameters:
-- `c` - A connection pointer
-- `hm` - Chunk to delete
-
-Return value: None
-
-Usage example:
-
-```c
-// Mongoose events handler
-void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
- if (ev == MG_EV_HTTP_CHUNK) {
- struct mg_http_message *hm = (struct mg_http_message *) ev_data;
- mg_http_delete_chunk(c, hm); // Remove received chunk
- }
-}
-```
-
-### struct mg\_http\_serve\_opts
-
-```c
-struct mg_http_serve_opts {
- const char *root_dir; // Web root directory, must be non-NULL
- const char *ssi_pattern; // SSI file name pattern, e.g. #.shtml
- const char *extra_headers; // Extra HTTP headers to add in responses
- const char *mime_types; // Extra mime types, ext1=type1,ext2=type2,..
- const char *page404; // Path to the 404 page, or NULL by default
- struct mg_fs *fs; // Filesystem implementation. Use NULL for POSIX
-};
-```
-
-A structure passed to `mg_http_serve_dir()` and `mg_http_serve_file()`, which
-drives the behavior of those two functions.
-
-### mg\_http\_serve\_dir()
-
-```c
-void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
- const struct mg_http_serve_opts *opts);
-```
-
-Serve static files according to the given options. Files can also be gzip compressed, including the directory index. All compressed files must end in `.gz` and there must not exist a file with the same name without the extension, otherwise it will take precedence; see [mg_http_serve_file()](#mg_http_serve_file)
-
-NOTE: In order to enable SSI, you need to set the `-DMG_ENABLE_SSI=1` build flag.
-
-NOTE: Avoid double dots `..` in
-the `root_dir`. If you need to
-reference an upper-level directory, use an absolute path.
-
-Parameters:
-- `c` - Connection to use
-- `hm` - HTTP message, that should be served
-- `opts` - Serve options. Note that `opts.root_dir` can optionally accept
- extra comma-separated `uri=path` pairs, see example below
-
-Return value: None
-
-Usage example:
-
-```c
-// Mongoose events handler
-void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
- if (ev == MG_EV_HTTP_MSG) {
- struct mg_http_message *hm = (struct mg_http_message *) ev_data;
- struct mg_http_serve_opts opts;
- memset(&opts, 0, sizeof(opts));
- opts.root_dir = "/var/www,/conf=/etc"; // Serve /var/www. URIs starting with /conf are served from /etc
- mg_http_serve_dir(c, hm, &opts);
- }
-}
-```
-
-### mg\_http\_serve\_file()
-
-```c
-void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
- const char *path, struct mg_http_serve_opts *opts);
-```
-
-Serve a static file. If a file with the filename specified in `path` does not exist, Mongoose tries appending `.gz`; and if such a file exists, it will serve it with a `Content-Encoding: gzip` header
-
-NOTE: `opts->root_dir` settings
-is ignored by this function.
-
-NOTE: `opts->extra_headers`
-must end with `\r\n`.
-
-Parameters:
-- `c` - Connection to use
-- `hm` - HTTP message to serve
-- `path` - Path to file to serve
-- `opts` - Serve options
-
-Return value: None
-
-Usage example:
-
-```c
-// Mongoose events handler
-void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
- if (ev == MG_EV_HTTP_MSG) {
- struct mg_http_message *hm = (struct mg_http_message *) ev_data;
- struct mg_http_serve_opts opts = {
- .mime_types = "png=image/png",
- .extra_headers = "AA: bb\r\nCC: dd\r\n"
- };
- mg_http_serve_file(c, hm, "a.png", &opts);
- }
-}
-```
-
-### mg\_http\_reply()
-
-```c
-void mg_http_reply(struct mg_connection *c, int status_code,
- const char *headers, const char *body_fmt, ...);
-```
-
-Send simple HTTP response using `printf()` semantic. This function formats
-response body according to a `body_fmt`, and automatically appends a correct
-`Content-Length` header. Extra headers could be passed via `headers` parameter.
-
-Parameters:
-- `c` - Connection to use
-- `status_code` - An HTTP response code
-- `headers` - Extra headers, default NULL. If not NULL, must end with `\r\n`
-- `fmt` - A format string for the HTTP body, in a printf semantics
-
-Return value: None
-
-
-
-Usage examples:
-
-Send a simple JSON response:
-```c
-mg_http_reply(c, 200, "Content-Type: application/json\r\n", "{\"result\": %d}", 123);
-```
-
-Send JSON response:
-```c
-char *json = mg_mprintf("{%Q:%d}", "name", 123);
-mg_http_reply(c, 200, "Content-Type: application/json\r\n", "%s\n", json);
-free(json);
-```
-
-Send a 302 redirect:
-```c
-mg_http_reply(c, 302, "Location: /\r\n", "");
-```
-
-Send error:
-```c
-mg_http_reply(c, 403, "", "%s", "Not Authorized\n");
-```
-
-### mg\_http\_get\_header()
-
-```c
-struct mg_str *mg_http_get_header(struct mg_http_message *hm, const char *name);
-```
-
-Get HTTP header value
-
-Parameters:
-- `hm` - HTTP message to look for header
-- `name` - Header name
-
-Return value: HTTP header value or `NULL` if not found
-
-Usage example:
-
-```c
-// Mongoose event handler
-void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
- if (ev == MG_EV_HTTP_MSG) {
- struct mg_http_message *hm = (struct mg_http_message *) ev_data;
- struct mg_str *s = mg_http_get_header(hm, "X-Extra-Header");
- if (s != NULL) {
- mg_http_reply(c, 200, "", "Holly molly! Header value: %.*s", (int) s->len, s->ptr);
- } else {
- mg_http_reply(c, 200, "", "Oh no, header is not set...");
- }
- }
-}
-```
-
-### mg\_http\_get\_header\_var()
-
-```c
-struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v);
-```
-
-Parse HTTP header (e.g. Cookie header) which has form
-`name1=value1; name2=value2; ...` and fetch a given variable.
-
-Parameters:
-- `s` - HTTP header
-- `name` - variable name name
-
-Return value: a requested variable, or an empty string.
-
-Usage example:
-
-```c
-struct mg_str *cookie = mg_http_get_header(hm, "Cookie");
-struct mg_str token = mg_str("");
-
-if (cookie != NULL) {
- token = mg_http_get_header_var(*cookie, mg_str("access_token"));
-}
-```
-
-### mg\_http\_var()
-
-```c
-struct mg_str mg_http_var(struct mg_str buf, struct mg_str name);
-```
-
-Fetch an undecoded HTTP variable. Parameters:
-- `buf` - a url-encoded string: HTTP request body or query string
-- `name` - a variable name to fetch
-
-Return value: variable's value. If not found, it it a NULL string.
-
-
-```c
-// We have received a request to /my/uri?a=b&c=d%20
-// The hm->query points to "a=b&c=d%20"
-struct mg_str v = mg_http_var(hm->query, mg_str("c")); // v = "d%20"
-```
-
-
-### mg\_http\_get\_var()
-
-```c
-int mg_http_get_var(const struct mg_str *var, const char *name, char *buf, int len);
-```
-
-Fetch and decode an HTTP variable
-
-Parameters:
-- `var` - HTTP request body
-- `name` - Variable name
-- `buf` - Buffer to write decoded variable
-- `len` - Buffer size
-
-Return value: Length of decoded variable. A zero or negative value means error
-
-Usage example:
-
-```c
-char buf[100] = "";
-mg_http_get_var(&hm->body, "key1", buf, sizeof(buf)) {
- ...
-}
-```
-
-### mg\_http\_creds()
-
-```c
-void mg_http_creds(struct mg_http_message *hm, char *user, size_t userlen,
- char *pass, size_t passlen);
-```
-
-Fetch authentication credential from the request, and store into the
-`user`, `userlen` and `pass`, `passlen` buffers. The credentials are looked
-up in the following order:
-- from the `Authorization` HTTP header,
- - Basic auth fills both user and pass
- - Bearer auth fills only pass
-- from the `access_token` cookie, fills pass
-- from the `?access_token=...` query string parameter, fills pass
-
-If none is found, then both user and pass are set to empty nul-terminated strings.
-
-Parameters:
-- `hm` - HTTP message to look for credentials
-- `user` - buffer to receive user name
-- `userlen` - size of `user` buffer
-- `pass` - buffer to receive password
-- `passlen` - size of `pass` buffer
-
-Return value: None
-
-Usage example:
-
-```c
-// Mongoose events handler
-void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
- if (ev == MG_EV_HTTP_MSG) {
- struct mg_http_message *hm = (struct mg_http_message *) ev_data;
- char user[100], pass[100];
- mg_http_creds(hm, user, sizeof(user), pass, sizeof(pass)); // "user" is now user name and "pass" is now password from request
- }
-}
-```
-
-### mg\_http\_match\_uri()
-
-```c
-bool mg_http_match_uri(const struct mg_http_message *hm, const char *glob);
-```
-
-Check if HTTP request matches a given glob pattern.
-
-Parameters:
-- `hm` - HTTP message to match
-- `glob` - Pattern
-
-Return value: True if HTTP request matches a given glob pattern; false otherwise
-
-Usage example:
-
-```c
-// Mongoose events handler
-void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
- if (ev == MG_EV_HTTP_MSG) {
- struct mg_http_message *hm = (struct mg_http_message *) ev_data;
- if (mg_http_match_uri(hm, "/secret")) {
- mg_http_reply(c, 200, NULL, "Very big secret!");
- } else {
- mg_http_reply(c, 200, NULL, "hello world..");
- }
- }
-}
-```
-
-### mg\_http\_bauth()
-
-```c
-void mg_http_bauth(struct mg_connection *c, const char *user, const char *pass);
-```
-
-Write a Basic `Authorization` header to the output buffer.
-
-Parameters:
-- `c` - Connection to use
-- `user` - User name
-- `pass` - Password
-
-Return value: None
-
-Usage example which uses Basic auth to create Stripe subscription:
-
-```c
- mg_printf(c, "POST /v1/subscriptions HTTP/1.1\r\n"
- "Host: api.stripe.com\r\n"
- "Transfer-Encoding: chunked\r\n");
- mg_http_bauth(c, stripe_private_key, NULL); // Add Basic auth header
- mg_printf(c, "%s", "\r\n"); // End HTTP headers
-
- mg_http_printf_chunk(c, "&customer=%s", customer_id); // Set customer
- mg_http_printf_chunk(c, "&items[0][price]=%s", price); // And price
- mg_http_printf_chunk(c, ""); // End request
-```
-
-### struct mg\_http\_part
-
-```c
-// Parameter for mg_http_next_multipart
-struct mg_http_part {
- struct mg_str name; // Form field name
- struct mg_str filename; // Filename for file uploads
- struct mg_str body; // Part contents
-};
-```
-
-Structure that describes a single part of a HTTP multipart message.
-
-
-
-### mg\_http\_next\_multipart()
-
-```c
-size_t mg_http_next_multipart(struct mg_str body, size_t offset, struct mg_http_part *part);
-```
-
-Parse the multipart chunk in the `body` at a given `offset`. An initial
-`offset` should be 0. Fill up parameters in the provided `part`, which could be
-NULL. Return offset to the next chunk, or 0 if there are no more chunks.
-
-Parameters:
-- `body`- Message body
-- `offset` - Start offset
-- `part` - Pointer to `struct mg_http_part` to fill
-
-Return value: offset to the next chunk, or 0 if there are no more chunks.
-
-Usage example (or see [form upload tutorial](../tutorials/file-uploads/) ):
-
-```c
-struct mg_http_part part;
-size_t pos = 0;
-
-while ((pos = mg_http_next_multipart(body, pos, &part)) != 0) {
- // Use part
-}
-```
-
-A diagram below shows how `mg_http_next_multipart()` in action:
-
-
-
-### mg\_http\_upload()
-
-```c
-
-int mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
- struct mg_fs *fs, const char *path, size_t max_size);
-```
-
-This is a helper utility function that is used to upload large files by small
-chunks.
-
-Append HTTP POST data to a file in a specified directory. A file name and
-file offset are specified by the query string parameters:
-`POST /upload?name=firmware.bin&offset=2048 HTTP/1.1`. If the offset is 0, then the
-file is truncated. It is a client's responsibility to divide a file into a
-smaller chunks and send a sequence of POST requests that will be handled by
-this function.
-
-Parameters:
-- `c`- a connection
-- `hm` - a parsed HTTP message
-- `fs` - a filesystem where to write a file, e.g. `&mg_fs_posix`
-- `path` - a filename
-- `max_size` - a maximum allowed file size
-
-Return value: file size after write, or negative number on error
-
-Usage example:
-
-```c
-static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
- if (ev == MG_EV_HTTP_MSG) {
- struct mg_http_message *hm = (struct mg_http_message *) ev_data;
- if (mg_http_match_uri(hm, "/upload")) {
- mg_http_upload(c, hm, &mg_fs_posix, "/tmp/myfile.bin", 99999);
- } else {
- struct mg_http_serve_opts opts = {.root_dir = "."}; // Serve
- mg_http_serve_dir(c, ev_data, &opts); // static content
- }
- }
-}
-
-```
-
-
-## WebSocket
-
-### struct mg\_ws\_message
-
-```c
-struct mg_ws_message {
- struct mg_str data; // WebSocket message data
- uint8_t flags; // WebSocket message flags
-};
-```
-
-This structure represents the WebSocket message, the `flags` element corresponds to the first byte as described in [RFC 6455 section 5.2](https://www.rfc-editor.org/rfc/rfc6455#section-5.2).
-
-#### WebSocket message type:
-
-To extract the message type from an incoming message, check the four LSBs in the `flags` element of the `struct mg_ws_message`.
-
-Possible WebSocket message types:
-
-```c
-#define WEBSOCKET_OP_CONTINUE 0
-#define WEBSOCKET_OP_TEXT 1
-#define WEBSOCKET_OP_BINARY 2
-#define WEBSOCKET_OP_CLOSE 8
-#define WEBSOCKET_OP_PING 9
-#define WEBSOCKET_OP_PONG 10
-```
-
-```c
-// Mongoose events handler
-void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
- if (ev == MG_EV_WS_MSG) {
- struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
- msgtype = wm->flags & 0x0F;
- if (msgtype == WEBSOCKET_OP_BINARY) {
- // This is a binary data message
- } else if (msgtype == WEBSOCKET_OP_TEXT) {
- // This is a text data message
- }
- }
-}
-```
-
-To send a message, use the proper message type as described in [RFC 6455 section 5.6](https://www.rfc-editor.org/rfc/rfc6455#section-5.6) for data frames. when calling [mg_ws_send()](#mg_ws_send) or [mg_ws_printf()](#mg_ws_printf-mg_ws_vprintf) below
-
-### mg\_ws\_connect()
-
-```c
-struct mg_connection *mg_ws_connect(struct mg_mgr *mgr, const char *url,
- mg_event_handler_t fn, void *fn_data,
- const char *fmt, ...);
-```
-
-Create client WebSocket connection.
-
-Note: this function does not connect to peer, it allocates required resources and
- starts the connect process. Once peer is really connected, the `MG_EV_CONNECT` event is
- sent to connection event handler.
-
-Parameters:
-- `mgr` - Event manager to use
-- `url` - Specifies remote URL, e.g. `http://google.com`
-- `fn` - An event handler function
-- `fn_data` - An arbitrary pointer, which will be passed as `fn_data` when an
- event handler is called. This pointer is also stored in a connection
- structure as `c->fn_data`
-- `fmt` - printf-like format string for additional HTTP headers, or NULL
-
-Return value: Pointer to created connection or `NULL` on error
-
-Usage example:
-
-```c
-struct mg_connection *c = mg_ws_connect(&mgr, "ws://test_ws_server.com:1000",
- handler, NULL, "%s", "Sec-WebSocket-Protocol: echo\r\n");
-if(c == NULL) fatal("Cannot create connection");
-```
-
-### mg\_ws\_upgrade()
-
-```c
-void mg_ws_upgrade(struct mg_connection *c, struct mg_http_message *,
- const char *fmt, ...);
-```
-
-Upgrade given HTTP connection to WebSocket. The `fmt` is a printf-like
-format string for the extra HTTP headers returned to the client in a
-WebSocket handshake. Set `fmt` to `NULL` if no extra headers need to be passed.
-
-Parameters:
-- `c` - Connection to use
-- `hm` - HTTP message
-- `fmt` - printf-like format string for additional HTTP headers, or NULL
-
-Return value: None
-
-Usage example:
-
-```c
-// Mongoose events handler
-void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
- if (ev == MG_EV_HTTP_MSG) {
- struct mg_http_message *hm = (struct mg_http_message *) ev_data;
- mg_ws_upgrade(c, hm, NULL); // Upgrade HTTP to WS
- }
-}
-```
-
-### mg\_ws\_send()
-
-```c
-size_t mg_ws_send(struct mg_connection *c, const void *buf, size_t len, int op);
-```
-
-Send data to WebSocket peer
-
-Parameters:
-- `c` - Connection to use
-- `buf` - Data to send
-- `len` - Data size
-- `op` - WebSocket message type, see [WebSocket message type](#websocket-message-type) above
-
-Return value: sent bytes count
-
-Usage example:
-
-```c
-// Mongoose events handler
-void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
- if (ev == MG_EV_WS_OPEN) {
- struct mg_http_message *hm = (struct mg_http_message *) ev_data;
- mg_ws_send(c, "opened", 6, WEBSOCKET_OP_BINARY); // Send "opened" to web socket connection
- }
-}
-```
-
-### mg\_ws\_printf(), mg\_ws\_vprintf()
-
-```c
-size_t mg_ws_printf(struct mg_connection *, int op, const char *fmt, ...);
-size_t mg_ws_vprintf(struct mg_connection *, int op, const char *fmt, va_list *);
-```
-
-Same as `mg_ws_send()`, but formats data using `printf()` semantics.
-
-Parameters:
-- `c` - Connection to use
-- `op` - WebSocket message type, see [WebSocket message type](#websocket-message-type) above
-- `fmt` - format string in `printf` semantics. see [mg\_snprintf](#mg_snprintf-mg_vsnprintf)
-for the list of supported format specifiers
-
-Return value: sent bytes count
-
-Usage example:
-
-```c
-mg_ws_printf(c, WEBSOCKET_OP_TEXT, "Hello, %s!", "world");
-```
-
-### mg\_ws\_wrap()
-
-```c
-size_t mg_ws_wrap(struct mg_connection *c, size_t len, int op)
-```
-
-Convert data in output buffer to WebSocket format. Useful then implementing protocol over WebSocket
-See [examples/mqtt-over-ws-client](https://github.com/cesanta/mongoose/tree/master/examples/mqtt-over-ws-client) for full example.
-
-Parameters:
-- `c` - Connection to use
-- `len` - Bytes count to convert
-- `op` - Websocket message type (see `mg_ws_send`)
-
-Return value: New size of connection output buffer
-
-Usage example:
-
-```c
-size_t len = c->send.len; // Store output buffer len
-mg_mqtt_login(c, s_url, &opts); // Write MQTT login message
-mg_ws_wrap(c, c->send.len - len, WEBSOCKET_OP_BINARY); // Wrap it into WS
-```
-
-## SNTP
-
-### mg_sntp_connect()
-
-```c
-struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, const char *url,
- mg_event_handler_t fn, void *fn_data)
-```
-
-Connect SNTP server.
-
-Parameters:
-- `mgr` - Event manager to use
-- `url` - Specifies remote URL, `time.google.com` if NULL.
-- `fn` - An event handler function
-- `fn_data` - An arbitrary pointer, which will be passed as `fn_data` when an
- event handler is called. This pointer is also stored in a connection
- structure as `c->fn_data`
-
-Return value: Pointer to created connection or `NULL` on error
-
-Usage example:
-
-```c
-static void sntp_cb(struct mg_connection *c, int ev, void *evd, void *fnd) {
- if (ev == MG_EV_SNTP_TIME) {
- // Time received
- struct timeval *tv = (struct timeval *tv)evd;
- }
-}
-...
-mg_sntp_connect(mgr&, NULL /* connect to time.google.com */, sntp_cb, NULL);
-```
-
-### mg_sntp_request()
-
-```c
-void mg_sntp_request(struct mg_connection *c)
-```
-
-Send time request to SNTP server
-
-Parameters:
-- `c` - Connection to use
-
-Return value: None
-
-Usage example:
-
-```c
-mg_sntp_request(c);
-```
-
-## MQTT
-
-### struct mg\_mqtt\_opts
-
-```c
-struct mg_mqtt_opts {
- struct mg_str user; // Username, can be empty
- struct mg_str pass; // Password, can be empty
- struct mg_str client_id; // Client ID
- struct mg_str will_topic; // Will topic
- struct mg_str will_message; // Will message
- uint8_t will_qos; // Will message quality of service
- uint8_t version; // Can be 4 (3.1.1), or 5. If 0, assume 4.
- uint16_t keepalive; // Keep-alive timer in seconds
- bool will_retain; // Retain last will
- bool clean; // Use clean session, 0 or 1
-};
-```
-
-Structure used to specify MQTT connection options.
-
-### struct mg\_mqtt\_message
-
-```c
-struct mg_mqtt_message {
- struct mg_str topic; // Parsed topic
- struct mg_str data; // Parsed message
- struct mg_str dgram; // Whole MQTT datagram, including headers
- uint16_t id; // Set for PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK, PUBLISH
- uint8_t cmd; // MQTT command, one of MQTT_CMD_*
- uint8_t qos; // Quality of service
- uint8_t ack; // Connack return code. 0 - success
-};
-```
-
-Structure represents the MQTT message.
-
-### mg\_mqtt\_connect()
-
-```c
-struct mg_connection *mg_mqtt_connect(struct mg_mgr *mgr, const char *url,
- struct mg_mqtt_opts *opts,
- mg_event_handler_t fn, void *fn_data);
-```
-
-Create client MQTT connection.
-
-Note: This function does not connect to peer; it allocates required resources and
-starts connect process. Once peer is really connected `MG_EV_CONNECT` event is
-sent to connection event handler.
-
-Parameters:
-- `mgr` - Event manager to use
-- `url` - Specifies remote URL, e.g. `http://google.com`
-- `opts` - MQTT options, with client ID, qos, etc
-- `fn` - An event handler function
-- `fn_data` - An arbitrary pointer, which will be passed as `fn_data` when an
- event handler is called. This pointer is also stored in a connection
- structure as `c->fn_data`
-
-Return value: pointer to created connection or `NULL` on error
-
-Usage example:
-
-```c
-void fn(struct mg_connection *c, int ev, void *evd, void *fnd) {
- char *buf = (char *) fnd;
- if (ev == MG_EV_MQTT_OPEN) {
- // Connection ready
- }
-}
-
-mg_mqtt_connect(&mgr, "mqtt://test.org:1883", NULL, handler, NULL);
-```
-
-### mg\_mqtt\_listen()
-
-```c
-struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url,
- mg_event_handler_t fn, void *fn_data);
-```
-
-Create MQTT listener.
-
-Parameters:
-- `mgr` - Event manager to use
-- `url` - Specifies local IP address and port to listen on, e.g. `mqtt://0.0.0.0:1883`
-- `fn` - An event handler function
-- `fn_data` - An arbitrary pointer, which will be passed as `fn_data` when an
- event handler is called. This pointer is also stored in a connection
- structure as `c->fn_data`
-
-Return value: Pointer to created connection or `NULL` on error
-
-Usage example:
-
-```c
-struct mg_connection *c = mg_mqtt_listen(&mgr, "0.0.0.0:1883", fn, arg);
-if (c == NULL) fatal("Cannot create connection");
-```
-
-### mg\_mqtt\_login
-
-```c
-void mg_mqtt_login(struct mg_connection *c, const char *url,
- struct mg_mqtt_opts *opts);
-```
-
-Send MQTT login request.
-
-Parameters:
-- `c` - Connection to use
-- `url` - URL, containing user name and password to use
-- `opts` - Request options
-
-Return value: None
-
-Usage example:
-
-```c
-// Mongoose events handler
-void fn(struct mg_connection *c, int ev, void *evd, void *fnd) {
- char *buf = (char *) fnd;
- if (ev == MG_EV_MQTT_OPEN) {
- struct mg_mqtt_opts opts = {.qos = 1,
- .will_topic = mg_str("my topic"),
- .will_message = mg_str("goodbye")};
- mg_mqtt_login(c, s_url, &opts);
- }
-}
-```
-
-### mg\_mqtt\_pub()
-
-```c
-void mg_mqtt_pub(struct mg_connection *c, struct mg_str topic,
- struct mg_str data, int qos, bool retain);
-```
-
-Publish message.
-
-Parameters:
-- `c` - Connection to use
-- `topic` - Topic to publish data
-- `data` - Data to publish
-- `qos` - Required QoS
-- `retain` - Retain flag
-
-Return value: None
-
-Usage example:
-
-```c
-mg_mqtt_pub(c, mg_str("topic"), mg_str("my data"), 1, false);
-```
-
-### mg\_mqtt\_sub()
-
-```c
-void mg_mqtt_sub(struct mg_connection *c, struct mg_str topic, int qos);
-```
-
-Subscribe to topic.
-
-Parameters:
-- `c` - Connection to use
-- `topic` - Topic to subscribe
-- `qos` - Required QoS
-
-Return value: None
-
-```c
-mg_mqtt_sub(c, mg_str("my/topic"), 1);
-```
-
-### mg\_mqtt\_send_header()
-
-```c
-void mg_mqtt_send_header(struct mg_connection *c, uint8_t cmd, uint8_t flags, uint32_t len);
-```
-Send MQTT command header. Useful in MQTT server implementation. Command can be one of the following value:
-
-```c
-#define MQTT_CMD_CONNECT 1
-#define MQTT_CMD_CONNACK 2
-#define MQTT_CMD_PUBLISH 3
-#define MQTT_CMD_PUBACK 4
-#define MQTT_CMD_PUBREC 5
-#define MQTT_CMD_PUBREL 6
-#define MQTT_CMD_PUBCOMP 7
-#define MQTT_CMD_SUBSCRIBE 8
-#define MQTT_CMD_SUBACK 9
-#define MQTT_CMD_UNSUBSCRIBE 10
-#define MQTT_CMD_UNSUBACK 11
-#define MQTT_CMD_PINGREQ 12
-#define MQTT_CMD_PINGRESP 13
-#define MQTT_CMD_DISCONNECT 14
-```
-
-Parameters:
-- `c` - Connection to use
-- `cmd` - Command (see above)
-- `flags` - Command flags
-- `len` - Size of the following command
-
-Return value: None
-
-Usage example:
-
-```c
-// Mongoose events handler
-void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
- if (ev == MG_EV_MQTT_CMD) {
- struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data;
- if (mm->cmd == MQTT_CMD_CONNECT) {
- uint8_t response[] = {0, 0};
- mg_mqtt_send_header(c, MQTT_CMD_CONNACK, 0, sizeof(response)); // Send acknowledgement
- mg_send(c, response, sizeof(response));
- }
- }
-}
-```
-
-### mg\_mqtt\_ping()
-
-```c
-void mg_mqtt_ping(struct mg_connection *c);
-```
-
-Send `MQTT_CMD_PINGREQ` command via `mg_mqtt_send_header`
-
-Parameters:
-- `c` - Connection to use
-
-Return value: None
-
-Usage example:
-
-```c
-// Send periodic pings to all WS connections
-static void timer_fn(void *arg) {
- struct mg_mgr *mgr = (struct mg_mgr *) arg;
- for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) {
- if (c->is_websocket) mg_mqtt_ping(c);
- }
-}
-```
-
-### mg_mqtt_parse
-
-```c
-int mg_mqtt_parse(const uint8_t *buf, size_t len, struct mg_mqtt_message *m);
-```
-
-Parse buffer and fill `mg_mqtt_message` structure if buffer contain MQTT message.
-
-Parameters:
-- `buf` - buffer with MQTT message to parse
-- `len` - buffer size
-- `m` - pointer to `mg_mqtt_message` structure to receive parsed message
-
-Return value: `MQTT_OK` if message successfully parsed, `MQTT_INCOMPLETE` if message
-isn't fully received and `MQTT_MALFORMED` if message has wrong format.
-
-Usage example:
-
-```c
-// Iterate over all MQTT frames contained in buf, len
-struct mg_mqtt_message mm;
-while ((mg_mqtt_parse(buf, len, &mm)) == MQTT_OK) {
- switch (mm.cmd) {
- case MQTT_CMD_CONNACK:
- ...
- }
- buf += mm.dgram.len;
- len -= mm.dgram.len;
-}
-```
-
-## TLS
-
-### struct mg\_tls\_opts
-
-```c
-struct mg_tls_opts {
- const char *ca; // CA certificate file. For both listeners and clients
- const char *crl; // Certificate Revocation List. For clients
- const char *cert; // Certificate
- const char *certkey; // Certificate key
- const char *ciphers; // Cipher list
- struct mg_str srvname; // If not empty, enables server name verification
- struct mg_fs *fs; // FS API for reading certificate files
-};
-```
-
-TLS initialisation structure:
-- `ca` - Certificate Authority. Can be a filename or a string. Used to verify
- a certificate that the other end sends to us. If NULL, then certificate checking
- is disabled
-- `crl` - Certificate Revocation List. Can be a filename or a string. Used to
- verify a certificate that the other end sends to us. If NULL, then certificate
- revocation checking is disabled
-- `cert` - Our own certificate. Can be a filename, or a string. If NULL, then
- we don't authenticate with the other peer
-- `certkey` - A key for a `cert`. Sometimes, a certificate and its key are
- bundled in a single .pem file, in which case the values for `cert` and
- `certkey` could be the same
-- `ciphers` - A list of allowed ciphers
-- `srvname` - Enable server name verification
-
-
-NOTE: if both `ca` and `cert` are set, then so-called two-way TLS is enabled,
-when both sides authenticate with each other. Usually, server-side connections
-set both `ca` and `cert`, whilst client-side - only `ca`.
-
-### mg\_tls\_init()
-
-```c
-void mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts);
-```
-
-Initialise TLS on a given connection.
-
-NOTE: mbedTLS implementation uses `mg_random`
-as RNG. The `mg_random` can be overridden by setting `MG_ENABLE_CUSTOM_RANDOM`
-and defining your own `mg_random()` implementation.
-
-Parameters:
-- `c` - Connection, for which TLS should be initialized
-- `opts` - TLS initialization parameters
-
-Return value: None
-
-Usage example:
-
-```c
-struct mg_tls_opts opts = {.cert = "ca.pem"};
-mg_tls_init(c, &opts);
-```
-
-## Timer
-
-### mg\_timer\_add()
-
-```c
-struct mg_timer *mg_timer_add(struct mg_mgr *mgr,
- uint64_t period_ms, unsigned flags,
- void (*fn)(void *), void *fn_data);
-```
-
-Setup a timer. This is a high-level timer API that allows to add a software
-timer to the event manager. This function `calloc()`s a new timer and
-adds it to the `mgr->timers` list. All added timers are polled when
-`mg_mgr_poll()` is called, and called if expired.
-
-NOTE: Make sure that the timer
-interval is equal or more to the `mg_mgr_poll()` timeout.
-
-Parameters:
-- `mgr` - Pointer to `mg_mgr` event manager structure
-- `ms` - An interval in milliseconds
-- `flags` - Timer flags bitmask, `MG_TIMER_REPEAT` and `MG_TIMER_RUN_NOW`
-- `fn` - Function to invoke
-- `fn_data` - Function argument
-
-Return value: Pointer to created timer
-
-Usage example:
-```c
-void timer_fn(void *data) {
- // ...
-}
-
-mg_timer_add(mgr, 1000, MG_TIMER_REPEAT, timer_fn, NULL);
-```
-
-### struct mg\_timer
-
-```c
-struct mg_timer {
- uint64_t period_ms; // Timer period in milliseconds
- uint64_t expire; // Expiration timestamp in milliseconds
- unsigned flags; // Possible flags values below
-#define MG_TIMER_ONCE 0 // Call function once
-#define MG_TIMER_REPEAT 1 // Call function periodically
-#define MG_TIMER_RUN_NOW 2 // Call immediately when timer is set
- void (*fn)(void *); // Function to call
- void *arg; // Function argument
- struct mg_timer *next; // Linkage
-};
-```
-
-Timer structure. Describes a software timer. Timer granularity is the same
-as the `mg_mgr_poll()` timeout argument in the main event loop.
-
-### mg\_timer\_init()
-
-```c
-void mg_timer_init(struct mg_timer **head,
- struct mg_timer *t, uint64_t period_ms, unsigned flags,
- void (*fn)(void *), void *fn_data);
-```
-
-Setup a timer.
-
-Parameters:
-- `head` - Pointer to `mg_timer` list head
-- `t` - Pointer to `mg_timer` that should be initialized
-- `ms` - An interval in milliseconds
-- `flags` - Timer flags bitmask, `MG_TIMER_REPEAT` and `MG_TIMER_RUN_NOW`
-- `fn` - Function to invoke
-- `fn_data` - Function argument
-
-Return value: None
-
-Usage example:
-```c
-void timer_fn(void *data) {
- // ...
-}
-
-struct mg_timer timer, *head = NULL;
-mg_timer_init(&head, &timer, 1000, MG_TIMER_REPEAT, timer_fn, NULL);
-```
-
-### mg\_timer\_free()
-
-```c
-void mg_timer_free(struct mg_timer **head, struct mg_timer *t);
-```
-Free timer, remove it from the internal timers list.
-
-Parameters:
-- `head` - Pointer to `mg_timer` list head
-- `t` - Timer to free
-
-Return value: None
-
-Usage example:
-```c
-struct mg_timer timer;
-// ...
-mg_timer_free(&timer);
-```
-
-### mg\_timer\_poll()
-
-```c
-void mg_timer_poll(struct mg_timer **head, uint64_t uptime_ms);
-```
-
-Traverse list of timers and call them if current timestamp `uptime_ms` is
-past the timer's expiration time.
-
-Note, that `mg_mgr_poll` function internally calls `mg_timer_poll`; therefore,
-in most cases it is unnecessary to call it explicitly.
-
-Parameters:
-- `head` - Pointer to `mg_timer` list head
-- `uptime_ms` - current timestamp
-
-Return value: None
-
-Usage example:
-
-```c
-mg_timer_poll(mg_millis());
-```
-
-## Time
-
-### mg\_millis()
-
-```c
-int64_t mg_millis(void);
-```
-
-Return current uptime in milliseconds.
-
-Parameters: None
-
-Return value: Current uptime
-
-Usage example:
-
-```c
-int64_t uptime = mg_millis();
-```
-
-## String
-
-### struct mg\_str
-
-```c
-struct mg_str {
- const char *ptr; // Pointer to string data
- size_t len; // String len
-};
-```
-
-This structure represent an arbitrary chunk of memory, not necessarily
-zero-terminated. This is a "mongoose string", and it gets used extensively
-in the codebase instead of C zero-terminated strings.
-
-For example, when an HTTP request is received, Mongoose created a
-`struct mg_http_message` which has a collection of `struct mg_str` pointing
-to request method, URI, headers, and so on. This way, Mongoose avoids
-any heap allocations and does not modify the received buffer - instead, it
-uses `struct mg_str` to describe various parts of HTTP request.
-
-Same goes with many other cases.
-
-NOTE: since `ptr` is not necessarily zero-terminated, do not use libc string
-functions against it - like `strlen()` or `sscanf()`.
-
-### mg\_str()
-
-```c
-struct mg_str mg_str(const char *s)
-```
-
-Create Mongoose string from NULL-terminated C-string. This function doesn't
-duplicate provided string, and stores pointer within created `mg_str` structure.
-
-Note, that is you have problems in C++ (constructor shadowing), there is `mg_str_s`
-synonym for this function.
-
-Parameters:
-- `s` - Pointer to NULL-terminated string to store in created mg_str
-
-Return value: Created Mongoose string
-
-Usage example:
-
-```c
-struct mg_str str = mg_str("Hello, world!);
-```
-
-### mg\_str\_n()
-
-```c
-struct mg_str mg_str_n(const char *s, size_t n);
-```
-
-Create Mongoose string from C-string `s` (can be non-NULL terminated, length is
-specified in `n`). Note: This function doesn't duplicate provided string,
-but stores pointer within created `mg_str` structure.
-
-Parameters:
-- `s` - Pointer to string to store in created `mg_str`
-- `n` - String length
-
-Return value: Created Mongoose string
-
-Usage example:
-
-```c
-struct mg_str str = mg_str_n("hi", 2);
-```
-
-### mg\_casecmp()
-
-```c
-int mg_casecmp(const char *s1, const char *s2);
-```
-
-Case insensitive compare two NULL-terminated strings.
-
-Parameters:
-- `s1`, `s2` - Pointers to strings to compare
-
-Return value: Zero if strings are equal, more than zero if first argument is greater then second, and less than zero otherwise
-
-Usage example:
-
-```c
-if (mg_casecmp("hello", "HELLO") == 0) {
- // Strings are equal
-}
-```
-
-### mg\_ncasecmp()
-
-```c
-int mg_ncasecmp(const char *s1, const char *s2, size_t len);
-```
-
-Case insensitive compare two C-strings, not more than `len` symbols or until meet `\0` symbol.
-
-Parameters:
-- `s1`, `s2` - Pointers to strings to compare
-- `len` - Maximum length to compare
-
-Return value: Zero if strings are equal, more than zero if first argument is
-greater then second, and less than zero otherwise
-
-Usage example:
-
-```c
-if (mg_ncasecmp("hello1", "HELLO2", 5) == 0) {
- // Strings are equal
-}
-```
-
-### mg\_vcmp()
-
-```c
-int mg_vcmp(const struct mg_str *s1, const char *s2);
-```
-
-Compare mongoose string and C-string.
-
-Parameters:
-- `s1` - Pointer to Mongoose string to compare
-- `s2` - Pointer to C-string to compare
-
-Return value: 0 if strings are equal, more than zero if first argument is
-greater then second, and less than zero otherwise
-
-Usage example:
-
-```c
-struct mg_str str = mg_str("hello");
-if (mg_vcmp(str, "hello") == 0) {
- // Strings are equal
-}
-```
-
-### mg\_vcasecmp()
-
-```c
-int mg_vcasecmp(const struct mg_str *str1, const char *str2);
-```
-
-Case insensitive compare mongoose string and C-string.
-
-Parameters:
-- `str1` - Mongoose string to compare
-- `str2` - C-string to compare
-
-Return value: Zero if strings are equal, more than zero if first argument is
-greater then second, and less than zero otherwise
-
-Usage example:
-
-```c
-struct mg_str str = mg_str("hello");
-if (mg_vcasecmp(str, "HELLO") == 0) {
- // Strings are equal
-}
-```
-
-### mg\_strcmp()
-
-```c
-int mg_strcmp(const struct mg_str str1, const struct mg_str str2);
-```
-
-Compare two mongoose strings.
-
-Parameters:
-- `str1`, `str2` - Pointers to Mongoose strings to compare
-
-Return value: Zero if strings are equal, more than zero if first argument is
-greater then second, and less than zero otherwise
-
-Usage example:
-
-```c
-struct mg_str str1 = mg_str("hello");
-struct mg_str str2 = mg_str("hello");
-if (mg_strcmp(str1, str2) == 0) {
- // Strings are equal
-}
-```
-
-### mg\_strdup()
-
-```c
-struct mg_str mg_strdup(const struct mg_str s);
-```
-
-Duplicate provided string. Return new string or `MG_NULL_STR` on error.
-Note: This function allocates memory for returned string. You may need to free it using `free` function.
-
-Parameters:
-- `s` - Mongoose string to duplicate
-
-Return value: Duplicated string
-
-Usage example:
-
-```c
-struct mg_str str1 = mg_str("hello");
-struct mg_str str2 = mg_strdup(str1);
-//...
-free((void *)str2.ptr);
-```
-
-
-### mg\_strstr()
-
-```c
-const char *mg_strstr(const struct mg_str haystack, const struct mg_str needle)
-```
-
-Search for `needle` substring in `haystack` string.
-
-Parameters:
-- `haystack` - Mongoose sting to search for substring
-- `needle` - Mongoose string to search
-
-Return value: pointer to `needle`
-occurrence within `haystack` or `NULL` if not found.
-
-Usage example:
-
-```c
-struct mg_str str = mg_str("Hello, world");
-struct mg_str sub_str = mg_str("world");
-
-if (mg_strstr(str, sub_str) != NULL) {
- // Found
-}
-```
-
-### mg\_strstrip()
-
-```c
-struct mg_str mg_strstrip(struct mg_str s)
-```
-
-Remove heading and trailing whitespace from mongoose string `s`.
-
-Parameters:
-- `s` - Mongoose string for trimming
-
-Return value: Input string
-
-Usage example:
-
-```c
-struct mg_str str = mg_strstrip(mg_str(" Hello, world "));
-if (mg_vcmp(str, "Hello, world") == 0) {
- // Strings are equal
-}
-```
-
-### mg\_match()
-
-```c
-bool mg_match(struct mg_str str, struct mg_str pattern, struct mg_str *caps);
-```
-
-Check if string `str` matches glob pattern `pattern`, and optionally capture
-wildcards into the provided array `caps`.
-
-NOTE: If `caps` is not NULL, then the
-`caps` array size must be at least the number of wildcard symbols in `pattern`
-plus 1. The last cap will be initialized to an empty string.
-
-The glob pattern matching rules are as follows:
-
-- `?` matches any single character
-- `*` matches zero or more characters except `/`
-- `#` matches zero or more characters
-- any other character matches itself
-
-Parameters:
-- `str` - a string to match
-- `pattern` - a pattern to match against
-- `caps` - an optional array of captures for wildcard symbols `?`, `*`, '#'
-
-Return value: `true` if matches, `false` otherwise
-
-Usage example:
-
-```c
-// Assume that hm->uri holds /foo/bar. Then we can match the requested URI:
-struct mg_str caps[3]; // Two wildcard symbols '*' plus 1
-if (mg_match(hm->uri, mg_str("/*/*"), caps)) {
- // caps[0] holds `foo`, caps[1] holds `bar`.
-}
-```
-
-### mg\_commalist()
-
-```c
-bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v);
-```
-
-Parse string `s`, which is a comma-separated list of entries. An entry could be
-either an arbitrary string, which gets stored in `v`, or a `KEY=VALUE` which
-gets stored in `k` and `v` respectively.
-
-IMPORTANT: This function modifies `s` by pointing to the next entry.
-
-Parameters:
-- `s` - String to search for entry
-- `k` - Pointer to `mg_str` to receive entry key
-- `v` - Pointer to `mg_str` to receive entry value
-
-Return value: `true` if entry is found, `false` otherwise
-
-Usage example:
-
-```c
-struct mg_str k, v, s = mg_str("a=333,b=777");
-while (mg_commalist(&s, &k, &v)) // This loop output:
- printf("[%.*s] set to [%.*s]\n", // [a] set to [333]
- (int) k.len, k.ptr, (int) v.len, v.ptr); // [b] set to [777]
-```
-
-
-
-### mg\_hex()
-
-```c
-char *mg_hex(const void *buf, size_t len, char *dst);
-```
-
-Hex-encode binary data `buf`, `len` into a buffer `dst` and nul-terminate it.
-The output buffer must be at least 2 x `len` + 1 big.
-
-Parameters:
-- `buf` - Data to hex-encode
-- `len` - Data length
-- `dst` - Pointer to output buffer
-
-Return value: `dst` pointer. The encoded characters are lowercase
-
-Usage example:
-
-```c
-char data[] = "\x1\x2\x3";
-char buf[sizeof(data)*2];
-char *hex = mg_hex(data, sizeof(data) - 1, buf);
-LOG(LL_INFO, ("%s", hex)); // Output "010203";
-free(hex);
-```
-
-### mg\_unhex()
-
-```c
-void mg_unhex(const char *buf, size_t len, unsigned char *to);
-```
-
-Hex-decode string `buf`, `len` into a buffer `to`. The `to` buffer should be
-at least `lsn` / 2 big.
-
-Parameters:
-- `buf` - Data to hex-decode
-- `len` - Data length
-- `to` - Pointer to output buffer
-
-Return value: None
-
-Usage example:
-
-```c
-char data[] = "010203";
-char *buf[sizeof(data)/2];
-char *hex = mg_unhex(data, sizeof(data) - 1, buf); // buf is now [1,2,3]
-free(hex);
-```
-
-### mg\_unhexn()
-
-```c
-unsigned long mg_unhexn(const char *s, size_t len);
-```
-
-Parse `len` characters of the hex-encoded string `s`.
-The maximum value of `len` is the width of the `long` x 2, for example
-on 32-bit platforms it is 8.
-
-Parameters:
-- `s` - String to parse
-- `len` - String length
-
-Return value: Return parsed value
-
-Usage example:
-
-```c
-char data[] = "010203";
-char *buf[sizeof(data)/2];
-unsigned long val = mg_unhex(data, sizeof(data) - 1); // val is now 123
-```
-
-### mg\_remove\_double\_dots()
-
-```c
-char *mg_remove_double_dots(char *s);
-```
-
-Modify string `s` in place by removing double dots from it. Used to
-sanitize file names or URIs received from the network.
-
-Parameters:
-- `s` - String to sanitise
-
-Return value: the `s` pointer
-
-Usage example:
-
-```c
-char data[] = "../../a.txt";
-mg_remove_double_dots(data); // data is /a.txt
-```
-
-### mg\_snprintf(), mg\_vsnprintf()
-```c
-size_t mg_snprintf(char *buf, size_t len, const char *fmt, ...);
-size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list ap);
-```
-
-Print formatted string into a string buffer, just like `snprintf()`
-standard function does, but in a predictable way that does not depend on
-the C library or the build environment. The return value can be larger
-than the buffer length `len`, in which case the overflow bytes are not printed.
-Mongoose library is often used to exchange data in JSON format, therefore a
-non-standard `%Q`, `%V`, `%H` specifiers for formatting JSON strings is also supported.
-
-Parameters:
-- `buf` - Pointer to pointer to output buffer
-- `len` - Buffer size
-- `fmt` - printf-like format string
-
-Supported format specifiers:
-- `hhd`, `hd`, `d`, `ld`, `lld` - for `char`, `short`, `int`, `long`, `int64_t`
-- `hhu`, `hu`, `u`, `lu`, `llu` - same but for unsigned variants
-- `hhx`, `hx`, `x`, `lx`, `llx` - same, unsigned and hex output
-- `s` - expect `char *`
-- `q` - expect `char *`, outputs JSON-escaped string (extension)
-- `Q` - expect `char *`, outputs double-quoted JSON-escaped string (extension)
-- `H` - expect `int`, `void *`, outputs double-quoted hex string (extension)
-- `V` - expect `int`, `void *`, outputs double-quoted base64 string (extension)
-- `M` - expect `mg_pfn_t`, calls another print function (extension)
-- `g`, `f` - expect `double`
-- `c` - expect `char`
-- `%` - expect `%` character itself
-- `p` - expect any pointer, prints `0x.....` hex value
-- `%X.Y` - optional width and precision modifiers
-- `%.*` - optional precision modifier specified as `int` argument
-
-Return value: Number of bytes printed
-
-Sending JSON HTTP response:
-
-```c
-mg_http_reply(c, 200, "Content-Type: application/json\r\n",
- "{%Q: %g}", "value", 1.2345);
-```
-
-Example of using more complex format strings:
-
-```c
-mg_snprintf(buf, sizeof(buf), "%lld", (int64_t) 123); // 123
-mg_snprintf(buf, sizeof(buf), "%.2s", "abcdef"); // ab
-mg_snprintf(buf, sizeof(buf), "%.*s", 2, "abcdef"); // ab
-mg_snprintf(buf, sizeof(buf), "%05x", 123); // 00123
-mg_snprintf(buf, sizeof(buf), "%%-%3s", "a"); // %- a
-mg_snprintf(buf, sizeof(buf), "hi, %Q", "a"); // hi, "a"
-mg_snprintf(buf, sizeof(buf), "r: %M, %d", f,1,2,7); // r: 3, 7
-
-// Printing sub-function for %M specifier. Grabs two int parameters
-size_t f(void (*out)(char, void *), void *ptr, va_list *ap) {
- int a = va_arg(*ap, int);
- int b = va_arg(*ap, int);
- return mg_xprintf(out, ptr, "%d", a + b);
-}
-```
-
-### mg\_mprintf(), mg\_vmprintf()
-
-```c
-char *mg_mprintf(const char *fmt, ...);
-char *mg_vmprintf(const char *fmt, va_list *ap);
-```
-
-Print message into an allocated memory buffer. Caller must free the result.
-
-Parameters:
-- `fmt` - printf-like format string
-
-Return value: allocated memory buffer
-
-Usage example:
-
-```c
-char *msg = mg_mprintf("Double quoted string: %Q!", "hi");
-free(msg);
-```
-
-### mg\_xprintf(), mg\_vxprintf()
-
-```c
-size_t mg_xprintf(void (*out)(char, void *), void *param, const char *fmt, ...);
-size_t mg_vxprintf(void (*out)(char, void *), void *param, const char *fmt,
- va_list *ap);
-```
-
-Print message using a specified character output function
-
-Parameters:
-- `out` - function to be used for printing chars
-- `param` - argument to be passed to `out`
-- `fmt` - printf-like format string
-
-Return value: Number of bytes printed
-
-Usage example:
-
-```c
-void myfn(char c, void *p);
-
-size_t len = mg_xprintf(myfn, myfn_p, "Double quoted string: %Q!", "hi");
-```
-
-### mg\_pfn\_iobuf()
-
-```c
-void mg_pfn_iobuf(char ch, void *param);
-```
-
-Print a character to a [Generic IO buffer](#struct-mg_iobuf)
-
-Parameters:
-- `ch` - char to be printed
-- `param` - must be `struct mg_iobuf *`
-
-Usage example:
-
-```c
-mg_xprintf(mg_pfn_iobuf, &c->send, "hi!"); // Append to the output buffer
-```
-
-### mg\_to64()
-
-```c
-int64_t mg_to64(struct mg_str str);
-uint64_t mg_tou64(struct mg_str str);
-```
-
-Parse 64-bit integer value held by string `s`.
-
-Parameters:
-- `str` - String to parse
-
-Return value: Parsed value
-
-Usage example:
-
-```c
-int64_t val = mg_to64(mg_str("123")); // Val is now 123
-```
-
-### mg\_aton()
-
-```c
-bool mg_aton(struct mg_str str, struct mg_addr *addr);
-```
-
-Parse IP address held by `str` and store it in `addr`.
-
-Parameters:
-- `str` - String to parse, for example `1.2.3.4`, `[::1]`, `01:02::03`
-- `addr` - Pointer to `mg_addr` string to receive parsed value
-
-Return value: `true` on success, `false` otherwise
-
-Usage example:
-
-```c
-struct mg_addr addr;
-if (mg_aton(mg_str("127.0.0.1"), &addr)) {
- // addr is now binary representation of 127.0.0.1 IP address
-}
-```
-
-## JSON
-
-Note that Mongoose's printing functions support non-standard format specifiers `%Q` and `%M`,
-which allow to print JSON strings easily:
-
-```c
-char *json = mg_mprintf("{%Q:%d}", "value", 123); // {"value":123}
-free(json);
-```
-
-Therefore, for a full JSON support, a set of parsing functions is required -
-which is described below.
-
-
-### mg\_json\_get()
-
-
-```c
-enum { MG_JSON_TOO_DEEP = -1, MG_JSON_INVALID = -2, MG_JSON_NOT_FOUND = -3 };
-int mg_json_get(struct mg_str json, const char *path, int *toklen);
-```
-
-Parse JSON string `json` and return the offset of the element
-specified by the JSON `path`. The length of the element is stored
-in the `toklen`.
-
-Parameters:
-- `json` - a string containing valid JSON
-- `path` - a JSON path. Must start with `$`, e.g. `$.user`
-- `toklen` - a pointer that receives element's length, can be NULL
-
-
-Return value: offset of the element, or negative `MG_JSON_*` on error.
-
-Usage example:
-
-```c
-// Create a json string: { "a": 1, "b": [2, 3] }
-char *buf = mg_mprintf("{ %Q: %d, %Q: [%d, %d] }", "a", 1, "b", 2, 3);
-struct mg_str json = mg_str(buf);
-int offset, length;
-
-// Lookup "$", which is the whole JSON. Can be used for validation
-offset = mg_json_get(json, "$", &length); // offset = 0, length = 23
-
-// Lookup attribute "a". Point to value "1"
-offset = mg_json_get(json, "$.a", &length); // offset = 7, length = 1
-
-// Lookup attribute "b". Point to array [2, 3]
-offset = mg_json_get(json, "$.b", &length); // offset = 15, length = 6
-
-// Lookup attribute "b[1]". Point to value "3"
-offset = mg_json_get(json, "$.b[1]", &length); // offset = 19, length = 1
-
-free(buf);
-```
-
-### mg\_json\_get\_num()
-
-```c
-bool mg_json_get_num(struct mg_str json, const char *path, double *v);
-```
-
-Fetch numeric (double) value from the json string `json` at JSON path
-`path` into a placeholder `v`. Return true if successful.
-
-Parameters:
-- `json` - a string containing valid JSON
-- `path` - a JSON path. Must start with `$`
-- `v` - a placeholder for value
-
-Return value: true on success, false on error
-
-Usage example:
-
-```c
-double d = 0.0;
-mg_json_get_num(mg_str("[1,2,3]", "$[1]", &d)); // d contains 2
-mg_json_get_num(mg_str("{\"a\":1.23}", "$.a", &d)); // d contains 1.23
-```
-
-### mg\_json\_get\_bool()
-
-```c
-bool mg_json_get_bool(struct mg_str json, const char *path, bool *v);
-```
-
-Fetch boolean (bool) value from the json string `json` at JSON path
-`path` into a placeholder `v`. Return true if successful.
-
-Parameters:
-- `json` - a string containing valid JSON
-- `path` - a JSON path. Must start with `$`
-- `v` - a placeholder for value
-
-Return value: true on success, false on error
-
-Usage example:
-
-```c
-bool b = false;
-mg_json_get_bool(mg_str("[123]", "$[0]", &b)); // Error. b remains to be false
-mg_json_get_bool(mg_str("[true]", "$[0]", &b)); // b is true
-```
-
-### mg\_json\_get\_long()
-
-```c
-long mg_json_get_long(struct mg_str json, const char *path, long default_val);
-```
-
-Fetch integer numeric (long) value from the json string `json` at JSON path
-`path`. Return it if found, or `default_val` if not found.
-
-Parameters:
-- `json` - a string containing valid JSON
-- `path` - a JSON path. Must start with `$`
-- `default_val` - a default value for the failure case
-
-Return value: found value, or `default_val` value
-
-Usage example:
-
-```c
-long a = mg_json_get_long(mg_str("[123]", "$a", -1)); // a = -1
-long b = mg_json_get_long(mg_str("[123]", "$[0]", -1)); // b = 123
-```
-
-### mg\_json\_get\_str()
-
-```c
-char *mg_json_get_str(struct mg_str json, const char *path);
-```
-
-Fetch string value from the json string `json` at JSON path
-`path`. If found, a string is allocated using `calloc()`,
-un-escaped, and returned to the caller. It is the caller's responsibility to
-`free()` the returned string.
-
-Parameters:
-- `json` - a string containing valid JSON
-- `path` - a JSON path. Must start with `$`
-
-Return value: non-NULL on success, NULL on error
-
-Usage example:
-
-```c
-struct mg_str json = mg_str("{\"a\": \"hi\"}"); // json = {"a": "hi"}
-char *str = mg_json_get_str(json, "$.a"); // str = "hi"
-free(str);
-```
-
-### mg\_json\_get\_hex()
-
-```c
-char *mg_json_get_hex(struct mg_str json, const char *path, int *len);
-```
-
-Fetch hex-encoded buffer from the json string `json` at JSON path
-`path`. If found, a buffer is allocated using `calloc()`, decoded,
-and returned to the caller. It is the caller's responsibility to
-`free()` the returned string. Returned buffer is 0-terminated.
-
-Parameters:
-- `json` - a string containing valid JSON
-- `path` - a JSON path. Must start with `$`
-- `len` - a pointer that receives decoded length. Can be NULL
-
-Return value: non-NULL on success, NULL on error
-
-Usage example:
-
-```c
-struct mg_str json = mg_str("{\"a\": \"6869\"}"); // json = {"a": "6869"}
-char *str = mg_json_get_hex(json, "$.a", NULL); // str = "hi"
-free(str);
-```
-
-### mg\_json\_get\_b64()
-
-```c
-char *mg_json_get_b4(struct mg_str json, const char *path, int *len);
-```
-
-Fetch base64-encoded buffer from the json string `json` at JSON path
-`path`. If found, a buffer is allocated using `calloc()`, decoded,
-and returned to the caller. It is the caller's responsibility to
-`free()` the returned string. Returned buffer is 0-terminated.
-
-Parameters:
-- `json` - a string containing valid JSON
-- `path` - a JSON path. Must start with `$`
-- `len` - a pointer that receives decoded length. Can be NULL
-
-Return value: non-NULL on success, NULL on error
-
-Usage example:
-
-```c
-struct mg_str json = mg_str("{\"a\": \"YWJj\"}"); // json = {"a": "YWJj"}
-char *str = mg_json_get_b64(json, "$.a", NULL); // str = "abc"
-free(str);
-```
-
-## RPC
-
-Mongoose includes a set of functions to ease server-side processing by means of RPC methods.
-
-### struct mg\_rpc
-
-The RPC method handler structure. Each method has an entry in a linked list, each entry points to a string describing the pattern that will invoke it and the function that will be called to satisfy the method invocation, with a proper function argument.
-
-```c
-struct mg_rpc {
- struct mg_rpc *next; // Next in list
- struct mg_str method; // Method pattern
- void (*fn)(struct mg_rpc_req *); // Handler function
- void *fn_data; // Handler function argument
-};
-```
-
-### struct mg\_rpc\_req
-
-The RPC request descriptor. An invoked method receives a descriptor containing the request, and a pointer to a function to be called to print the output response, with a proper function argument; e.g.: [mg_pfn_realloc()](#mg_pfn_realloc) or [mg_pfn_iobuf()](#mg_pfn_iobuf)
-
-```c
-struct mg_rpc_req {
- struct mg_rpc **head; // RPC handlers list head
- struct mg_rpc *rpc; // RPC handler being called
- mg_pfn_t pfn; // Response printing function
- void *pfn_data; // Response printing function data
- void *req_data; // Arbitrary request data
- struct mg_str frame; // Request, e.g. {"id":1,"method":"add","params":[1,2]}
-};
-```
-
-### mg\_rpc\_add()
-
-```c
-void mg_rpc_add(struct mg_rpc **head, struct mg_str method_pattern,
- void (*handler)(struct mg_rpc_req *), void *handler_data);
-```
-
-Add the method `method_pattern` to the list `head` of RPC methods. Invoking this method will call `handler` and pass `handler_data` to it with the request (as `r->fn_data` in the usage example below).
-
-Parameters:
-- `head` - the linked list pointer
-- `method_pattern` - the name of the method
-- `handler` - the RPC function performing the action for this method
-- `handler_data` - Arbitrary function data
-
-NOTE:
-if `method_pattern` is an empty string, this handler will be called to process
-JSON-RPC responses. Handling responses might be necessary if the JSON requests
-are initiated by both sides.
-
-Usage example:
-
-```c
-struct mg_rpc *s_rpc_head = NULL;
-
-static void rpc_sum(struct mg_rpc_req *r) {
- double a = 0.0, b = 0.0;
- mg_json_get_num(r->frame, "$.params[0]", &a);
- mg_json_get_num(r->frame, "$.params[1]", &b);
- mg_rpc_ok(r, "%g", a + b);
-}
-
-static void rpc_mul(struct mg_rpc_req *r) {//...}
-
-
- mg_rpc_add(&s_rpc_head, mg_str("sum"), rpc_sum, NULL);
- mg_rpc_add(&s_rpc_head, mg_str("mul"), rpc_mul, NULL);
-```
-
-### mg\_rpc\_del()
-
-```c
-void mg_rpc_del(struct mg_rpc **head, void (*handler)(struct mg_rpc_req *));
-```
-
-Remove the method with RPC function handler `handler` from the list `head` of RPC methods.
-
-Parameters:
-- `head` - the linked list pointer
-- `handler` - the RPC function performing the action for this method, use NULL to deallocate all
-
-Usage example:
-
-```c
-struct mg_rpc *s_rpc_head = NULL;
-// add methods
-// ...
-
-// Time to cleanup
-mg_rpc_del(&s_rpc_head, rpc_mul); // Deallocate specific handler
-mg_rpc_del(&s_rpc_head, NULL); // Deallocate all RPC handlers
-```
-
-### mg\_rpc\_process()
-
-```c
-void mg_rpc_process(struct mg_rpc_req *req);
-```
-
-Invoke the proper method for this request. If the requested method does not exist, `mg_rpc_err()` will be invoked and an error indication will be printed
-
-Parameters:
-- `req` - a request
-
-
-Usage example:
-
-```c
-struct mg_rpc *s_rpcs = NULL; // Empty RPC list head
-mg_rpc_add(&s_rpcs, mg_str("rpc.list"), mg_rpc_list, NULL); // Add rpc.list
-// ... add more RPC methods
-
-// On request, process the incoming frame
-struct mg_str req = mg_str("{\"id\":1,\"method\":\"sum\",\"params\":[1,2]}");
-struct mg_iobuf io = {0, 0, 0, 512}; // Empty IO buf, with 512 realloc granularity
-struct mg_rpc_req r = {
- .head = &s_rpcs, // RPC list head
- .rpc = NULL, // This will be set by mg_rpc_process()
- .pfn = mg_pfn_iobuf, // Printing function: print into the io buffer
- .pfn_data = &io, // Pass our io buffer as a parameter
- .req_data = NULL, // No specific request data
- .frame = req, // Specify incoming frame
-};
-
-mg_rpc_process(&r);
-if (io.buf != NULL) printf("Response: %s\n", (char *) io.buf);
-mg_iobuf_free(&io);
-```
-
-### mg\_rpc\_ok(), mg\_rpc\_vok()
-
-```c
-void mg_rpc_ok(struct mg_rpc_req *, const char *fmt, ...);
-void mg_rpc_vok(struct mg_rpc_req *, const char *fmt, va_list *ap);
-```
-
-Helper functions to print result frames
-
-Parameters:
-- `req` - a request
-- `fmt` - printf-like format string
-
-Usage example:
-
-```c
-static void rpc_sum(struct mg_rpc_req *r) {
- double a = 0.0, b = 0.0;
- mg_json_get_num(r->frame, "$.params[0]", &a);
- mg_json_get_num(r->frame, "$.params[1]", &b);
- mg_rpc_ok(r, "%g", a + b);
-}
-```
-
-### mg\_rpc\_err(), mg\_rpc\_verr()
-
-```c
-void mg_rpc_err(struct mg_rpc_req *, int code, const char *fmt, ...);
-void mg_rpc_verr(struct mg_rpc_req *, int code, const char *fmt, va_list *);
-```
-
-Helper functions to print error frames
-
-Parameters:
-- `req` - a request
-- `fmt` - printf-like format string
-
-Usage example:
-
-```c
-static void rpc_dosome(struct mg_rpc_req *r) {
- ...
- mg_rpc_err(r, -32109, "\"%.*s not found\"", len, &r->frame.ptr[offset]);
-}
-```
-
-### mg\_rpc\_list()
-
-```c
-void mg_rpc_list(struct mg_rpc_req *r);
-```
-
-Built-in RPC method to list all registered RPC methods. This function is not usually called directly, but registered as a method.
-
-Parameters:
-- `req` - a request
-
-Usage example:
-
-```c
-mg_rpc_add(&s_rpc_head, mg_str("rpc.list"), mg_rpc_list, &s_rpc_head);
-```
-
-(see also [mg_rpc_add()](#mg_rpc_add))
-
-
-## Utility
-
-### mg\_call()
-
-```c
-void mg_call(struct mg_connection *c, int ev, void *ev_data);
-```
-
-Send `ev` event to `c` event handler. This function is useful then implementing
-your own protocol.
-
-Parameters:
-- `c` - Connection to send event
-- `ev` - Event to send
-- `ev_data` - Additional event parameter
-
-Return value: None
-
-Usage example:
-
-```c
-// In a timer callback, send MG_EV_USER event to all connections
-static void timer_fn(void *arg) {
- struct mg_mgr *mgr = (struct mg_mgr *) arg;
- for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) {
- mg_call(c, MG_EV_USER, "hi!");
- }
-}
-```
-
-### mg\_error()
-
-```c
-void mg_error(struct mg_connection *c, const char *fmt, ...);
-```
-
-Send `MG_EV_ERROR` to connection event handler with error message formatted using printf semantics.
-
-Parameters:
-- `c` - Connection to send event
-- `fmt` - Format string in `printf` semantics
-
-Return value: None
-
-Usage example:
-
-```c
-mg_error(c, "Operation failed, error code: %d", errno);
-```
-
-### mg\_md5\_init()
-
-```c
-void mg_md5_init(mg_md5_ctx *c);
-```
-
-Initialize context for MD5 hashing.
-
-Parameters:
-- `c` - Pointer to `mg_md5_ctx` structure to initialize
-
-Return value: None
-
-Usage example:
-
-```c
-mg_md5_ctx ctx;
-mg_md5_init(&ctx);
-```
-
-### mg\_md5\_update()
-
-```c
-void mg_md5_update(mg_md5_ctx *c, const unsigned char *data, size_t len);
-```
-Hash `len` bytes of data pointed by `data` using MD5 algorithm.
-
-Parameters:
-- `c` - MD5 context
-- `data` - Data to hash
-- `len` - Data length
-
-Return value: None
-
-Usage example:
-
-```c
-mg_md5_ctx ctx;
-// Context initialization
-// ...
-
-mg_md5_update(&ctx, "data", 4); // hash "data" string
-mg_md5_update(&ctx, "more data", 9); // hash "more data" string
-```
-
-### mg\_md5\_final()
-
-```c
-void mg_md5_final(mg_md5_ctx *c, unsigned char buf[16]);
-```
-
-Get current MD5 hash for context.
-
-Parameters:
-- `c` - MD5 context
-- `buf` - Pointer to buffer to write MD5 hash value
-
-Return value: None
-
-Usage example:
-
-```c
-mg_md5_ctx ctx;
-// Context initialization
-// ...
-
-unsigned char buf[16];
-mg_md5_final(&ctx, buf); // `buf` is now MD5 hash
-```
-
-### mg\_sha1\_init()
-
-```c
-void mg_sha1_init(mg_sha1_ctx *c);
-```
-
-Initialize context for calculating SHA1 hash
-
-Parameters:
-- `c` - pointer to `mg_sha1_ctx` structure to initialize
-
-Return value: none
-
-Usage example:
-
-```c
-mg_sha1_ctx ctx;
-mg_sha1_init(&ctx);
-```
-
-### mg\_sha1\_update()
-
-```c
-void mg_sha1_update(mg_sha1_ctx *c, const unsigned char *data, size_t len);
-```
-
-Hash `len` bytes of `data` using SHA1 algorithm.
-
-Parameters:
-- `c` - SHA1 context
-- `data` - Data to hash
-- `len` - Data length
-
-Return value: None
-
-Usage example:
-
-```c
-mg_sha1_ctx ctx;
-// Context initialization
-// ...
-
-mg_sha1_update(&ctx, "data", 4); // hash "data" string
-mg_sha1_update(&ctx, "more data", 9); // hash "more data" string
-```
-
-### mg\_sha1\_final()
-
-```c
-void mg_sha1_final(unsigned char digest[20], mg_sha1_ctx *c);
-```
-
-Get current SHA1 hash for context.
-
-Parameters:
-- `c` - SHA1 context
-- `digest` - Pointer to buffer to receive hash value
-
-Return value: None
-
-Usage example:
-
-```c
-mg_sha1_ctx ctx;
-// Context initialization
-// ...
-
-unsigned char buf[20];
-mg_sha1_final(buf, &ctx); // `buf` is now SHA1 hash
-```
-
-### mg\_base64\_update()
-
-```c
-int mg_base64_update(unsigned char p, char *out, int pos);
-```
-
-Encode `p` byte to base64 and write result into `out` buffer starting with `pos` position.
-
-Parameters:
-- `p` - Byte to encode
-- `out` - Pointer to buffer to write result
-- `pos` - Position in output buffer to write result
-
-Return value: New position for further operations
-
-Usage example:
-
-```c
-char buf[10];
-mg_base64_update((unsigned char)"a", buf, 0); // Encode "a" into base64 and write it to the beginning of buf
-```
-
-### mg\_base64\_final()
-
-```c
-int mg_base64_final(char *buf, int pos);
-```
-
-Add base64 finish mark and `\0` symbol to `buf` at `pos` position.
-
-Parameters:
-- `buf` - Pointer to buffer to write finish mark
-- `pos` - Position to write
-
-Return value: New position for further operations
-
-```c
-char buf[10];
-int pos;
-// ...
-
-mg_base64_final(buf, pos);
-```
-
-### mg\_base64\_encode()
-
-```c
-int mg_base64_encode(const unsigned char *p, int n, char *to);
-```
-
-Encode `n` bytes data pointed by `p` using base64 and write result into `to`.
-
-Parameters:
-- `p` - Pointer to data to encode
-- `n` - Data length
-- `to` - Pointer to buffer to write result
-
-Return value: Written symbols number
-
-Usage example:
-
-```c
-char buf[128];
-mg_base64_encode((uint8_t *) "abcde", 5, buf); // buf is now YWJjZGU=
-```
-
-### mg\_base64\_decode()
-
-```c
-int mg_base64_decode(const char *src, int n, char *dst);
-```
-
-Decode `n` bytes of base64-ed `src` and write it to `dst`.
-
-Parameters:
-- `src` - Data to decode
-- `n` - Data length
-- `dst` - Pointer to output buffer
-
-Return value: Number of written symbols.
-
-Usage example:
-
-```c
-char buf[128];
-mg_base64_decode("Q2VzYW50YQ==", 12, buf); // buf is now "Cesanta"
-```
-
-### mg\_random()
-
-```c
-void mg_random(void *buf, size_t len);
-```
-
-Fill in buffer `buf`, `len` with random data. Note: Mongoose uses this
-function for TLS and some other routines that require RNG (random number
-generator). It is possible to override a built-in `mg_random()` by specifying
-a `MG_ENABLE_CUSTOM_RANDOM=1` build preprocessor constant.
-
-Parameters:
-- `buf` - Pointer to buffer to receive random data
-- `len` - Buffer size
-
-Return value: None
-
-Usage example:
-```c
-char buf[10];
-mg_random(buf, sizeof(buf)); // `buf` is now random bytes
-```
-
-### mg\_random\_str()
-
-```c
-char *mg_random_str(char *buf, size_t len);
-```
-
-Fill in buffer `buf`, `len` with random alphanumeric characters: `a-zA-Z0-9`.
-A buffer is zero-terminated.
-
-Parameters:
-- `buf` - a pointer to a buffer
-- `len` - a buffer size
-
-Return value: `buf` value.
-
-Usage example:
-```c
-char buf[10];
-printf("Random: %s\n", mg_random_str(buf, sizeof(buf)));
-```
-
-### mg\_ntohs()
-
-```c
-uint16_t mg_ntohs(uint16_t net);
-```
-
-Convert `uint16_t` value to host order.
-
-Parameters:
-- `net` - 16-bit value in network order
-
-Return value: 16-bit value in host order
-
-Usage example:
-
-```c
-uint16_t val = mg_ntohs(0x1234);
-```
-
-### mg\_ntohl()
-
-```c
-uint32_t mg_ntohl(uint32_t net);
-```
-
-Convert `uint32_t` value to host order.
-
-Parameters:
-- `net` - 32-bit value in network order
-
-Return value: 32-bit value in host order
-
-Usage example:
-
-```c
-uint32_t val = mg_ntohl(0x12345678);
-```
-
-### mg\_htons()
-
-```c
-uint16_t mg_htons(uint16_t h);
-```
-
-Convert `uint16_t` value to network order.
-
-Parameters:
-- `h` - 16-bit value in host order
-
-Return value: 16-bit value in network order
-
-Usage example:
-
-```c
-uint16_t val = mg_htons(0x1234);
-```
-
-### mg\_htonl()
-
-```c
-uint32_t mg_ntohl(uint32_t h);
-```
-
-Convert `uint32_t` value to network order.
-
-Parameters:
-- `h` - 32-bit value in host order
-
-Return value: 32-bit value in network order
-
-Usage example:
-
-```c
-uint32_t val = mg_htonl(0x12345678);
-```
-
-### mg\_crc32()
-
-```c
-uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len);
-```
-
-Calculate CRC32 checksum for a given buffer. An initial `crc` value should be `0`.
-
-Parameters:
-- `crc` - Initial CRC value
-- `buf` - Data to calculate CRC32
-- `len` - Data size
-
-Return value: Calculated CRC32 checksum
-
-Usage example:
-
-```c
-char data[] = "hello";
-uint32_t crc = mg_crc32(0, data, sizeof(data));
-```
-
-### mg\_check\_ip\_acl()
-
-```c
-int mg_check_ip_acl(struct mg_str acl, uint32_t remote_ip);
-```
-
-Check IPv4 address `remote_ip` against the IP ACL `acl`. Parameters:
-
-Parameters:
-- `acl` - an ACL string, e.g. `-0.0.0.0/0,+1.2.3.4`
-- `remote_ip` - IPv4 address in network byte order
-
-Return value: 1 if `remote_ip` is allowed, 0 if not, and <0 if `acl` is invalid
-
-Usage example:
-
-```c
-if (mg_check_ip_acl(mg_str("-0.0.0.0/0,+1.2.3.4"), c->peer.ip) != 1) {
- LOG(LL_INFO, ("NOT ALLOWED!"));
-}
-```
-
-### mg\_url\_decode()
-
-```c
-int mg_url_decode(const char *s, size_t n, char *to, size_t to_len, int form);
-```
-
-Decode URL-encoded string `s` and write it into `to` buffer.
-
-Parameters:
-- `s` - String to encode
-- `n` - String to encode length
-- `to` - Pointer to output buffer
-- `to_len` - Output buffer size
-- `form` - If non-zero, then `+` is decoded as whitespace.
-
-Return value: Decoded bytes count or negative value on error
-
-Usage example:
-
-```c
-char url[] = "eexample.org%2Ftest";
-char buf[1024];
-mg_url_encode(url, sizeof(url) - 1, buf, sizeof(buf), 0); // buf is now "example.org/test"
-```
-
-### mg\_url\_encode
-
-```c
-size_t mg_url_encode(const char *s, size_t n, char *buf, size_t len);
-```
-
-Encode `s` string to URL-encoding and write encoded string into `buf`.
-
-Parameters:
-- `s` - String to encode
-- `n` - String to encode length
-- `buf` - Output buffer
-- `len` - Output buffer size
-
-Return value: Number of characters written to `buf`
-
-Usage example:
-
-```c
-char url[] = "example.org/test";
-char buf[1024];
-mg_url_encode(url, sizeof(url) - 1, buf, sizeof(buf)); // buf is now "example.org%2Ftest"
-```
-
-### mg\_print\_ip
-
-```c
-size_t mg_print_ip(void (*out)(char, void *), void *param, va_list *ap);
-```
-
-Print an IP address using a specified character output function. Expects a pointer to a `struct mg_str` as the next argument in the _va\_list_ `ap`
-
-Parameters:
-- `out` - function to be used for printing chars
-- `param` - argument to be passed to `out`
-
-Return value: Number of bytes printed
-
-Usage example:
-
-```c
-struct mg_address addr;
-addr.ip = MG_U32('a', 'b', 'c', 'd');
-mg_snprintf(buf, sizeof(buf), "%M", mg_print_ip, &addr); // 97.98.99.100
-```
-
-### mg\_print\_ip\_port
-
-```c
-size_t mg_print_ip_port(void (*out)(char, void *), void *param, va_list *ap);
-```
-
-Print an IP address and port, using a specified character output function. Expects a pointer to a `struct mg_str` as the next argument in the _va\_list_ `ap`
-
-Parameters:
-- `out` - function to be used for printing chars
-- `param` - argument to be passed to `out`
-
-Return value: Number of bytes printed
-
-Usage example:
-
-```c
-struct mg_address addr;
-addr.ip = MG_U32('a', 'b', 'c', 'd');
-addr.port = mg_htons(1234);
-mg_snprintf(buf, sizeof(buf), "%M", mg_print_ip_port, &addr); // 97.98.99.100:1234
-```
-
-### mg\_print\_ip4
-
-```c
-size_t mg_print_ip4(void (*out)(char, void *), void *param, va_list *ap);
-```
-
-Print an IP address using a specified character output function. Expects a pointer to a buffer containing the IPv4 address in network order as the next argument in the _va\_list_ `ap`
-
-Parameters:
-- `out` - function to be used for printing chars
-- `param` - argument to be passed to `out`
-
-Return value: Number of bytes printed
-
-Usage example:
-
-```c
-mg_snprintf(buf, sizeof(buf), "%M", mg_print_ip4, "abcd"); // 97.98.99.100
-```
-
-### mg\_print\_ip6
-
-```c
-size_t mg_print_ip6(void (*out)(char, void *), void *param, va_list *ap);
-```
-
-Print an IPv6 address using a specified character output function. Expects a pointer to a buffer containing the IPv6 address in network order as the next argument in the _va\_list_ `ap`
-
-Parameters:
-- `out` - function to be used for printing chars
-- `param` - argument to be passed to `out`
-
-Return value: Number of bytes printed
-
-Usage example:
-
-```c
-mg_snprintf(buf, sizeof(buf), "%M", mg_print_ip6, "abcdefghijklmnop"); // [4142:4344:4546:4748:494a:4b4c:4d4e:4f50]
-```
-
-### mg\_print\_mac
-
-```c
-size_t mg_print_mac(void (*out)(char, void *), void *param, va_list *ap);
-```
-
-Print a MAC address using a specified character output function. Expects a pointer to a buffer containing the hardware address as the next argument in the _va\_list_ `ap`
-
-Parameters:
-- `out` - function to be used for printing chars
-- `param` - argument to be passed to `out`
-
-Return value: Number of bytes printed
-
-Usage example:
-
-```c
-mg_snprintf(buf, sizeof(buf), "%M", mg_print_mac, "abcdef"); // 61:62:63:64:65:66
-```
-
-## IO Buffers
-
-IO buffer, described by the `struct mg_iobuf`, is a simple data structure
-that inserts or deletes chunks of data at arbitrary offsets and grows/shrinks
-automatically.
-
-### struct mg\_iobuf
-
-```c
-struct mg_iobuf {
- unsigned char *buf; // Pointer to stored data
- size_t size; // Total size available
- size_t len; // Current number of bytes
- size_t align; // Alignment during allocation
-};
-```
-
-Generic IO buffer. The `size` specifies an allocation size of the data pointed
-by `buf`, and `len` specifies number of bytes currently stored.
-
-
-
-### mg\_iobuf\_init()
-
-```c
-int mg_iobuf_init(struct mg_iobuf *io, size_t size, size_t align);
-```
-
-Initialize IO buffer, allocate `size` bytes.
-
-Parameters:
-- `io` - Pointer to `mg_iobuf` structure to initialize
-- `size` - Amount of bytes to allocate
-- `align` - Align `size` to the `align` mem boundary. `0` means no alignment
-
-Return value: 1 on success, 0 on allocation failure
-
-Usage example:
-
-```c
-struct mg_iobuf io;
-if (mg_iobuf_init(&io, 0, 64)) {
- // io successfully initialized
-}
-```
-
-### mg\_iobuf\_resize()
-
-```c
-int mg_iobuf_resize(struct mg_iobuf *io, size_t size);
-```
-
-Resize IO buffer, set the new size to `size`. The `io->buf` pointer could
-change after this, for example if the buffer grows. If `size` is 0, then the
-`io->buf` is freed and set to NULL, and both `size` and `len` are set to 0.
-The resulting `io->size` is always aligned to the `io->align` byte boundary;
-therefore, to avoid memory fragmentation and frequent reallocations, set
-`io->align` to a higher value.
-
-Parameters:
-- `io` - iobuf to resize
-- `size` - New size
-
-Return value: 1 on success, 0 on allocation failure
-
-Usage example:
-
-```c
-struct mg_iobuf io;
-mg_iobuf_init(&io, 0, 10); // An empty buffer with 10-byte alignment
-
-if (mg_iobuf_resize(&io, 1)) {
- // New io size is 10
-}
-```
-
-### mg\_iobuf\_free()
-
-```c
-void mg_iobuf_free(struct mg_iobuf *io);
-```
-
-Free memory pointed by `io->buf` and set to NULL. Both `size` and `len` are set to 0.
-
-Parameters:
-- `io` - iobuf to free
-
-Return value: None
-
-Usage example:
-
-```c
-struct mg_iobuf io;
-// IO buffer initialization
-// ...
-
-// Time to cleanup
-mg_iobuf_free(&io);
-```
-
-### mg\_iobuf\_add()
-
-```c
-size_t mg_iobuf_add(struct mg_iobuf *io, size_t offset, const void *buf, size_t len);
-```
-
-Insert data buffer `buf`, `len` at offset `offset`. The iobuf is expanded
-if required. The resulting `io->size` is always aligned to the `io->align` byte boundary; therefore,
-to avoid memory fragmentation and frequent reallocations, set `align` to a higher value.
-
-Parameters:
-- `io` - iobuf to add data
-- `offset` - Offset to add data
-- `buf` - Data to add
-- `len` - Data length
-
-Return value: new `io` length
-
-Usage example:
-
-```c
-struct mg_iobuf io; // Declare buffer
-mg_iobuf_init(&io, 0, 16); // Initialise empty buffer with 16 byte alignment
-```
-
-
-
-```c
-mg_iobuf_add(&io, io.len, "hello", 5); // Append "hello"
-```
-
-
-
-
-### mg\_iobuf\_del()
-
-```c
-size_t mg_iobuf_del(struct mg_iobuf *io, size_t offset, size_t len);
-```
-
-Delete `len` bytes starting from `offset`, and shift the remaining bytes.
-If `len` is greater than `io->len`, nothing happens, so such call is silently ignored.
-
-Parameters:
-- `io` - iobuf to delete data
-- `offset` - Start offset
-- `len` - Amount of bytes to delete
-
-Return value: New `io` length
-
-Usage example:
-
-```c
-struct mg_iobuf io;
-mg_iobuf_init(&io, 0, 16); // Empty buffer
-mg_iobuf_add(&io, 0, "hi", 2); // io->len is 2, io->size is 16
-mg_iobuf_del(&io, 0, "hi", 2); // io->len is 0, io->size is still 16
-```
-
-
-
-## URL
-
-### mg\_url\_port()
-
-```c
-unsigned short mg_url_port(const char *url);
-```
-
-Return port for given URL
-
-Parameters:
-- `url` - URL to extract port
-
-Return value: Port for given URL or `0` if URL doesn't contain port and there
-isn't default port for URL protocol
-
-Usage example:
-
-```c
-unsigned short port1 = mg_url_port("https://myhost.com") // port1 is now 443 (default https port)
-unsigned short port2 = mg_url_port("127.0.0.1:567") // port2 is now 567
-```
-
-### mg\_url\_is_ssl()
-
-```c
-int mg_url_is_ssl(const char *url);
-```
-
-Check if given URL uses encrypted scheme
-
-Parameters:
-- `url` - URL to check
-
-Return value: `0` is given URL uses encrypted scheme and non-zero otherwise
-
-Usage example:
-
-```c
-if (mg_url_is_ssl("https://example.org") == 0) {
- // scheme is encrypted
-}
-```
-
-### mg\_url\_host()
-
-```c
-struct mg_str mg_url_host(const char *url);
-```
-
-Extract host name from given URL.
-
-Parameters:
-- `url` - a URL string
-
-Return value: host name
-
-Usage example:
-
-```c
-struct mg_str a = mg_url_host("https://my.example.org:1234"); // a == "my.example.org"
-struct mg_str b = mg_url_host("tcp://[::1]"); // b == "[::1]"
-```
-
-### mg\_url\_user()
-
-```c
-struct mg_str mg_url_user(const char *url);
-```
-
-Extract user name from given URL.
-
-Parameters:
-- `url` - URL to extract user name
-
-Return value: User name or empty string if not found
-
-Usage example:
-
-```c
-struct mg_str user_name = mg_url_user("https://user@password@my.example.org"); // user_name is now "user"
-```
-
-### mg\_url\_pass()
-
-```c
-struct mg_str mg_url_pass(const char *url);
-```
-
-Extract password from given URL.
-
-Parameters:
-- `url` - URL to extract password
-
-Return value: Password or empty string if not found
-
-Usage example:
-
-```c
-struct mg_str pwd = mg_url_user("https://user@password@my.example.org"); // pwd is now "password"
-```
-
-### mg\_url\_uri()
-
-```c
-const char *mg_url_uri(const char *url);
-```
-
-Extract URI from given URL.
-Note, that function returns pointer within `url`; there is no need to free() it explicitly
-
-Parameters:
-- `url` - URL to extract URI
-
-Return value: URI or `\` if not found
-
-Usage example:
-
-```c
-const char *uri = mg_url_uri("https://example.org/subdir/subsubdir"); // `uri` is now pointer to "subdir/subsubdir"
-```
-
-
-
-## Logging
-
-Mongoose provides a set of functions and macros for logging. The application can
-use these functions for its own purposes as well as the rest of Mongoose API.
-
-### LOG()
-
-```c
-#define LOG(level, args)
-#define MG_ERROR(args) MG_LOG(MG_LL_ERROR, args)
-#define MG_INFO(args) MG_LOG(MG_LL_INFO, args)
-#define MG_DEBUG(args) MG_LOG(MG_LL_DEBUG, args)
-#define MG_VERBOSE(args) MG_LOG(MG_LL_VERBOSE, args)
-```
-
-Logging macros.
-Usage example:
-
-```c
-MG_INFO(("Hello %s!", "world")); // Output "Hello, world"
-```
-
-### mg\_log\_set()
-
-```c
-void mg_log_set(const char *spec);
-```
-
-Set Mongoose logging level.
-
-Parameters:
-- `spec` - String, containing log level, can be one of the following values:
- - `0` - Disable logging
- - `1` - Log errors only
- - `2` - Log errors and info messages
- - `3` - Log errors, info and debug messages
- - `4` - Log everything
-
-Return value: None
-
-It is possible to override log level per source file basis. For example, if
-there is a file called `foo.c`, and you'd like to set a global level to `2`
-(info) but increase log level for file foo.c to `debug`, then, a `spec` should
-look like `"2,foo.c=3"`. There could be several comma-separated overrides.
-
-Usage example:
-```c
-mg_log_set("2"); // Set log level to info
-mg_log_set("2,foo.c=3,bar.c=0"); // Set log level to info, with overrides
-```
-
-### mg\_hexdump()
-
-```c
-void mg_hexdump(const void *buf, int len);
-```
-
-Log a hex dump of binary data `buf`, `len`.
-
-Parameters:
-- `buf` - Data pointer
-- `len` - Data length
-
-Return value: none
-
-Usage example:
-
-```c
-mg_hexdump(c->recv.buf, c->recv.len); // Hex dump incoming data
-```
-
-### mg\_log\_set\_fn()
-
-```c
-void mg_log_set_fn(mg_pfn_t logfunc, void *param);
-```
-
-Redirect logs to a custom function. Parameters:
-- `logfunc` - a pointer to a function that logs a single character
-- `param` - a parameter for a logging function
-
-Usage example: redirecting logs to syslog.
-
-```c
-static void mylog(char ch, void *param) {
- static char buf[256];
- static size_t len;
- buf[len++] = ch;
- if (ch == '\n' || len >= sizeof(buf)) {
- syslog(LOG_INFO, "%.*s", (int) len, buf); // Send logs
- len = 0;
- }
-}
-...
-mg_log_set_fn(mylog, NULL);
-```
-
-## Filesystem
-
-### struct mg\_fs
-
-```c
-struct mg_fs {
- int (*st)(const char *path, size_t *size, time_t *mtime); // stat file
- void (*ls)(const char *path, void (*fn)(const char *, void *), void *);
- void *(*op)(const char *path, int flags); // Open file
- void (*cl)(void *fd); // Close file
- size_t (*rd)(void *fd, void *buf, size_t len); // Read file
- size_t (*wr)(void *fd, const void *buf, size_t len); // Write file
- size_t (*sk)(void *fd, size_t offset); // Set file position
- bool (*mv)(const char *from, const char *to); // Rename file
- bool (*rm)(const char *path); // Delete file
- bool (*mkd)(const char *path); // Create directory
-};
-
-enum { MG_FS_READ = 1, MG_FS_WRITE = 2, MG_FS_DIR = 4 };
-```
-
-Filesystem virtualisation layer.
-
-Mongoose allows to override file IO operations in order to support different
-storages, like programmable flash, no-filesystem devices etc.
-In order to accomplish this, Mongoose provides a `struct mg_fs` API to
-specify a custom filesystem. In addition to this, Mongoose provides several
-built-in APIs - a standard POSIX, FatFS, and a "packed FS" API:
-
-```c
-extern struct mg_fs mg_fs_posix; // POSIX open/close/read/write/seek
-extern struct mg_fs mg_fs_packed; // Packed FS, see examples/device-dashboard
-extern struct mg_fs mg_fs_fat; // FAT FS
-```
-
-### struct mg\_fd
-
-```c
-struct mg_fd {
- void *fd;
- struct mg_fs *fs;
-};
-```
-
-Opened file abstraction.
-
-### mg\_fs\_open()
-
-```c
-struct mg_fd *mg_fs_open(struct mg_fs *fs, const char *path, int flags);
-```
-
-Open a file in a given filesystem.
-
-Parameters:
-- `fs` - a filesystem implementation
-- `path` - a file path
-- `flags` - desired flags, a combination of `MG_FS_READ` and `MG_FS_WRITE`
-
-Return value: a non-NULL opened descriptor, or NULL on failure.
-
-Usage example:
-
-```c
-struct mg_fd *fd = mg_fs_open(&mg_fs_posix, "/tmp/data.json", MG_FS_WRITE);
-```
-
-### mg\_fs\_close()
-
-```c
-void mg_fs_close(struct mg_fd *fd);
-```
-
-Close an opened file descriptor.
-
-Parameters:
-- `fd` - an opened file descriptor
-
-Return value: none
-
-### mg\_file\_read()
-
-```c
-char *mg_file_read(struct mg_fs *fs, const char *path, size_t *size);
-```
-
-Read the whole file in memory.
-
-Parameters:
-- `fs` - a filesystem implementation
-- `path` - a file path
-- `size` - if not NULL, will contained the size of the read file
-
-Return value: on success, a pointer to file data, which is guaranteed
-to the 0-terminated. On error, NULL.
-
-Usage example:
-
-```c
-size_t size = 0;
-char *data = mg_file_read(&mg_fs_packed, "/data.json", &size);
-```
-
-### mg\_file\_write()
-
-```c
-bool mg_file_write(struct mg_fs *fs, const char *path, const void *buf, size_t len);
-```
-
-Write a piece of data `buf`, `len` to a file `path`. If the file does not
-exist, it gets created. The previous content, if any, is deleted.
-
-Parameters:
-- `fs` - a filesystem implementation
-- `path` - a file path
-- `buf` - a pointer to data to be written
-- `len` - a data size
-
-Return value: true on success, false on error
-
-Usage example:
-
-```c
-mg_file_write(&mg_fs_fat, "/test.txt", "hi\n", 3);
-```
-
-### mg\_file\_printf()
-
-```c
-bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...);
-```
-
-Write a printf-formatted data to a file `path`. If the file does not
-exist, it gets created. The previous content, if any, is deleted.
-
-Parameters:
-- `fs` - a filesystem implementation
-- `path` - a file path
-- `fmt` - a [mg_snprintf](#mg-snrpintf) format string
-
-Return value: true on success, false on error
-
-Usage example:
-
-```c
-mg_file_printf(&mg_fs_fat, "/test.txt", "%s\n", "hi");
-```
diff --git a/docs/images/arch1.svg b/docs/images/arch1.svg
deleted file mode 100644
index dc95f61e..00000000
--- a/docs/images/arch1.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
\ No newline at end of file
diff --git a/docs/images/arch2.svg b/docs/images/arch2.svg
deleted file mode 100644
index f1af3d18..00000000
--- a/docs/images/arch2.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
\ No newline at end of file
diff --git a/docs/images/mg_commalist.svg b/docs/images/mg_commalist.svg
deleted file mode 100644
index 9ea9fa44..00000000
--- a/docs/images/mg_commalist.svg
+++ /dev/null
@@ -1,97 +0,0 @@
-
-
-
diff --git a/docs/images/mg_http_get_request_len.svg b/docs/images/mg_http_get_request_len.svg
deleted file mode 100644
index 62d9abef..00000000
--- a/docs/images/mg_http_get_request_len.svg
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
diff --git a/docs/images/mg_http_message.svg b/docs/images/mg_http_message.svg
deleted file mode 100644
index e3db9f5b..00000000
--- a/docs/images/mg_http_message.svg
+++ /dev/null
@@ -1,113 +0,0 @@
-
-
-
diff --git a/docs/images/mg_http_next_multipart.svg b/docs/images/mg_http_next_multipart.svg
deleted file mode 100644
index ca7c301c..00000000
--- a/docs/images/mg_http_next_multipart.svg
+++ /dev/null
@@ -1,109 +0,0 @@
-
-
-
diff --git a/docs/images/mg_http_part.svg b/docs/images/mg_http_part.svg
deleted file mode 100644
index 711d2b93..00000000
--- a/docs/images/mg_http_part.svg
+++ /dev/null
@@ -1,94 +0,0 @@
-
-
-
diff --git a/docs/images/mg_http_reply.svg b/docs/images/mg_http_reply.svg
deleted file mode 100644
index c9241d3f..00000000
--- a/docs/images/mg_http_reply.svg
+++ /dev/null
@@ -1,84 +0,0 @@
-
-
-
diff --git a/docs/images/mg_iobuf.svg b/docs/images/mg_iobuf.svg
deleted file mode 100644
index 859d4c92..00000000
--- a/docs/images/mg_iobuf.svg
+++ /dev/null
@@ -1,124 +0,0 @@
-
-
-
diff --git a/docs/images/mg_iobuf_add1.svg b/docs/images/mg_iobuf_add1.svg
deleted file mode 100644
index b408b7a3..00000000
--- a/docs/images/mg_iobuf_add1.svg
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
diff --git a/docs/images/mg_iobuf_add2.svg b/docs/images/mg_iobuf_add2.svg
deleted file mode 100644
index e93fe8b2..00000000
--- a/docs/images/mg_iobuf_add2.svg
+++ /dev/null
@@ -1,185 +0,0 @@
-
-
-
diff --git a/docs/images/mg_iobuf_del.svg b/docs/images/mg_iobuf_del.svg
deleted file mode 100644
index 997f719e..00000000
--- a/docs/images/mg_iobuf_del.svg
+++ /dev/null
@@ -1,347 +0,0 @@
-
-
-
diff --git a/examples/arduino/w5500/w5500.ino b/examples/arduino/w5500/w5500.ino
index 2ede10b5..93959785 100644
--- a/examples/arduino/w5500/w5500.ino
+++ b/examples/arduino/w5500/w5500.ino
@@ -9,7 +9,7 @@ struct mg_tcpip_spi spi = {
[](void *) { digitalWrite(SS_PIN, HIGH); }, // end transaction
[](void *, uint8_t c) { return SPI.transfer(c); }, // execute transaction
};
-struct mg_tcpip_if mif = {.mac = {2, 0, 1, 2, 3, 5}}; // MIP network interface
+struct mg_tcpip_if mif = {.mac = {2, 0, 1, 2, 3, 5}}; // network interface
void setup() {
Serial.begin(115200);
diff --git a/examples/esp32/uart-bridge/README.md b/examples/esp32/uart-bridge/README.md
index f44d741f..6e60fad4 100644
--- a/examples/esp32/uart-bridge/README.md
+++ b/examples/esp32/uart-bridge/README.md
@@ -1,30 +1,10 @@
# A UART to network bridge for ESP32
-This example is a demonstration of how Mongoose Library could be integrated
-into an embedded device and provide a UART-to-Network bridge capability:
-
-- A device opens listening TCP port and Websocket port and waits for connections
-- When a client connects, data is exchanged with the device's UART
-- Everything that client send, is sent to the UART
-- Everything that is read from the UART, gets sent to the client
-- Multiple clients are allowed
-- Live UART console allows to talk to the UART from the web page
-- Web UI is hardcoded into the binary and does not need a filesystem
-
-# Screenshots
-

-# Build and flash
+- See detailed tutorial at https://mongoose.ws/tutorials/esp32/uart-bridge/
-Build requires Docker installed, and uses Espressif's ESP-IDF docker image:
-
-```sh
-make build
-make flash PORT=/dev/YOURSERIAL
-```
-
-# Flash pre-built firmware
+## Flash pre-built firmware
You can flash a pre-built firmware to the ESP32 device using the following
instructions:
diff --git a/examples/esp8266/http-client-server/README.md b/examples/esp8266/http-client-server/README.md
index 61fa661e..097cc2f5 100644
--- a/examples/esp8266/http-client-server/README.md
+++ b/examples/esp8266/http-client-server/README.md
@@ -1,22 +1 @@
-# An example ESP8266 application
-
-To build this application, follow these steps:
-
-1. Make sure you have Docker installed
-2. Clone whole mongoose repository on your machine:
- ```sh
- git clone https://github.com/cesanta/mongoose.git
- ```
-3. Start command line or terminal, and go into the example's directory:
- ```sh
- cd mongoose/examples/esp8266
- ```
-4. Run `make`. This invokes a Docker-based build. A firmware will be built
- in the `src/build/` directory:
- ```sh
- make build
- ```
-5. Flash your ESP8266. If you have esptool.py installed, the build process will end telling you the command to flash your device. You can just run `make` again to use Docker:
- ```sh
- make flash PORT=/your/serial
- ```
+See detailed tutorial at https://mongoose.ws/tutorials/esp8266/http-client-server/
\ No newline at end of file
diff --git a/examples/rp2040/pico-rmii/main.c b/examples/rp2040/pico-rmii/main.c
index 6ea96ea1..3c2ed5a3 100644
--- a/examples/rp2040/pico-rmii/main.c
+++ b/examples/rp2040/pico-rmii/main.c
@@ -24,10 +24,10 @@ int main(void) {
stdio_init_all();
struct mg_mgr mgr; // Initialise Mongoose event manager
- mg_mgr_init(&mgr); // and attach it to the MIP interface
+ mg_mgr_init(&mgr); // and attach it to the interface
mg_log_set(MG_LL_DEBUG); // Set log level
- MG_INFO(("Init MIP"));
+ MG_INFO(("Init TCP/IP"));
// Initialise Mongoose network stack and specific driver
// Set consecutive GPIOs for RMII (tx and rx) and SMI function groups
struct mg_tcpip_driver_rp2040_rmii_data driver_data = {
diff --git a/examples/rp2040/pico-rndis-dashboard/main.c b/examples/rp2040/pico-rndis-dashboard/main.c
index 1ebd3292..03b1af26 100644
--- a/examples/rp2040/pico-rndis-dashboard/main.c
+++ b/examples/rp2040/pico-rndis-dashboard/main.c
@@ -53,7 +53,7 @@ int main(void) {
stdio_init_all();
struct mg_mgr mgr; // Initialise Mongoose event manager
- mg_mgr_init(&mgr); // and attach it to the MIP interface
+ mg_mgr_init(&mgr); // and attach it to the interface
mg_timer_add(&mgr, 500, MG_TIMER_REPEAT, blink_cb, &mgr);
struct mg_tcpip_driver driver = {.tx = usb_tx, .up = usb_up};
diff --git a/examples/rp2040/pico-w5500/main.c b/examples/rp2040/pico-w5500/main.c
index 5b151801..e605c3e4 100644
--- a/examples/rp2040/pico-w5500/main.c
+++ b/examples/rp2040/pico-w5500/main.c
@@ -6,6 +6,7 @@
#include "hardware/spi.h"
#include "pico/rand.h"
#include "pico/stdlib.h"
+#include "pico/unique_id.h"
#include "mongoose.h"
@@ -31,42 +32,58 @@ void mg_random(void *buf, size_t len) {
}
}
-static void timer_cb(void *arg) {
- gpio_put(PICO_DEFAULT_LED_PIN, !gpio_get_out_level(PICO_DEFAULT_LED_PIN));
- bool up = ((struct mg_tcpip_if *) arg)->state == MIP_STATE_READY;
- MG_INFO(("Ethernet: %s", up ? "up" : "down")); // Show network status
+static void timer_fn(void *arg) {
+ gpio_put(PICO_DEFAULT_LED_PIN,
+ !gpio_get_out_level(PICO_DEFAULT_LED_PIN)); // Blink LED
+ struct mg_tcpip_if *ifp = arg; // And show
+ const char *names[] = {"down", "up", "ready"}; // network stats
+ MG_INFO(("Ethernet: %s, IP: %M, rx:%u, tx:%u, dr:%u, er:%u",
+ names[ifp->state], mg_print_ip4, &ifp->ip, ifp->nrecv, ifp->nsent,
+ ifp->ndrop, ifp->nerr));
}
-int main(void) {
- stdio_init_all();
- MG_INFO(("Starting ..."));
-
- // Init LED
- gpio_init(LED);
- gpio_set_dir(LED, GPIO_OUT);
-
+static void eth_spi_init(void) {
// Init SPI pins
spi_init(spi0, 500 * 1000);
gpio_set_function(SPI_RX, GPIO_FUNC_SPI); // MISO
gpio_set_function(SPI_TX, GPIO_FUNC_SPI); // MOSI
gpio_set_function(SPI_CLK, GPIO_FUNC_SPI); // CLK
gpio_init(SPI_CS); // CS
- gpio_set_dir(SPI_CS, GPIO_OUT); // Set CS it to output
+ gpio_set_dir(SPI_CS, GPIO_OUT); // Set CS to output
gpio_put(SPI_CS, 1); // And drive CS high (inactive)
+}
- // Init Mongoose
+// Helper macro for MAC generation
+#define GENERATE_LOCALLY_ADMINISTERED_MAC(id) \
+ { 2, id[3], id[4], id[5], id[6], id[7] }
+
+int main(void) {
+ stdio_init_all();
+ gpio_init(LED); // Setup board LED
+ gpio_set_dir(LED, GPIO_OUT);
+ eth_spi_init(); // Initialise SPI pins
+ MG_INFO(("Starting ..."));
+
+ pico_unique_board_id_t board_id;
+ pico_get_unique_board_id(&board_id);
+ uint8_t *id = board_id.id;
+
+ struct mg_mgr mgr; // Initialise
+ mg_mgr_init(&mgr); // Mongoose event manager
+ mg_log_set(MG_LL_DEBUG); // Set log level
+
+ // Initialise Mongoose network stack
+ // Specify MAC address, and IP/mask/GW in network byte order for static
+ // IP configuration. If IP/mask/GW are unset, DHCP is going to be used
struct mg_tcpip_spi spi = {NULL, spi_begin, spi_end, spi_txn};
- struct mg_tcpip_if mif = {.mac = {2, 0, 1, 2, 3, 5},
+ struct mg_tcpip_if mif = {.mac = GENERATE_LOCALLY_ADMINISTERED_MAC(id),
.driver = &mg_tcpip_driver_w5500,
.driver_data = &spi};
- struct mg_mgr mgr; // Declare event manager
- mg_mgr_init(&mgr); // Init event manager
- mg_log_set(MG_LL_DEBUG); // Set DEBUG log level
- mg_tcpip_init(&mgr, &mif); // Init TCP/IP stack
- mg_timer_add(&mgr, BLINK_PERIOD_MS, MG_TIMER_REPEAT, timer_cb, &mif);
+ mg_tcpip_init(&mgr, &mif);
+ mg_timer_add(&mgr, BLINK_PERIOD_MS, MG_TIMER_REPEAT, timer_fn, &mif);
- MG_INFO(("Waiting until network is up..."));
- while (mif.state != MIP_STATE_READY) {
+ MG_INFO(("MAC: %M. Waiting for IP...", mg_print_mac, mif.mac));
+ while (mif.state != MG_TCPIP_STATE_READY) {
mg_mgr_poll(&mgr, 0);
}
diff --git a/examples/stm32/nucleo-f429zi-baremetal/main.c b/examples/stm32/nucleo-f429zi-baremetal/main.c
index 91eb5477..755a537c 100644
--- a/examples/stm32/nucleo-f429zi-baremetal/main.c
+++ b/examples/stm32/nucleo-f429zi-baremetal/main.c
@@ -73,7 +73,7 @@ int main(void) {
mg_timer_add(&mgr, BLINK_PERIOD_MS, MG_TIMER_REPEAT, timer_fn, &mif);
MG_INFO(("MAC: %M. Waiting for IP...", mg_print_mac, mif.mac));
- while (mif.state != MIP_STATE_READY) {
+ while (mif.state != MG_TCPIP_STATE_READY) {
mg_mgr_poll(&mgr, 0);
}
diff --git a/examples/stm32/nucleo-f429zi-freertos/main.c b/examples/stm32/nucleo-f429zi-freertos/main.c
index 3e156311..0660828d 100644
--- a/examples/stm32/nucleo-f429zi-freertos/main.c
+++ b/examples/stm32/nucleo-f429zi-freertos/main.c
@@ -45,7 +45,7 @@ static void ethernet_init(void) {
static void server(void *args) {
struct mg_mgr mgr; // Initialise Mongoose event manager
- mg_mgr_init(&mgr); // and attach it to the MIP interface
+ mg_mgr_init(&mgr); // and attach it to the interface
mg_log_set(MG_LL_DEBUG); // Set log level
// Initialise Mongoose network stack
@@ -60,7 +60,7 @@ static void server(void *args) {
mg_timer_add(&mgr, BLINK_PERIOD_MS, MG_TIMER_REPEAT, timer_fn, &mif);
MG_INFO(("MAC: %M. Waiting for IP...", mg_print_mac, mif.mac));
- while (mif.state != MIP_STATE_READY) {
+ while (mif.state != MG_TCPIP_STATE_READY) {
mg_mgr_poll(&mgr, 0);
}
diff --git a/examples/stm32/nucleo-f429zi-freertos/mongoose_custom.h b/examples/stm32/nucleo-f429zi-freertos/mongoose_custom.h
index 53162794..6f0df4ff 100644
--- a/examples/stm32/nucleo-f429zi-freertos/mongoose_custom.h
+++ b/examples/stm32/nucleo-f429zi-freertos/mongoose_custom.h
@@ -1,7 +1,5 @@
#pragma once
-#include // we are not using lwIP
-
// See https://mongoose.ws/documentation/#build-options
#define MG_ARCH MG_ARCH_FREERTOS
#define MG_ENABLE_TCPIP 1
diff --git a/examples/stm32/nucleo-f746zg-baremetal/main.c b/examples/stm32/nucleo-f746zg-baremetal/main.c
index 91eb5477..755a537c 100644
--- a/examples/stm32/nucleo-f746zg-baremetal/main.c
+++ b/examples/stm32/nucleo-f746zg-baremetal/main.c
@@ -73,7 +73,7 @@ int main(void) {
mg_timer_add(&mgr, BLINK_PERIOD_MS, MG_TIMER_REPEAT, timer_fn, &mif);
MG_INFO(("MAC: %M. Waiting for IP...", mg_print_mac, mif.mac));
- while (mif.state != MIP_STATE_READY) {
+ while (mif.state != MG_TCPIP_STATE_READY) {
mg_mgr_poll(&mgr, 0);
}
diff --git a/examples/stm32/nucleo-f746zg-freertos/main.c b/examples/stm32/nucleo-f746zg-freertos/main.c
index 3e156311..0660828d 100644
--- a/examples/stm32/nucleo-f746zg-freertos/main.c
+++ b/examples/stm32/nucleo-f746zg-freertos/main.c
@@ -45,7 +45,7 @@ static void ethernet_init(void) {
static void server(void *args) {
struct mg_mgr mgr; // Initialise Mongoose event manager
- mg_mgr_init(&mgr); // and attach it to the MIP interface
+ mg_mgr_init(&mgr); // and attach it to the interface
mg_log_set(MG_LL_DEBUG); // Set log level
// Initialise Mongoose network stack
@@ -60,7 +60,7 @@ static void server(void *args) {
mg_timer_add(&mgr, BLINK_PERIOD_MS, MG_TIMER_REPEAT, timer_fn, &mif);
MG_INFO(("MAC: %M. Waiting for IP...", mg_print_mac, mif.mac));
- while (mif.state != MIP_STATE_READY) {
+ while (mif.state != MG_TCPIP_STATE_READY) {
mg_mgr_poll(&mgr, 0);
}
diff --git a/examples/stm32/nucleo-f746zg-freertos/mongoose_custom.h b/examples/stm32/nucleo-f746zg-freertos/mongoose_custom.h
index 53162794..6f0df4ff 100644
--- a/examples/stm32/nucleo-f746zg-freertos/mongoose_custom.h
+++ b/examples/stm32/nucleo-f746zg-freertos/mongoose_custom.h
@@ -1,7 +1,5 @@
#pragma once
-#include // we are not using lwIP
-
// See https://mongoose.ws/documentation/#build-options
#define MG_ARCH MG_ARCH_FREERTOS
#define MG_ENABLE_TCPIP 1
diff --git a/examples/stm32/nucleo-h743zi-baremetal/Makefile b/examples/stm32/nucleo-h743zi-baremetal/Makefile
index d63f5781..1efbad53 100644
--- a/examples/stm32/nucleo-h743zi-baremetal/Makefile
+++ b/examples/stm32/nucleo-h743zi-baremetal/Makefile
@@ -8,7 +8,7 @@ LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs nano.specs -lc -lgcc -Wl,--
SOURCES = main.c syscalls.c sysinit.c
SOURCES += cmsis_h7/Source/Templates/gcc/startup_stm32h743xx.s # ST startup file. Compiler-dependent!
-# Mongoose-specific source code files and build options. See https://mongoose.ws/documentation/#build-options
+# Mongoose-specific. See https://mongoose.ws/documentation/#build-options
SOURCES += mongoose.c net.c packed_fs.c
CFLAGS += -DMG_ENABLE_TCPIP=1 -DMG_ARCH=MG_ARCH_NEWLIB -DMG_ENABLE_PACKED_FS=1
CFLAGS += -DMG_ENABLE_CUSTOM_RANDOM=1 -DMG_ENABLE_CUSTOM_MILLIS=1
@@ -41,11 +41,11 @@ DEVICE_URL ?= https://dash.vcon.io/api/v3/devices/6
update: firmware.bin
curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/ota --data-binary @$<
-update test: CFLAGS_EXTRA += -DUART_DEBUG=USART1
+test update: CFLAGS_EXTRA += -DUART_DEBUG=USART1
test: update
curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=5 | tee /tmp/output.txt
grep 'READY, IP:' /tmp/output.txt # Check for network init
grep 'MQTT connected' /tmp/output.txt # Check for MQTT connection success
clean:
- @rm -rf firmware.* *.su cmsis_core cmsis_h7
+ $(RM) firmware.* *.su cmsis_core cmsis_h7
diff --git a/examples/stm32/nucleo-h743zi-baremetal/main.c b/examples/stm32/nucleo-h743zi-baremetal/main.c
index 893f2f71..3ca94030 100644
--- a/examples/stm32/nucleo-h743zi-baremetal/main.c
+++ b/examples/stm32/nucleo-h743zi-baremetal/main.c
@@ -75,7 +75,7 @@ int main(void) {
mg_timer_add(&mgr, BLINK_PERIOD_MS, MG_TIMER_REPEAT, timer_fn, &mif);
MG_INFO(("MAC: %M. Waiting for IP...", mg_print_mac, mif.mac));
- while (mif.state != MIP_STATE_READY) {
+ while (mif.state != MG_TCPIP_STATE_READY) {
mg_mgr_poll(&mgr, 0);
}
diff --git a/examples/ti/ek-tm4c1294xl-baremetal/Makefile b/examples/ti/ek-tm4c1294xl-baremetal/Makefile
index f68f8c5c..4d3ca4f8 100644
--- a/examples/ti/ek-tm4c1294xl-baremetal/Makefile
+++ b/examples/ti/ek-tm4c1294xl-baremetal/Makefile
@@ -40,11 +40,11 @@ DEVICE_URL ?= https://dash.vcon.io/api/v3/devices/1
update: firmware.bin
curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/ota --data-binary @$<
-test update: CFLAGS_EXTRA += -DUART_DEBUG=UART1
+test update: CFLAGS_EXTRA += -DUART_DEBUG=UART0
test: update
curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=5 | tee /tmp/output.txt
grep 'READY, IP:' /tmp/output.txt # Check for network init
- grep 'MQTT connected' /tmp/output.txt # Check for MQTT connection success
+# grep 'MQTT connected' /tmp/output.txt # Check for MQTT connection success
clean:
$(RM) firmware.* *.su cmsis_core cmsis_tm4c
diff --git a/examples/ti/ek-tm4c1294xl-baremetal/README.md b/examples/ti/ek-tm4c1294xl-baremetal/README.md
index 050971c0..be7e35e7 100644
--- a/examples/ti/ek-tm4c1294xl-baremetal/README.md
+++ b/examples/ti/ek-tm4c1294xl-baremetal/README.md
@@ -1,47 +1 @@
-# Baremetal webserver on EK-TM4C1294XL
-
-This firmware uses experimental TCP/IP stack of the Mongoose Network Library,
-which implements the following:
-
-- A complete [HTTP device dashboard](../../device-dashboard) with:
- - User authentication
- - Real-time device data graph
- - Coninfiguration display and update
- - MQTT communication with a remote MQTT server
-- No dependencies: no HAL, no CMSIS, no RTOS
-- Hand-written [mcu.h](mcu.h) header based on the [datasheet](https://www.ti.com/lit/pdf/spms433)
-- Interrupt-driven Ethernet driver
-- LED blinky, based on SysTick interrupt
-- User button handler, turns off/on an LED, based on GPIO IRQs (interrupt-driven)
-- HardFault handler alternate blinks LEDs
-- Debug log on UART0 (ICDI)
-
-## Requirements
-
-- [GNU make](http://mongoose.ws/tutorials/tools/#gnu-make)
-- [ARM GCC](http://mongoose.ws/tutorials/tools/#arm-gcc)
-- [Uniflash](https://mongoose.ws/tutorials/ti/ek-tm4c1294xl-baremetal/#build-and-run) for flashing
-
-The links above will send you to tutorials on how to install each of those tools in your workstation for Linux, Mac, and Windows.
-
-
-## Usage
-
-Plugin your board into USB, and attach an Ethernet cable.
-To build:
-
-```sh
-$ make clean build
-```
-
-To flash: use Uniflash
-
-To see debug log, use any serial monitor program like `picocom` at 115200 bps and configure it to insert carriage returns after line feeds:
-
-```sh
-$ picocom /dev/ttyACM0 -i -b 115200 --imap=lfcrlf
-```
-
-There is also a [detailed tutorial on this example](https://mongoose.ws/tutorials/ti/ek-tm4c1294xl-baremetal/)
-
-For more details and benchmark data on MIP, check the [F746ZG example](../../stm32/nucleo-f746zg-baremetal/)
+See detailed tutorial at https://mongoose.ws/tutorials/ti/ek-tm4c1294xl-baremetal/
diff --git a/examples/ti/ek-tm4c1294xl-baremetal/hal.h b/examples/ti/ek-tm4c1294xl-baremetal/hal.h
index 457d2999..d116f7ed 100644
--- a/examples/ti/ek-tm4c1294xl-baremetal/hal.h
+++ b/examples/ti/ek-tm4c1294xl-baremetal/hal.h
@@ -133,22 +133,18 @@ static inline void gpio_irq_attach(uint16_t pin) {
#define UARTNO(u) ((uint8_t) (((unsigned int) (u) -UART0_BASE) / UART_OFFSET))
static inline void uart_init(UART0_Type *uart, unsigned long baud) {
- struct uarthw {
- uint16_t rx, tx; // pins
- uint8_t af; // Alternate function
- };
- // rx, tx, af for UART0,1,2
- struct uarthw uarthw[3] = {{PIN('A', 0), PIN('A', 1), 1},
- {PIN('B', 0), PIN('B', 1), 1},
- {PIN('A', 6), PIN('A', 7), 1}}; // or PD4, PD5...
-
+ uint8_t af = 1; // Alternate function
+ uint16_t rx = 0, tx = 0; // pins
uint8_t uartno = UARTNO(uart);
+
SYSCTL->RCGCUART |= BIT(uartno); // Enable peripheral clock
- gpio_init(uarthw[uartno].tx, GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL,
- GPIO_SPEED_HIGH, 0, uarthw[uartno].af);
- gpio_init(uarthw[uartno].rx, GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL,
- GPIO_SPEED_HIGH, 0, uarthw[uartno].af);
+ if (uart == UART0) tx = PIN('A', 0), rx = PIN('A', 1);
+ if (uart == UART1) tx = PIN('B', 0), rx = PIN('B', 1);
+ if (uart == UART2) tx = PIN('A', 6), rx = PIN('A', 7); // or PD4, PD5...
+
+ gpio_init(tx, GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, 0, af);
+ gpio_init(rx, GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, 0, af);
// (16.3.2) ClkDiv = 16 (HSE=0)
// BRD = BRDI + BRDF = UARTSysClk / (ClkDiv * Baud Rate)
// UARTFBRD[DIVFRAC] = integer(BRDF * 64 + 0.5)
diff --git a/examples/ti/ek-tm4c1294xl-baremetal/main.c b/examples/ti/ek-tm4c1294xl-baremetal/main.c
index f5221b8b..388fe8ce 100644
--- a/examples/ti/ek-tm4c1294xl-baremetal/main.c
+++ b/examples/ti/ek-tm4c1294xl-baremetal/main.c
@@ -84,7 +84,7 @@ int main(void) {
mg_timer_add(&mgr, BLINK_PERIOD_MS, MG_TIMER_REPEAT, timer_fn, &mif);
MG_INFO(("MAC: %M. Waiting for IP...", mg_print_mac, mif.mac));
- while (mif.state != MIP_STATE_READY) {
+ while (mif.state != MG_TCPIP_STATE_READY) {
mg_mgr_poll(&mgr, 0);
}
diff --git a/examples/ti/ek-tm4c1294xl-freertos/Makefile b/examples/ti/ek-tm4c1294xl-freertos/Makefile
index f1b7a6db..5470429f 100644
--- a/examples/ti/ek-tm4c1294xl-freertos/Makefile
+++ b/examples/ti/ek-tm4c1294xl-freertos/Makefile
@@ -46,11 +46,11 @@ DEVICE_URL ?= https://dash.vcon.io/api/v3/devices/1
update: firmware.bin
curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/ota --data-binary @$<
-test update: CFLAGS_EXTRA += -DUART_DEBUG=UART1
+test update: CFLAGS_EXTRA += -DUART_DEBUG=UART0
test: update
curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=5 | tee /tmp/output.txt
grep 'READY, IP:' /tmp/output.txt # Check for network init
- grep 'MQTT connected' /tmp/output.txt # Check for MQTT connection success
+# grep 'MQTT connected' /tmp/output.txt # Check for MQTT connection success
clean:
$(RM) firmware.* *.su cmsis_core cmsis_tm4c FreeRTOS-Kernel
diff --git a/examples/ti/ek-tm4c1294xl-freertos/main.c b/examples/ti/ek-tm4c1294xl-freertos/main.c
index 74279af8..48cb1012 100644
--- a/examples/ti/ek-tm4c1294xl-freertos/main.c
+++ b/examples/ti/ek-tm4c1294xl-freertos/main.c
@@ -48,7 +48,7 @@ static void ethernet_init(void) {
static void server(void *args) {
struct mg_mgr mgr; // Initialise Mongoose event manager
- mg_mgr_init(&mgr); // and attach it to the MIP interface
+ mg_mgr_init(&mgr); // and attach it to the interface
mg_log_set(MG_LL_DEBUG); // Set log level
// Initialise Mongoose network stack
@@ -69,7 +69,7 @@ static void server(void *args) {
mg_timer_add(&mgr, BLINK_PERIOD_MS, MG_TIMER_REPEAT, timer_fn, &mif);
MG_INFO(("MAC: %M. Waiting for IP...", mg_print_mac, mif.mac));
- while (mif.state != MIP_STATE_READY) {
+ while (mif.state != MG_TCPIP_STATE_READY) {
mg_mgr_poll(&mgr, 0);
}
diff --git a/examples/ti/ek-tm4c1294xl-freertos/mongoose_custom.h b/examples/ti/ek-tm4c1294xl-freertos/mongoose_custom.h
index 96150766..4b2bdf97 100644
--- a/examples/ti/ek-tm4c1294xl-freertos/mongoose_custom.h
+++ b/examples/ti/ek-tm4c1294xl-freertos/mongoose_custom.h
@@ -1,7 +1,5 @@
#pragma once
-#include // we are not using lwIP
-
// See https://mongoose.ws/documentation/#build-options
#define MG_ARCH MG_ARCH_FREERTOS
#define MG_ENABLE_TCPIP 1
diff --git a/examples/zephyr/README.md b/examples/zephyr/README.md
index 674a7a41..7d2078c5 100644
--- a/examples/zephyr/README.md
+++ b/examples/zephyr/README.md
@@ -1,24 +1,15 @@
# Zephyr Project examples
-## Building
+- See detailed tutorial at https://mongoose.ws/tutorials/zephyr/device-dashboard/
-- Run `make zephyr`, this will clone the Zephyr Project repo or update it if you've already done it before
-- Now you can build the project, follow one of this options:
+The tutorial is specific for the Device Dashboard example but [build instructions](https://mongoose.ws/tutorials/zephyr/device-dashboard/#build-and-run) are generic
-### Trying on a simulated board
-The default board is an x86 machine that can be simulated with QEMU, and runs inside a _Docker_ container, you only need to have _Docker_ installed. Its Ethernet is also simulated and uses the TUN/TAP interface.
-You'll need a TUN/TAP interface (default name is _tap0_) on your host machine; follow the instructions [here](https://github.com/cesanta/mongoose/tree/master/examples/mip-tap#linux-setup) to configure your network to provide a suitable networking environment.
-- Run `make build`. The interface configuration on the simulated machine side is in the file _overlay-e1000.conf_, and defaults to _tap0_. You can change this overlay file adding an `OVERLAY` argument (e.g.: *make build OVERLAY="-DOVERLAY_CONFIG=something"*).
-- Once the build succeeds, run `make run` to run the target. The default _Docker_ configuration is to share _/dev/net/tun_; if this does not work for you, pass the necessary _Docker_ parameters inside `SHARETUN` (e.g.: *make run SHARETUN=something*).
+## Optional: Using a simulated board
-### Using a real board
+For testing purposes, we build for an x86 target that can be simulated with QEMU, and runs inside a _Docker_ container. Its Ethernet interface is also simulated, and uses the TUN/TAP interface. You can use this to try the examples or for your own development with Zephyr, without the need to have an actual board connected; you only need to have _Docker_ installed as the Zephyr Docker image handles the emulation for you.
-- Run `make build BOARD=yourboardname` (e.g.: *make build BOARD=nucleo_f746zg*)
-- Once the build succeeds, run `make flash`. The Makefile shares the USB bus with the _Docker_ container, this works well with ST-Link and J-Link devices. If this does not work for you, pass the necessary _Docker_ parameters inside `SHAREUSB` (e.g.: *make flash SHAREUSB=something*)
-- The generated ELF file is at _build/zephyr_
-
-## Cleaning up
-
-- You can run `make clean` to clean up the build files but keep the configuration files, which speeds the next build
-- If you do major changes (like compiling for a different board...), run `make pristine` to clean up everything under the _build_ directory.
+- You'll need a TUN/TAP interface (default name is _tap0_) on your host machine; follow the instructions [here](https://github.com/cesanta/mongoose/tree/master/examples/mip-tap#linux-setup) to configure your network to provide a suitable networking environment.
+- Run `make build BUILD_ARGS="-b qemu_x86" OVERLAY="-DOVERLAY_CONFIG=overlay-e1000.conf"`.
+ - The interface configuration on the simulated machine side is in the file _overlay-e1000.conf_, and defaults to _tap0_. You can change this file or specify a different one by changing the `OVERLAY_CONFIG` argument above.
+- Once the build succeeds, run `make run` to run the target. The default _Docker_ configuration is to share _/dev/net/tun_; if this does not work for you, pass the necessary _Docker_ parameters to _make_ with a `DOCKER_ARGS=` argument.
diff --git a/examples/zephyr/device-dashboard/Makefile b/examples/zephyr/device-dashboard/Makefile
index 59d4f872..c198e649 100644
--- a/examples/zephyr/device-dashboard/Makefile
+++ b/examples/zephyr/device-dashboard/Makefile
@@ -1,61 +1,46 @@
-MONGOOSE_ROOT ?= $(realpath $(CURDIR)/../../..)
-DOCKER_PROJECT_DIR ?= /workdir
-ZEPHYR_DIR ?= zephyrproject
-PROJECT_NAME = $(notdir $(CURDIR))
-PROJECT_PATH = $(realpath $(CURDIR))
-ZEPHYR_PATH = $(realpath $(CURDIR)/..)/$(ZEPHYR_DIR)
-DOCKER_PROJECT_PATH = $(DOCKER_PROJECT_DIR)/$(PROJECT_NAME)
-DOCKER_ZEPHYR_PATH = $(DOCKER_PROJECT_DIR)/$(ZEPHYR_DIR)
-
-BOARD ?= qemu_x86
-ifeq "$(BOARD)" "qemu_x86"
-OVERLAY ?= -DOVERLAY_CONFIG=overlay-e1000.conf
-else
-OVERLAY ?=
-endif
-
-SHAREUSB ?= --privileged -v /dev/bus/usb/:/dev/bus/usb
-SHARETUN ?= --cap-add=NET_ADMIN --device /dev/net/tun --net=host
-DOCKER ?= docker run --rm -v $(PROJECT_PATH):$(DOCKER_PROJECT_PATH) -v $(ZEPHYR_PATH):$(DOCKER_ZEPHYR_PATH)
-REPO ?= zephyrprojectrtos/ci
-
+TOP_DIR ?= $(realpath $(CURDIR)/../../..)
+ZEPHYR_DIR ?= $(realpath $(CURDIR)/..)/zephyrproject
+DOCKER ?= docker run --rm \
+ -v $(TOP_DIR):$(TOP_DIR) \
+ -w $(realpath $(CURDIR)) \
+ -e ZEPHYR_BASE=$(ZEPHYR_DIR)/zephyr \
+ $(DOCKER_ARGS) zephyrprojectrtos/ci
+BUILD_ARGS ?= -b nucleo_f746zg
+.PHONY: build
example:
true
-build:
- cp $(MONGOOSE_ROOT)/mongoose.[ch] src/
- cp $(MONGOOSE_ROOT)/examples/device-dashboard/net.c src/
- cp $(MONGOOSE_ROOT)/examples/device-dashboard/packed_fs.c src/
- $(DOCKER) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH)/zephyr && \
- west build -b $(BOARD) -p auto $(DOCKER_PROJECT_PATH) $(OVERLAY) --build-dir $(DOCKER_PROJECT_PATH)/build'
+zephyr:
+ test -d $(ZEPHYR_DIR) || ( mkdir -p $(ZEPHYR_DIR) ; $(DOCKER) west init $(ZEPHYR_DIR))
+ $(DOCKER) sh -c 'cd $(ZEPHYR_DIR) && west update'
+build:
+ cp $(TOP_DIR)/mongoose.[ch] src/
+ cp $(TOP_DIR)/examples/device-dashboard/net.c src/
+ cp $(TOP_DIR)/examples/device-dashboard/packed_fs.c src/
+ $(DOCKER) west build $(BUILD_ARGS) -p auto $(realpath $(CURDIR)) $(OVERLAY)
+
+run: DOCKER_ARGS ?= --cap-add=NET_ADMIN --device /dev/net/tun --net=host -it
run:
- $(DOCKER) -it $(SHARETUN) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH) && \
- west build -t run --build-dir $(DOCKER_PROJECT_PATH)/build'
+ $(DOCKER) west build -t run
clean:
- $(DOCKER) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH) && \
- west build -t clean --build-dir $(DOCKER_PROJECT_PATH)/build'
+ -$(DOCKER) west build -t clean
pristine:
- $(DOCKER) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH) && \
- west build -t pristine --build-dir $(DOCKER_PROJECT_PATH)/build'
+ -$(DOCKER) west build -t pristine
+purge: clean pristine
+ rm -rf $(TOP_DIR)/.west $(ZEPHYR_DIR) build
+flash debug: DOCKER_ARGS ?= --privileged -v /dev/bus/usb/:/dev/bus/usb -it
flash:
- $(DOCKER) $(SHAREUSB) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH) && \
- west flash --build-dir $(DOCKER_PROJECT_PATH)/build'
+ $(DOCKER) west flash --build-dir $(realpath $(CURDIR))/build
debug:
- $(DOCKER) -it $(SHAREUSB) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH) && \
- west debug --build-dir $(DOCKER_PROJECT_PATH)/build'
+ $(DOCKER) west debug --build-dir $(realpath $(CURDIR))/build
-.PHONY: build flash zephyr clean pristine run
-
-zephyr:
-ifeq ($(wildcard $(ZEPHYR_PATH)/.*),)
- mkdir $(ZEPHYR_PATH)
- $(DOCKER) $(REPO) /bin/sh -c 'cd $(DOCKER_PROJECT_DIR) && west init ./$(ZEPHYR_DIR)'
-endif
- $(DOCKER) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH) && west update'
+test: BUILD_ARGS = -b qemu_x86
+test: OVERLAY = -DOVERLAY_CONFIG=overlay-e1000.conf
+test: build run
diff --git a/examples/zephyr/http-client/Makefile b/examples/zephyr/http-client/Makefile
index 2b0fa519..e5aa7fff 100644
--- a/examples/zephyr/http-client/Makefile
+++ b/examples/zephyr/http-client/Makefile
@@ -5,8 +5,7 @@ DOCKER ?= docker run --rm \
-w $(realpath $(CURDIR)) \
-e ZEPHYR_BASE=$(ZEPHYR_DIR)/zephyr \
$(DOCKER_ARGS) zephyrprojectrtos/ci
-#BUILD_ARGS ?= -b qemu_x86 -DOVERLAY_CONFIG=overlay-e1000.conf
-BUILD_ARGS ?= -b nucleo_f429zi
+BUILD_ARGS ?= -b nucleo_f746zg
.PHONY: build
example:
@@ -18,7 +17,7 @@ zephyr:
build:
cp $(TOP_DIR)/mongoose.[ch] src/
- $(DOCKER) west build $(BUILD_ARGS) -p auto $(realpath $(CURDIR))
+ $(DOCKER) west build $(BUILD_ARGS) -p auto $(realpath $(CURDIR)) $(OVERLAY)
run: DOCKER_ARGS ?= --cap-add=NET_ADMIN --device /dev/net/tun --net=host -it
run:
@@ -39,3 +38,7 @@ flash:
debug:
$(DOCKER) west debug --build-dir $(realpath $(CURDIR))/build
+
+test: BUILD_ARGS = -b qemu_x86
+test: OVERLAY = -DOVERLAY_CONFIG=overlay-e1000.conf
+test: build run
diff --git a/examples/zephyr/http-server/Makefile b/examples/zephyr/http-server/Makefile
index be7a4cb9..e5aa7fff 100644
--- a/examples/zephyr/http-server/Makefile
+++ b/examples/zephyr/http-server/Makefile
@@ -1,59 +1,44 @@
-MONGOOSE_ROOT ?= $(realpath $(CURDIR)/../../..)
-DOCKER_PROJECT_DIR ?= /workdir
-ZEPHYR_DIR ?= zephyrproject
-PROJECT_NAME = $(notdir $(CURDIR))
-PROJECT_PATH = $(realpath $(CURDIR))
-ZEPHYR_PATH = $(realpath $(CURDIR)/..)/$(ZEPHYR_DIR)
-DOCKER_PROJECT_PATH = $(DOCKER_PROJECT_DIR)/$(PROJECT_NAME)
-DOCKER_ZEPHYR_PATH = $(DOCKER_PROJECT_DIR)/$(ZEPHYR_DIR)
-
-BOARD ?= qemu_x86
-ifeq "$(BOARD)" "qemu_x86"
-OVERLAY ?= -DOVERLAY_CONFIG=overlay-e1000.conf
-else
-OVERLAY ?=
-endif
-
-SHAREUSB ?= --privileged -v /dev/bus/usb/:/dev/bus/usb
-SHARETUN ?= --cap-add=NET_ADMIN --device /dev/net/tun --net=host
-DOCKER ?= docker run --rm -v $(PROJECT_PATH):$(DOCKER_PROJECT_PATH) -v $(ZEPHYR_PATH):$(DOCKER_ZEPHYR_PATH)
-REPO ?= zephyrprojectrtos/ci
-
+TOP_DIR ?= $(realpath $(CURDIR)/../../..)
+ZEPHYR_DIR ?= $(realpath $(CURDIR)/..)/zephyrproject
+DOCKER ?= docker run --rm \
+ -v $(TOP_DIR):$(TOP_DIR) \
+ -w $(realpath $(CURDIR)) \
+ -e ZEPHYR_BASE=$(ZEPHYR_DIR)/zephyr \
+ $(DOCKER_ARGS) zephyrprojectrtos/ci
+BUILD_ARGS ?= -b nucleo_f746zg
+.PHONY: build
example:
true
-build:
- cp $(MONGOOSE_ROOT)/mongoose.[ch] src/
- $(DOCKER) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH)/zephyr && \
- west build -b $(BOARD) -p auto $(DOCKER_PROJECT_PATH) $(OVERLAY) --build-dir $(DOCKER_PROJECT_PATH)/build'
+zephyr:
+ test -d $(ZEPHYR_DIR) || ( mkdir -p $(ZEPHYR_DIR) ; $(DOCKER) west init $(ZEPHYR_DIR))
+ $(DOCKER) sh -c 'cd $(ZEPHYR_DIR) && west update'
+build:
+ cp $(TOP_DIR)/mongoose.[ch] src/
+ $(DOCKER) west build $(BUILD_ARGS) -p auto $(realpath $(CURDIR)) $(OVERLAY)
+
+run: DOCKER_ARGS ?= --cap-add=NET_ADMIN --device /dev/net/tun --net=host -it
run:
- $(DOCKER) -it $(SHARETUN) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH) && \
- west build -t run --build-dir $(DOCKER_PROJECT_PATH)/build'
+ $(DOCKER) west build -t run
clean:
- $(DOCKER) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH) && \
- west build -t clean --build-dir $(DOCKER_PROJECT_PATH)/build'
+ -$(DOCKER) west build -t clean
pristine:
- $(DOCKER) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH) && \
- west build -t pristine --build-dir $(DOCKER_PROJECT_PATH)/build'
+ -$(DOCKER) west build -t pristine
+purge: clean pristine
+ rm -rf $(TOP_DIR)/.west $(ZEPHYR_DIR) build
+flash debug: DOCKER_ARGS ?= --privileged -v /dev/bus/usb/:/dev/bus/usb -it
flash:
- $(DOCKER) $(SHAREUSB) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH) && \
- west flash --build-dir $(DOCKER_PROJECT_PATH)/build'
+ $(DOCKER) west flash --build-dir $(realpath $(CURDIR))/build
debug:
- $(DOCKER) -it $(SHAREUSB) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH) && \
- west debug --build-dir $(DOCKER_PROJECT_PATH)/build'
+ $(DOCKER) west debug --build-dir $(realpath $(CURDIR))/build
-.PHONY: build flash zephyr clean pristine run
-
-zephyr:
-ifeq ($(wildcard $(ZEPHYR_PATH)/.*),)
- mkdir $(ZEPHYR_PATH)
- $(DOCKER) $(REPO) /bin/sh -c 'cd $(DOCKER_PROJECT_DIR) && west init ./$(ZEPHYR_DIR)'
-endif
- $(DOCKER) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH) && west update'
+test: BUILD_ARGS = -b qemu_x86
+test: OVERLAY = -DOVERLAY_CONFIG=overlay-e1000.conf
+test: build run
diff --git a/examples/zephyr/mqtt-aws-client/Makefile b/examples/zephyr/mqtt-aws-client/Makefile
index be7a4cb9..e5aa7fff 100644
--- a/examples/zephyr/mqtt-aws-client/Makefile
+++ b/examples/zephyr/mqtt-aws-client/Makefile
@@ -1,59 +1,44 @@
-MONGOOSE_ROOT ?= $(realpath $(CURDIR)/../../..)
-DOCKER_PROJECT_DIR ?= /workdir
-ZEPHYR_DIR ?= zephyrproject
-PROJECT_NAME = $(notdir $(CURDIR))
-PROJECT_PATH = $(realpath $(CURDIR))
-ZEPHYR_PATH = $(realpath $(CURDIR)/..)/$(ZEPHYR_DIR)
-DOCKER_PROJECT_PATH = $(DOCKER_PROJECT_DIR)/$(PROJECT_NAME)
-DOCKER_ZEPHYR_PATH = $(DOCKER_PROJECT_DIR)/$(ZEPHYR_DIR)
-
-BOARD ?= qemu_x86
-ifeq "$(BOARD)" "qemu_x86"
-OVERLAY ?= -DOVERLAY_CONFIG=overlay-e1000.conf
-else
-OVERLAY ?=
-endif
-
-SHAREUSB ?= --privileged -v /dev/bus/usb/:/dev/bus/usb
-SHARETUN ?= --cap-add=NET_ADMIN --device /dev/net/tun --net=host
-DOCKER ?= docker run --rm -v $(PROJECT_PATH):$(DOCKER_PROJECT_PATH) -v $(ZEPHYR_PATH):$(DOCKER_ZEPHYR_PATH)
-REPO ?= zephyrprojectrtos/ci
-
+TOP_DIR ?= $(realpath $(CURDIR)/../../..)
+ZEPHYR_DIR ?= $(realpath $(CURDIR)/..)/zephyrproject
+DOCKER ?= docker run --rm \
+ -v $(TOP_DIR):$(TOP_DIR) \
+ -w $(realpath $(CURDIR)) \
+ -e ZEPHYR_BASE=$(ZEPHYR_DIR)/zephyr \
+ $(DOCKER_ARGS) zephyrprojectrtos/ci
+BUILD_ARGS ?= -b nucleo_f746zg
+.PHONY: build
example:
true
-build:
- cp $(MONGOOSE_ROOT)/mongoose.[ch] src/
- $(DOCKER) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH)/zephyr && \
- west build -b $(BOARD) -p auto $(DOCKER_PROJECT_PATH) $(OVERLAY) --build-dir $(DOCKER_PROJECT_PATH)/build'
+zephyr:
+ test -d $(ZEPHYR_DIR) || ( mkdir -p $(ZEPHYR_DIR) ; $(DOCKER) west init $(ZEPHYR_DIR))
+ $(DOCKER) sh -c 'cd $(ZEPHYR_DIR) && west update'
+build:
+ cp $(TOP_DIR)/mongoose.[ch] src/
+ $(DOCKER) west build $(BUILD_ARGS) -p auto $(realpath $(CURDIR)) $(OVERLAY)
+
+run: DOCKER_ARGS ?= --cap-add=NET_ADMIN --device /dev/net/tun --net=host -it
run:
- $(DOCKER) -it $(SHARETUN) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH) && \
- west build -t run --build-dir $(DOCKER_PROJECT_PATH)/build'
+ $(DOCKER) west build -t run
clean:
- $(DOCKER) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH) && \
- west build -t clean --build-dir $(DOCKER_PROJECT_PATH)/build'
+ -$(DOCKER) west build -t clean
pristine:
- $(DOCKER) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH) && \
- west build -t pristine --build-dir $(DOCKER_PROJECT_PATH)/build'
+ -$(DOCKER) west build -t pristine
+purge: clean pristine
+ rm -rf $(TOP_DIR)/.west $(ZEPHYR_DIR) build
+flash debug: DOCKER_ARGS ?= --privileged -v /dev/bus/usb/:/dev/bus/usb -it
flash:
- $(DOCKER) $(SHAREUSB) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH) && \
- west flash --build-dir $(DOCKER_PROJECT_PATH)/build'
+ $(DOCKER) west flash --build-dir $(realpath $(CURDIR))/build
debug:
- $(DOCKER) -it $(SHAREUSB) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH) && \
- west debug --build-dir $(DOCKER_PROJECT_PATH)/build'
+ $(DOCKER) west debug --build-dir $(realpath $(CURDIR))/build
-.PHONY: build flash zephyr clean pristine run
-
-zephyr:
-ifeq ($(wildcard $(ZEPHYR_PATH)/.*),)
- mkdir $(ZEPHYR_PATH)
- $(DOCKER) $(REPO) /bin/sh -c 'cd $(DOCKER_PROJECT_DIR) && west init ./$(ZEPHYR_DIR)'
-endif
- $(DOCKER) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH) && west update'
+test: BUILD_ARGS = -b qemu_x86
+test: OVERLAY = -DOVERLAY_CONFIG=overlay-e1000.conf
+test: build run
diff --git a/examples/zephyr/websocket-server/Makefile b/examples/zephyr/websocket-server/Makefile
index be7a4cb9..e5aa7fff 100644
--- a/examples/zephyr/websocket-server/Makefile
+++ b/examples/zephyr/websocket-server/Makefile
@@ -1,59 +1,44 @@
-MONGOOSE_ROOT ?= $(realpath $(CURDIR)/../../..)
-DOCKER_PROJECT_DIR ?= /workdir
-ZEPHYR_DIR ?= zephyrproject
-PROJECT_NAME = $(notdir $(CURDIR))
-PROJECT_PATH = $(realpath $(CURDIR))
-ZEPHYR_PATH = $(realpath $(CURDIR)/..)/$(ZEPHYR_DIR)
-DOCKER_PROJECT_PATH = $(DOCKER_PROJECT_DIR)/$(PROJECT_NAME)
-DOCKER_ZEPHYR_PATH = $(DOCKER_PROJECT_DIR)/$(ZEPHYR_DIR)
-
-BOARD ?= qemu_x86
-ifeq "$(BOARD)" "qemu_x86"
-OVERLAY ?= -DOVERLAY_CONFIG=overlay-e1000.conf
-else
-OVERLAY ?=
-endif
-
-SHAREUSB ?= --privileged -v /dev/bus/usb/:/dev/bus/usb
-SHARETUN ?= --cap-add=NET_ADMIN --device /dev/net/tun --net=host
-DOCKER ?= docker run --rm -v $(PROJECT_PATH):$(DOCKER_PROJECT_PATH) -v $(ZEPHYR_PATH):$(DOCKER_ZEPHYR_PATH)
-REPO ?= zephyrprojectrtos/ci
-
+TOP_DIR ?= $(realpath $(CURDIR)/../../..)
+ZEPHYR_DIR ?= $(realpath $(CURDIR)/..)/zephyrproject
+DOCKER ?= docker run --rm \
+ -v $(TOP_DIR):$(TOP_DIR) \
+ -w $(realpath $(CURDIR)) \
+ -e ZEPHYR_BASE=$(ZEPHYR_DIR)/zephyr \
+ $(DOCKER_ARGS) zephyrprojectrtos/ci
+BUILD_ARGS ?= -b nucleo_f746zg
+.PHONY: build
example:
true
-build:
- cp $(MONGOOSE_ROOT)/mongoose.[ch] src/
- $(DOCKER) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH)/zephyr && \
- west build -b $(BOARD) -p auto $(DOCKER_PROJECT_PATH) $(OVERLAY) --build-dir $(DOCKER_PROJECT_PATH)/build'
+zephyr:
+ test -d $(ZEPHYR_DIR) || ( mkdir -p $(ZEPHYR_DIR) ; $(DOCKER) west init $(ZEPHYR_DIR))
+ $(DOCKER) sh -c 'cd $(ZEPHYR_DIR) && west update'
+build:
+ cp $(TOP_DIR)/mongoose.[ch] src/
+ $(DOCKER) west build $(BUILD_ARGS) -p auto $(realpath $(CURDIR)) $(OVERLAY)
+
+run: DOCKER_ARGS ?= --cap-add=NET_ADMIN --device /dev/net/tun --net=host -it
run:
- $(DOCKER) -it $(SHARETUN) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH) && \
- west build -t run --build-dir $(DOCKER_PROJECT_PATH)/build'
+ $(DOCKER) west build -t run
clean:
- $(DOCKER) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH) && \
- west build -t clean --build-dir $(DOCKER_PROJECT_PATH)/build'
+ -$(DOCKER) west build -t clean
pristine:
- $(DOCKER) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH) && \
- west build -t pristine --build-dir $(DOCKER_PROJECT_PATH)/build'
+ -$(DOCKER) west build -t pristine
+purge: clean pristine
+ rm -rf $(TOP_DIR)/.west $(ZEPHYR_DIR) build
+flash debug: DOCKER_ARGS ?= --privileged -v /dev/bus/usb/:/dev/bus/usb -it
flash:
- $(DOCKER) $(SHAREUSB) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH) && \
- west flash --build-dir $(DOCKER_PROJECT_PATH)/build'
+ $(DOCKER) west flash --build-dir $(realpath $(CURDIR))/build
debug:
- $(DOCKER) -it $(SHAREUSB) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH) && \
- west debug --build-dir $(DOCKER_PROJECT_PATH)/build'
+ $(DOCKER) west debug --build-dir $(realpath $(CURDIR))/build
-.PHONY: build flash zephyr clean pristine run
-
-zephyr:
-ifeq ($(wildcard $(ZEPHYR_PATH)/.*),)
- mkdir $(ZEPHYR_PATH)
- $(DOCKER) $(REPO) /bin/sh -c 'cd $(DOCKER_PROJECT_DIR) && west init ./$(ZEPHYR_DIR)'
-endif
- $(DOCKER) $(REPO) /bin/sh -c 'cd $(DOCKER_ZEPHYR_PATH) && west update'
+test: BUILD_ARGS = -b qemu_x86
+test: OVERLAY = -DOVERLAY_CONFIG=overlay-e1000.conf
+test: build run
diff --git a/mongoose.c b/mongoose.c
index e946730f..f18d1c4f 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -4250,7 +4250,7 @@ static void mg_set_non_blocking_mode(MG_SOCKET_TYPE fd) {
#elif MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK
unsigned long on = 1;
ioctlsocket(fd, FIONBIO, &on);
-#elif MG_ARCH == MG_ARCH_RTX
+#elif MG_ENABLE_RL
unsigned long on = 1;
ioctlsocket(fd, FIONBIO, &on);
#elif MG_ENABLE_FREERTOS_TCP
@@ -4598,8 +4598,9 @@ static void mg_iotest(struct mg_mgr *mgr, int ms) {
EventBits_t bits = FreeRTOS_FD_ISSET(c->fd, mgr->ss);
c->is_readable = bits & (eSELECT_READ | eSELECT_EXCEPT) ? 1U : 0;
c->is_writable = bits & eSELECT_WRITE ? 1U : 0;
- FreeRTOS_FD_CLR(c->fd, mgr->ss,
- eSELECT_READ | eSELECT_EXCEPT | eSELECT_WRITE);
+ if (c->fd != MG_INVALID_SOCKET)
+ FreeRTOS_FD_CLR(c->fd, mgr->ss,
+ eSELECT_READ | eSELECT_EXCEPT | eSELECT_WRITE);
}
#elif MG_ENABLE_EPOLL
size_t max = 1;
@@ -5734,6 +5735,10 @@ uint64_t mg_millis(void) {
return (uint64_t) Clock_getTicks();
#elif MG_ARCH == MG_ARCH_ZEPHYR
return (uint64_t) k_uptime_get();
+#elif MG_ARCH == MG_ARCH_CMSIS_RTOS1
+ return (uint64_t)rt_time_get();
+#elif MG_ARCH == MG_ARCH_CMSIS_RTOS2
+ return (uint64_t)((osKernelGetTickCount() * 1000) / osKernelGetTickFreq());
#elif MG_ARCH == MG_ARCH_UNIX && defined(__APPLE__)
// Apple CLOCK_MONOTONIC_RAW is equivalent to CLOCK_BOOTTIME on linux
// Apple CLOCK_UPTIME_RAW is equivalent to CLOCK_MONOTONIC_RAW on linux
@@ -6511,7 +6516,7 @@ static size_t mg_tcpip_driver_stm32_tx(const void *buf, size_t len,
static bool mg_tcpip_driver_stm32_up(struct mg_tcpip_if *ifp) {
uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR);
bool up = bsr & BIT(2) ? 1 : 0;
- if ((ifp->state == MIP_STATE_DOWN) && up) { // link state just went up
+ if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
uint32_t scsr = eth_read_phy(PHY_ADDR, PHY_CSCR);
uint32_t maccr = ETH->MACCR | BIT(14) | BIT(11); // 100M, Full-duplex
if ((scsr & BIT(3)) == 0) maccr &= ~BIT(14); // 10M
@@ -6787,7 +6792,7 @@ static size_t mg_tcpip_driver_stm32h_tx(const void *buf, size_t len,
static bool mg_tcpip_driver_stm32h_up(struct mg_tcpip_if *ifp) {
uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR);
bool up = bsr & BIT(2) ? 1 : 0;
- if ((ifp->state == MIP_STATE_DOWN) && up) { // link state just went up
+ if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
uint32_t scsr = eth_read_phy(PHY_ADDR, PHY_CSCR);
uint32_t maccr = ETH->MACCR | BIT(14) | BIT(13); // 100M, Full-duplex
if ((scsr & BIT(3)) == 0) maccr &= ~BIT(14); // 10M
@@ -7042,7 +7047,7 @@ static size_t mg_tcpip_driver_tm4c_tx(const void *buf, size_t len,
static bool mg_tcpip_driver_tm4c_up(struct mg_tcpip_if *ifp) {
uint32_t bmsr = emac_read_phy(EPHY_ADDR, EPHYBMSR);
bool up = (bmsr & BIT(2)) ? 1 : 0;
- if ((ifp->state == MIP_STATE_DOWN) && up) { // link state just went up
+ if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
uint32_t sts = emac_read_phy(EPHY_ADDR, EPHYSTS);
uint32_t emaccfg = EMAC->EMACCFG | BIT(14) | BIT(11); // 100M, Full-duplex
if (sts & BIT(1)) emaccfg &= ~BIT(14); // 10M
@@ -7352,7 +7357,7 @@ static void arp_ask(struct mg_tcpip_if *ifp, uint32_t ip) {
}
static void onstatechange(struct mg_tcpip_if *ifp) {
- if (ifp->state == MIP_STATE_READY) {
+ if (ifp->state == MG_TCPIP_STATE_READY) {
MG_INFO(("READY, IP: %M", mg_print_ip4, &ifp->ip));
MG_INFO((" GW: %M", mg_print_ip4, &ifp->gw));
if (ifp->lease_expire > ifp->now) {
@@ -7360,10 +7365,10 @@ static void onstatechange(struct mg_tcpip_if *ifp) {
(" Lease: %lld sec", (ifp->lease_expire - ifp->now) / 1000));
}
arp_ask(ifp, ifp->gw);
- } else if (ifp->state == MIP_STATE_UP) {
+ } else if (ifp->state == MG_TCPIP_STATE_UP) {
MG_ERROR(("Link up"));
srand((unsigned int) mg_millis());
- } else if (ifp->state == MIP_STATE_DOWN) {
+ } else if (ifp->state == MG_TCPIP_STATE_DOWN) {
MG_ERROR(("Link down"));
}
}
@@ -7533,7 +7538,7 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) {
if (ip && mask && gw && ifp->ip == 0) {
memcpy(ifp->gwmac, pkt->eth->src, sizeof(ifp->gwmac));
ifp->ip = ip, ifp->gw = gw, ifp->mask = mask;
- ifp->state = MIP_STATE_READY;
+ ifp->state = MG_TCPIP_STATE_READY;
onstatechange(ifp);
tx_dhcp_request(ifp, pkt->eth->src, ip, pkt->dhcp->siaddr);
uint64_t rand;
@@ -7916,16 +7921,16 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t uptime_ms) {
// Handle physical interface up/down status
if (expired_1000ms && ifp->driver->up) {
bool up = ifp->driver->up(ifp);
- bool current = ifp->state != MIP_STATE_DOWN;
+ bool current = ifp->state != MG_TCPIP_STATE_DOWN;
if (up != current) {
- ifp->state = up == false ? MIP_STATE_DOWN
- : ifp->enable_dhcp_client ? MIP_STATE_UP
- : MIP_STATE_READY;
+ ifp->state = up == false ? MG_TCPIP_STATE_DOWN
+ : ifp->enable_dhcp_client ? MG_TCPIP_STATE_UP
+ : MG_TCPIP_STATE_READY;
if (!up && ifp->enable_dhcp_client) ifp->ip = 0;
onstatechange(ifp);
}
}
- if (ifp->state == MIP_STATE_DOWN) return;
+ if (ifp->state == MG_TCPIP_STATE_DOWN) return;
// If IP not configured, send DHCP
if (ifp->ip == 0 && expired_1000ms) tx_dhcp_discover(ifp);
@@ -8109,7 +8114,7 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv;
bool res = false;
- if (ifp->ip == 0 || ifp->state != MIP_STATE_READY) {
+ if (ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY) {
mg_error(c, "net down");
} else if (c->is_udp) {
struct connstate *s = (struct connstate *) (c + 1);
diff --git a/mongoose.h b/mongoose.h
index 69496b80..00f9910b 100644
--- a/mongoose.h
+++ b/mongoose.h
@@ -27,18 +27,20 @@ extern "C" {
#endif
-#define MG_ARCH_CUSTOM 0 // User creates its own mongoose_custom.h
-#define MG_ARCH_UNIX 1 // Linux, BSD, Mac, ...
-#define MG_ARCH_WIN32 2 // Windows
-#define MG_ARCH_ESP32 3 // ESP32
-#define MG_ARCH_ESP8266 4 // ESP8266
-#define MG_ARCH_FREERTOS 5 // FreeRTOS
-#define MG_ARCH_AZURERTOS 6 // MS Azure RTOS
-#define MG_ARCH_ZEPHYR 7 // Zephyr RTOS
-#define MG_ARCH_NEWLIB 8 // Bare metal ARM
-#define MG_ARCH_RTX 9 // Keil MDK RTX
-#define MG_ARCH_TIRTOS 10 // Texas Semi TI-RTOS
-#define MG_ARCH_RP2040 11 // Raspberry Pi RP2040
+#define MG_ARCH_CUSTOM 0 // User creates its own mongoose_custom.h
+#define MG_ARCH_UNIX 1 // Linux, BSD, Mac, ...
+#define MG_ARCH_WIN32 2 // Windows
+#define MG_ARCH_ESP32 3 // ESP32
+#define MG_ARCH_ESP8266 4 // ESP8266
+#define MG_ARCH_FREERTOS 5 // FreeRTOS
+#define MG_ARCH_AZURERTOS 6 // MS Azure RTOS
+#define MG_ARCH_ZEPHYR 7 // Zephyr RTOS
+#define MG_ARCH_NEWLIB 8 // Bare metal ARM
+#define MG_ARCH_CMSIS_RTOS1 9 // CMSIS-RTOS API v1 (Keil RTX)
+#define MG_ARCH_TIRTOS 10 // Texas Semi TI-RTOS
+#define MG_ARCH_RP2040 11 // Raspberry Pi RP2040
+#define MG_ARCH_ARMCC 12 // Keil MDK-Core with Configuration Wizard
+#define MG_ARCH_CMSIS_RTOS2 13 // CMSIS-RTOS API v2 (Keil RTX5, FreeRTOS)
#if !defined(MG_ARCH)
#if defined(__unix__) || defined(__APPLE__)
@@ -58,10 +60,14 @@ extern "C" {
#define MG_ARCH MG_ARCH_AZURERTOS
#elif defined(PICO_TARGET_NAME)
#define MG_ARCH MG_ARCH_RP2040
+#elif defined(__ARMCC_VERSION)
+#define MG_ARCH MG_ARCH_ARMCC
#endif
#endif // !defined(MG_ARCH)
-#if !defined(MG_ARCH) || (MG_ARCH == MG_ARCH_CUSTOM)
+// if the user did not specify an MG_ARCH, or specified a custom one, OR
+// we guessed a known IDE, pull the customized config (Configuration Wizard)
+#if !defined(MG_ARCH) || (MG_ARCH == MG_ARCH_CUSTOM) || MG_ARCH == MG_ARCH_ARMCC
#include "mongoose_custom.h" // keep this include
#endif
@@ -166,7 +172,9 @@ extern "C" {
#if MG_ARCH == MG_ARCH_FREERTOS
#include
-// #include // Cannot include errno - might conflict with lwip!
+#if !defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP
+#include
+#endif
#include
#include
#include
@@ -174,7 +182,12 @@ extern "C" {
#include
#include // rand(), strtol(), atoi()
#include
+#if defined(__ARMCC_VERSION)
+#define mode_t size_t
+#include
+#else
#include
+#endif
#include
#include
@@ -242,7 +255,8 @@ int mkdir(const char *, mode_t);
#endif
-#if MG_ARCH == MG_ARCH_RTX
+#if MG_ARCH == MG_ARCH_ARMCC || MG_ARCH == MG_ARCH_CMSIS_RTOS1 || \
+ MG_ARCH == MG_ARCH_CMSIS_RTOS2
#include
#include
@@ -254,8 +268,28 @@ int mkdir(const char *, mode_t);
#include
#include
#include
+#if MG_ARCH == MG_ARCH_CMSIS_RTOS1
+#include "cmsis_os.h" // keep this include
+// https://developer.arm.com/documentation/ka003821/latest
+extern uint32_t rt_time_get(void);
+#elif MG_ARCH == MG_ARCH_CMSIS_RTOS2
+#include "cmsis_os2.h" // keep this include
+#endif
-#if !defined MG_ENABLE_RL && (!defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP)
+#define strdup(s) ((char *) mg_strdup(mg_str(s)).ptr)
+
+#if defined(__ARMCC_VERSION)
+#define mode_t size_t
+#define mkdir(a, b) mg_mkdir(a, b)
+static inline int mg_mkdir(const char *path, mode_t mode) {
+ (void) path, (void) mode;
+ return -1;
+}
+#endif
+
+#if (MG_ARCH == MG_ARCH_CMSIS_RTOS1 || MG_ARCH == MG_ARCH_CMSIS_RTOS2) && \
+ !defined MG_ENABLE_RL && (!defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP) && \
+ (!defined(MG_ENABLE_TCPIP) || !MG_ENABLE_TCPIP)
#define MG_ENABLE_RL 1
#endif
@@ -482,24 +516,12 @@ int sscanf(const char *, const char *, ...);
#if defined(MG_ENABLE_FREERTOS_TCP) && MG_ENABLE_FREERTOS_TCP
-#include
-#include
#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
#include
-#include
#include
#include
+#include // contents to be moved and file removed, some day
#define MG_SOCKET_TYPE Socket_t
#define MG_INVALID_SOCKET FREERTOS_INVALID_SOCKET
@@ -515,6 +537,20 @@ int sscanf(const char *, const char *, ...);
#define SO_ERROR 0
#define SOL_SOCKET 0
#define SO_REUSEADDR 0
+
+#define MG_SOCK_ERR(errcode) ((errcode) < 0 ? (errcode) : 0)
+
+#define MG_SOCK_PENDING(errcode) \
+ ((errcode) == -pdFREERTOS_ERRNO_EWOULDBLOCK || \
+ (errcode) == -pdFREERTOS_ERRNO_EISCONN || \
+ (errcode) == -pdFREERTOS_ERRNO_EINPROGRESS || \
+ (errcode) == -pdFREERTOS_ERRNO_EAGAIN)
+
+#define MG_SOCK_RESET(errcode) ((errcode) == -pdFREERTOS_ERRNO_ENOTCONN)
+
+// actually only if optional timeout is enabled
+#define MG_SOCK_INTR(fd) (fd == NULL)
+
#define sockaddr_in freertos_sockaddr
#define sockaddr freertos_sockaddr
#define accept(a, b, c) FreeRTOS_accept((a), (b), (c))
@@ -546,8 +582,17 @@ static inline int mg_getpeername(MG_SOCKET_TYPE fd, void *buf, socklen_t *len) {
#if defined(MG_ENABLE_LWIP) && MG_ENABLE_LWIP
-#if defined(__GNUC__)
+
+#if defined(__GNUC__) && !defined(__ARMCC_VERSION)
#include
+#endif
+
+struct timeval;
+
+#include
+
+#if !LWIP_TIMEVAL_PRIVATE
+#if defined(__GNUC__) && !defined(__ARMCC_VERSION) // armclang sets both
#include
#else
struct timeval {
@@ -555,8 +600,7 @@ struct timeval {
long tv_usec;
};
#endif
-
-#include
+#endif
#if LWIP_SOCKET != 1
// Sockets support disabled in LWIP by default
@@ -568,9 +612,7 @@ struct timeval {
#if defined(MG_ENABLE_RL) && MG_ENABLE_RL
#include
-#define MG_ENABLE_CUSTOM_MILLIS 1
#define closesocket(x) closesocket(x)
-#define mkdir(a, b) (-1)
#define TCP_NODELAY SO_KEEPALIVE
@@ -1514,32 +1556,32 @@ struct mg_tcpip_driver {
// Network interface
struct mg_tcpip_if {
- uint8_t mac[6]; // MAC address. Must be set to a valid MAC
- uint32_t ip, mask, gw; // IP address, mask, default gateway
- struct mg_str tx; // Output (TX) buffer
- bool enable_dhcp_client; // Enable DCHP client
- bool enable_dhcp_server; // Enable DCHP server
- bool enable_crc32_check; // Do a CRC check on rx frames and strip it
- bool enable_mac_check; // Do a MAC check on rx frames
+ uint8_t mac[6]; // MAC address. Must be set to a valid MAC
+ uint32_t ip, mask, gw; // IP address, mask, default gateway
+ struct mg_str tx; // Output (TX) buffer
+ bool enable_dhcp_client; // Enable DCHP client
+ bool enable_dhcp_server; // Enable DCHP server
+ bool enable_crc32_check; // Do a CRC check on rx frames and strip it
+ bool enable_mac_check; // Do a MAC check on rx frames
struct mg_tcpip_driver *driver; // Low level driver
void *driver_data; // Driver-specific data
struct mg_mgr *mgr; // Mongoose event manager
struct mg_queue recv_queue; // Receive queue
// Internal state, user can use it but should not change it
- uint8_t gwmac[6]; // Router's MAC
- uint64_t now; // Current time
- uint64_t timer_1000ms; // 1000 ms timer: for DHCP and link state
- uint64_t lease_expire; // Lease expiration time
- uint16_t eport; // Next ephemeral port
- volatile uint32_t ndrop; // Number of received, but dropped frames
- volatile uint32_t nrecv; // Number of received frames
- volatile uint32_t nsent; // Number of transmitted frames
- volatile uint32_t nerr; // Number of driver errors
- uint8_t state; // Current state
-#define MIP_STATE_DOWN 0 // Interface is down
-#define MIP_STATE_UP 1 // Interface is up
-#define MIP_STATE_READY 2 // Interface is up and has IP
+ uint8_t gwmac[6]; // Router's MAC
+ uint64_t now; // Current time
+ uint64_t timer_1000ms; // 1000 ms timer: for DHCP and link state
+ uint64_t lease_expire; // Lease expiration time
+ uint16_t eport; // Next ephemeral port
+ volatile uint32_t ndrop; // Number of received, but dropped frames
+ volatile uint32_t nrecv; // Number of received frames
+ volatile uint32_t nsent; // Number of transmitted frames
+ volatile uint32_t nerr; // Number of driver errors
+ uint8_t state; // Current state
+#define MG_TCPIP_STATE_DOWN 0 // Interface is down
+#define MG_TCPIP_STATE_UP 1 // Interface is up
+#define MG_TCPIP_STATE_READY 2 // Interface is up and has IP
};
void mg_tcpip_init(struct mg_mgr *, struct mg_tcpip_if *);
diff --git a/src/arch.h b/src/arch.h
index c7223bb7..d40c767a 100644
--- a/src/arch.h
+++ b/src/arch.h
@@ -1,17 +1,19 @@
#pragma once
-#define MG_ARCH_CUSTOM 0 // User creates its own mongoose_custom.h
-#define MG_ARCH_UNIX 1 // Linux, BSD, Mac, ...
-#define MG_ARCH_WIN32 2 // Windows
-#define MG_ARCH_ESP32 3 // ESP32
-#define MG_ARCH_ESP8266 4 // ESP8266
-#define MG_ARCH_FREERTOS 5 // FreeRTOS
-#define MG_ARCH_AZURERTOS 6 // MS Azure RTOS
-#define MG_ARCH_ZEPHYR 7 // Zephyr RTOS
-#define MG_ARCH_NEWLIB 8 // Bare metal ARM
-#define MG_ARCH_RTX 9 // Keil MDK RTX
-#define MG_ARCH_TIRTOS 10 // Texas Semi TI-RTOS
-#define MG_ARCH_RP2040 11 // Raspberry Pi RP2040
+#define MG_ARCH_CUSTOM 0 // User creates its own mongoose_custom.h
+#define MG_ARCH_UNIX 1 // Linux, BSD, Mac, ...
+#define MG_ARCH_WIN32 2 // Windows
+#define MG_ARCH_ESP32 3 // ESP32
+#define MG_ARCH_ESP8266 4 // ESP8266
+#define MG_ARCH_FREERTOS 5 // FreeRTOS
+#define MG_ARCH_AZURERTOS 6 // MS Azure RTOS
+#define MG_ARCH_ZEPHYR 7 // Zephyr RTOS
+#define MG_ARCH_NEWLIB 8 // Bare metal ARM
+#define MG_ARCH_CMSIS_RTOS1 9 // CMSIS-RTOS API v1 (Keil RTX)
+#define MG_ARCH_TIRTOS 10 // Texas Semi TI-RTOS
+#define MG_ARCH_RP2040 11 // Raspberry Pi RP2040
+#define MG_ARCH_ARMCC 12 // Keil MDK-Core with Configuration Wizard
+#define MG_ARCH_CMSIS_RTOS2 13 // CMSIS-RTOS API v2 (Keil RTX5, FreeRTOS)
#if !defined(MG_ARCH)
#if defined(__unix__) || defined(__APPLE__)
@@ -31,10 +33,14 @@
#define MG_ARCH MG_ARCH_AZURERTOS
#elif defined(PICO_TARGET_NAME)
#define MG_ARCH MG_ARCH_RP2040
+#elif defined(__ARMCC_VERSION)
+#define MG_ARCH MG_ARCH_ARMCC
#endif
#endif // !defined(MG_ARCH)
-#if !defined(MG_ARCH) || (MG_ARCH == MG_ARCH_CUSTOM)
+// if the user did not specify an MG_ARCH, or specified a custom one, OR
+// we guessed a known IDE, pull the customized config (Configuration Wizard)
+#if !defined(MG_ARCH) || (MG_ARCH == MG_ARCH_CUSTOM) || MG_ARCH == MG_ARCH_ARMCC
#include "mongoose_custom.h" // keep this include
#endif
diff --git a/src/arch_freertos.h b/src/arch_freertos.h
index eb9615d3..345c0db4 100644
--- a/src/arch_freertos.h
+++ b/src/arch_freertos.h
@@ -3,7 +3,9 @@
#if MG_ARCH == MG_ARCH_FREERTOS
#include
-// #include // Cannot include errno - might conflict with lwip!
+#if !defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP
+#include
+#endif
#include
#include
#include
@@ -11,7 +13,12 @@
#include
#include // rand(), strtol(), atoi()
#include
+#if defined(__ARMCC_VERSION)
+#define mode_t size_t
+#include
+#else
#include
+#endif
#include
#include
diff --git a/src/arch_rtx.h b/src/arch_rtx.h
index f364c16b..ff312f57 100644
--- a/src/arch_rtx.h
+++ b/src/arch_rtx.h
@@ -1,6 +1,7 @@
#pragma once
-#if MG_ARCH == MG_ARCH_RTX
+#if MG_ARCH == MG_ARCH_ARMCC || MG_ARCH == MG_ARCH_CMSIS_RTOS1 || \
+ MG_ARCH == MG_ARCH_CMSIS_RTOS2
#include
#include
@@ -12,8 +13,28 @@
#include
#include
#include
+#if MG_ARCH == MG_ARCH_CMSIS_RTOS1
+#include "cmsis_os.h" // keep this include
+// https://developer.arm.com/documentation/ka003821/latest
+extern uint32_t rt_time_get(void);
+#elif MG_ARCH == MG_ARCH_CMSIS_RTOS2
+#include "cmsis_os2.h" // keep this include
+#endif
-#if !defined MG_ENABLE_RL && (!defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP)
+#define strdup(s) ((char *) mg_strdup(mg_str(s)).ptr)
+
+#if defined(__ARMCC_VERSION)
+#define mode_t size_t
+#define mkdir(a, b) mg_mkdir(a, b)
+static inline int mg_mkdir(const char *path, mode_t mode) {
+ (void) path, (void) mode;
+ return -1;
+}
+#endif
+
+#if (MG_ARCH == MG_ARCH_CMSIS_RTOS1 || MG_ARCH == MG_ARCH_CMSIS_RTOS2) && \
+ !defined MG_ENABLE_RL && (!defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP) && \
+ (!defined(MG_ENABLE_TCPIP) || !MG_ENABLE_TCPIP)
#define MG_ENABLE_RL 1
#endif
diff --git a/src/net_ft.h b/src/net_ft.h
index 0c43ea07..9679605b 100644
--- a/src/net_ft.h
+++ b/src/net_ft.h
@@ -2,24 +2,12 @@
#if defined(MG_ENABLE_FREERTOS_TCP) && MG_ENABLE_FREERTOS_TCP
-#include
-#include
#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
#include
-#include
#include
#include
+#include // contents to be moved and file removed, some day
#define MG_SOCKET_TYPE Socket_t
#define MG_INVALID_SOCKET FREERTOS_INVALID_SOCKET
@@ -35,6 +23,20 @@
#define SO_ERROR 0
#define SOL_SOCKET 0
#define SO_REUSEADDR 0
+
+#define MG_SOCK_ERR(errcode) ((errcode) < 0 ? (errcode) : 0)
+
+#define MG_SOCK_PENDING(errcode) \
+ ((errcode) == -pdFREERTOS_ERRNO_EWOULDBLOCK || \
+ (errcode) == -pdFREERTOS_ERRNO_EISCONN || \
+ (errcode) == -pdFREERTOS_ERRNO_EINPROGRESS || \
+ (errcode) == -pdFREERTOS_ERRNO_EAGAIN)
+
+#define MG_SOCK_RESET(errcode) ((errcode) == -pdFREERTOS_ERRNO_ENOTCONN)
+
+// actually only if optional timeout is enabled
+#define MG_SOCK_INTR(fd) (fd == NULL)
+
#define sockaddr_in freertos_sockaddr
#define sockaddr freertos_sockaddr
#define accept(a, b, c) FreeRTOS_accept((a), (b), (c))
diff --git a/src/net_lwip.h b/src/net_lwip.h
index d137e919..0aee72f2 100644
--- a/src/net_lwip.h
+++ b/src/net_lwip.h
@@ -1,8 +1,17 @@
#pragma once
#if defined(MG_ENABLE_LWIP) && MG_ENABLE_LWIP
-#if defined(__GNUC__)
+
+#if defined(__GNUC__) && !defined(__ARMCC_VERSION)
#include
+#endif
+
+struct timeval;
+
+#include
+
+#if !LWIP_TIMEVAL_PRIVATE
+#if defined(__GNUC__) && !defined(__ARMCC_VERSION) // armclang sets both
#include
#else
struct timeval {
@@ -10,8 +19,7 @@ struct timeval {
long tv_usec;
};
#endif
-
-#include
+#endif
#if LWIP_SOCKET != 1
// Sockets support disabled in LWIP by default
diff --git a/src/net_rl.h b/src/net_rl.h
index 2e4b6874..4c0e3890 100644
--- a/src/net_rl.h
+++ b/src/net_rl.h
@@ -3,9 +3,7 @@
#if defined(MG_ENABLE_RL) && MG_ENABLE_RL
#include
-#define MG_ENABLE_CUSTOM_MILLIS 1
#define closesocket(x) closesocket(x)
-#define mkdir(a, b) (-1)
#define TCP_NODELAY SO_KEEPALIVE
diff --git a/src/sock.c b/src/sock.c
index aaf58ca9..c5088b92 100644
--- a/src/sock.c
+++ b/src/sock.c
@@ -151,7 +151,7 @@ static void mg_set_non_blocking_mode(MG_SOCKET_TYPE fd) {
#elif MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK
unsigned long on = 1;
ioctlsocket(fd, FIONBIO, &on);
-#elif MG_ARCH == MG_ARCH_RTX
+#elif MG_ENABLE_RL
unsigned long on = 1;
ioctlsocket(fd, FIONBIO, &on);
#elif MG_ENABLE_FREERTOS_TCP
@@ -499,8 +499,9 @@ static void mg_iotest(struct mg_mgr *mgr, int ms) {
EventBits_t bits = FreeRTOS_FD_ISSET(c->fd, mgr->ss);
c->is_readable = bits & (eSELECT_READ | eSELECT_EXCEPT) ? 1U : 0;
c->is_writable = bits & eSELECT_WRITE ? 1U : 0;
- FreeRTOS_FD_CLR(c->fd, mgr->ss,
- eSELECT_READ | eSELECT_EXCEPT | eSELECT_WRITE);
+ if (c->fd != MG_INVALID_SOCKET)
+ FreeRTOS_FD_CLR(c->fd, mgr->ss,
+ eSELECT_READ | eSELECT_EXCEPT | eSELECT_WRITE);
}
#elif MG_ENABLE_EPOLL
size_t max = 1;
diff --git a/src/tcpip/driver_stm32.c b/src/tcpip/driver_stm32.c
index 0cb74cb4..3d3a1d77 100644
--- a/src/tcpip/driver_stm32.c
+++ b/src/tcpip/driver_stm32.c
@@ -178,7 +178,7 @@ static size_t mg_tcpip_driver_stm32_tx(const void *buf, size_t len,
static bool mg_tcpip_driver_stm32_up(struct mg_tcpip_if *ifp) {
uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR);
bool up = bsr & BIT(2) ? 1 : 0;
- if ((ifp->state == MIP_STATE_DOWN) && up) { // link state just went up
+ if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
uint32_t scsr = eth_read_phy(PHY_ADDR, PHY_CSCR);
uint32_t maccr = ETH->MACCR | BIT(14) | BIT(11); // 100M, Full-duplex
if ((scsr & BIT(3)) == 0) maccr &= ~BIT(14); // 10M
diff --git a/src/tcpip/driver_stm32h.c b/src/tcpip/driver_stm32h.c
index 5c5b3d20..2567f95d 100644
--- a/src/tcpip/driver_stm32h.c
+++ b/src/tcpip/driver_stm32h.c
@@ -233,7 +233,7 @@ static size_t mg_tcpip_driver_stm32h_tx(const void *buf, size_t len,
static bool mg_tcpip_driver_stm32h_up(struct mg_tcpip_if *ifp) {
uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR);
bool up = bsr & BIT(2) ? 1 : 0;
- if ((ifp->state == MIP_STATE_DOWN) && up) { // link state just went up
+ if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
uint32_t scsr = eth_read_phy(PHY_ADDR, PHY_CSCR);
uint32_t maccr = ETH->MACCR | BIT(14) | BIT(13); // 100M, Full-duplex
if ((scsr & BIT(3)) == 0) maccr &= ~BIT(14); // 10M
diff --git a/src/tcpip/driver_tm4c.c b/src/tcpip/driver_tm4c.c
index f7a5d937..61bd5b0e 100644
--- a/src/tcpip/driver_tm4c.c
+++ b/src/tcpip/driver_tm4c.c
@@ -209,7 +209,7 @@ static size_t mg_tcpip_driver_tm4c_tx(const void *buf, size_t len,
static bool mg_tcpip_driver_tm4c_up(struct mg_tcpip_if *ifp) {
uint32_t bmsr = emac_read_phy(EPHY_ADDR, EPHYBMSR);
bool up = (bmsr & BIT(2)) ? 1 : 0;
- if ((ifp->state == MIP_STATE_DOWN) && up) { // link state just went up
+ if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
uint32_t sts = emac_read_phy(EPHY_ADDR, EPHYSTS);
uint32_t emaccfg = EMAC->EMACCFG | BIT(14) | BIT(11); // 100M, Full-duplex
if (sts & BIT(1)) emaccfg &= ~BIT(14); // 10M
diff --git a/src/tcpip/tcpip.c b/src/tcpip/tcpip.c
index 02330b2c..d6649861 100644
--- a/src/tcpip/tcpip.c
+++ b/src/tcpip/tcpip.c
@@ -173,7 +173,7 @@ static void arp_ask(struct mg_tcpip_if *ifp, uint32_t ip) {
}
static void onstatechange(struct mg_tcpip_if *ifp) {
- if (ifp->state == MIP_STATE_READY) {
+ if (ifp->state == MG_TCPIP_STATE_READY) {
MG_INFO(("READY, IP: %M", mg_print_ip4, &ifp->ip));
MG_INFO((" GW: %M", mg_print_ip4, &ifp->gw));
if (ifp->lease_expire > ifp->now) {
@@ -181,10 +181,10 @@ static void onstatechange(struct mg_tcpip_if *ifp) {
(" Lease: %lld sec", (ifp->lease_expire - ifp->now) / 1000));
}
arp_ask(ifp, ifp->gw);
- } else if (ifp->state == MIP_STATE_UP) {
+ } else if (ifp->state == MG_TCPIP_STATE_UP) {
MG_ERROR(("Link up"));
srand((unsigned int) mg_millis());
- } else if (ifp->state == MIP_STATE_DOWN) {
+ } else if (ifp->state == MG_TCPIP_STATE_DOWN) {
MG_ERROR(("Link down"));
}
}
@@ -354,7 +354,7 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) {
if (ip && mask && gw && ifp->ip == 0) {
memcpy(ifp->gwmac, pkt->eth->src, sizeof(ifp->gwmac));
ifp->ip = ip, ifp->gw = gw, ifp->mask = mask;
- ifp->state = MIP_STATE_READY;
+ ifp->state = MG_TCPIP_STATE_READY;
onstatechange(ifp);
tx_dhcp_request(ifp, pkt->eth->src, ip, pkt->dhcp->siaddr);
uint64_t rand;
@@ -737,16 +737,16 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t uptime_ms) {
// Handle physical interface up/down status
if (expired_1000ms && ifp->driver->up) {
bool up = ifp->driver->up(ifp);
- bool current = ifp->state != MIP_STATE_DOWN;
+ bool current = ifp->state != MG_TCPIP_STATE_DOWN;
if (up != current) {
- ifp->state = up == false ? MIP_STATE_DOWN
- : ifp->enable_dhcp_client ? MIP_STATE_UP
- : MIP_STATE_READY;
+ ifp->state = up == false ? MG_TCPIP_STATE_DOWN
+ : ifp->enable_dhcp_client ? MG_TCPIP_STATE_UP
+ : MG_TCPIP_STATE_READY;
if (!up && ifp->enable_dhcp_client) ifp->ip = 0;
onstatechange(ifp);
}
}
- if (ifp->state == MIP_STATE_DOWN) return;
+ if (ifp->state == MG_TCPIP_STATE_DOWN) return;
// If IP not configured, send DHCP
if (ifp->ip == 0 && expired_1000ms) tx_dhcp_discover(ifp);
@@ -930,7 +930,7 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv;
bool res = false;
- if (ifp->ip == 0 || ifp->state != MIP_STATE_READY) {
+ if (ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY) {
mg_error(c, "net down");
} else if (c->is_udp) {
struct connstate *s = (struct connstate *) (c + 1);
diff --git a/src/tcpip/tcpip.h b/src/tcpip/tcpip.h
index 677dba60..3bdcefea 100644
--- a/src/tcpip/tcpip.h
+++ b/src/tcpip/tcpip.h
@@ -15,32 +15,32 @@ struct mg_tcpip_driver {
// Network interface
struct mg_tcpip_if {
- uint8_t mac[6]; // MAC address. Must be set to a valid MAC
- uint32_t ip, mask, gw; // IP address, mask, default gateway
- struct mg_str tx; // Output (TX) buffer
- bool enable_dhcp_client; // Enable DCHP client
- bool enable_dhcp_server; // Enable DCHP server
- bool enable_crc32_check; // Do a CRC check on rx frames and strip it
- bool enable_mac_check; // Do a MAC check on rx frames
+ uint8_t mac[6]; // MAC address. Must be set to a valid MAC
+ uint32_t ip, mask, gw; // IP address, mask, default gateway
+ struct mg_str tx; // Output (TX) buffer
+ bool enable_dhcp_client; // Enable DCHP client
+ bool enable_dhcp_server; // Enable DCHP server
+ bool enable_crc32_check; // Do a CRC check on rx frames and strip it
+ bool enable_mac_check; // Do a MAC check on rx frames
struct mg_tcpip_driver *driver; // Low level driver
void *driver_data; // Driver-specific data
struct mg_mgr *mgr; // Mongoose event manager
struct mg_queue recv_queue; // Receive queue
// Internal state, user can use it but should not change it
- uint8_t gwmac[6]; // Router's MAC
- uint64_t now; // Current time
- uint64_t timer_1000ms; // 1000 ms timer: for DHCP and link state
- uint64_t lease_expire; // Lease expiration time
- uint16_t eport; // Next ephemeral port
- volatile uint32_t ndrop; // Number of received, but dropped frames
- volatile uint32_t nrecv; // Number of received frames
- volatile uint32_t nsent; // Number of transmitted frames
- volatile uint32_t nerr; // Number of driver errors
- uint8_t state; // Current state
-#define MIP_STATE_DOWN 0 // Interface is down
-#define MIP_STATE_UP 1 // Interface is up
-#define MIP_STATE_READY 2 // Interface is up and has IP
+ uint8_t gwmac[6]; // Router's MAC
+ uint64_t now; // Current time
+ uint64_t timer_1000ms; // 1000 ms timer: for DHCP and link state
+ uint64_t lease_expire; // Lease expiration time
+ uint16_t eport; // Next ephemeral port
+ volatile uint32_t ndrop; // Number of received, but dropped frames
+ volatile uint32_t nrecv; // Number of received frames
+ volatile uint32_t nsent; // Number of transmitted frames
+ volatile uint32_t nerr; // Number of driver errors
+ uint8_t state; // Current state
+#define MG_TCPIP_STATE_DOWN 0 // Interface is down
+#define MG_TCPIP_STATE_UP 1 // Interface is up
+#define MG_TCPIP_STATE_READY 2 // Interface is up and has IP
};
void mg_tcpip_init(struct mg_mgr *, struct mg_tcpip_if *);
diff --git a/src/util.c b/src/util.c
index e2d44730..28941f57 100644
--- a/src/util.c
+++ b/src/util.c
@@ -109,6 +109,10 @@ uint64_t mg_millis(void) {
return (uint64_t) Clock_getTicks();
#elif MG_ARCH == MG_ARCH_ZEPHYR
return (uint64_t) k_uptime_get();
+#elif MG_ARCH == MG_ARCH_CMSIS_RTOS1
+ return (uint64_t)rt_time_get();
+#elif MG_ARCH == MG_ARCH_CMSIS_RTOS2
+ return (uint64_t)((osKernelGetTickCount() * 1000) / osKernelGetTickFreq());
#elif MG_ARCH == MG_ARCH_UNIX && defined(__APPLE__)
// Apple CLOCK_MONOTONIC_RAW is equivalent to CLOCK_BOOTTIME on linux
// Apple CLOCK_UPTIME_RAW is equivalent to CLOCK_MONOTONIC_RAW on linux
diff --git a/test/mip_test.c b/test/mip_test.c
index b796e48a..29db4d8c 100644
--- a/test/mip_test.c
+++ b/test/mip_test.c
@@ -22,7 +22,7 @@ static void test_statechange(void) {
struct mg_tcpip_if iface;
memset(&iface, 0, sizeof(iface));
iface.ip = mg_htonl(0x01020304);
- iface.state = MIP_STATE_READY;
+ iface.state = MG_TCPIP_STATE_READY;
iface.tx.ptr = tx, iface.tx.len = sizeof(tx);
iface.driver = &mg_tcpip_driver_mock;
onstatechange(&iface);