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.
Want to evaluate Mongoose the easy way? Try Mongoose Wizard - a no-code Web UI builder.
2-minute integration guide
Step 1. Follow Build Tools to setup your development
environment
Step 2. Copy mongoose.c
and mongoose.h
to your source tree
Step 3. Add the following snippets to your main.c
file:
#include "mongoose.h"
// Connection event handler function
static void fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data; // Parsed HTTP request
if (mg_match(hm->uri, mg_str("/api/hello"), NULL)) { // REST API call?
mg_http_reply(c, 200, "", "{%m:%d}\n", MG_ESC("status"), 1); // Yes. Respond JSON
} else {
struct mg_http_serve_opts opts = {.root_dir = "."}; // For all other URLs,
mg_http_serve_dir(c, hm, &opts); // Serve static files
}
}
}
int main() {
struct mg_mgr mgr; // Mongoose event manager. Holds all connections
mg_mgr_init(&mgr); // Initialise event manager
mg_http_listen(&mgr, "http://0.0.0.0:8000", fn, NULL); // Setup listener
for (;;) {
mg_mgr_poll(&mgr, 1000); // Infinite event loop
}
return 0;
}
Step 4. Rebuild and run. Point your browser at http://localhost:8000.
NOTE: If you're building for some embedded system, create
mongoose_config.h
and add extra build flags in that file. See Build options for details.
Connections and event manager
Mongoose has two basic data structures:
struct mg_mgr
- An event manager that holds all active connectionsstruct 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.
Consider the snippet that starts HTTP server:
struct mg_mgr mgr; // Event manager
mg_mgr_init(&mgr); // Init manager
mg_http_listen(&mgr, "http://0.0.0.0:8000", fn, NULL); // Setup HTTP listener
mg_http_listen(&mgr, "https://0.0.0.0:8443", fn, NULL); // Setup HTTPS listener
for (;;) { // Infinite event loop
mg_mgr_poll(&mgr, 1000); // Process all connections
}
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
.
In the snippet above, a user-specified fn()
function will be called on
every event, like incoming HTTP request.
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.
Send and receive buffers
Each connection has a send and receive buffer:
struct mg_connection::send
- Data to be sent to a peerstruct 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:
// Event handler function defines connection behavior
static void fn(struct mg_connection *c, int ev, void *ev_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
- The connection receiving this eventint ev
- An event number, defined in mongoose.h. For example, when data arrives on an inbound connection,ev
would beMG_EV_READ
void *ev_data
- Points to the event-specific data, and it has a different meaning for different events. For example, for anMG_EV_READ
event,ev_data
is along *
pointing to the number of bytes received from a remote peer and saved into thec->recv
IO buffer. The exact meaning ofev_data
is described for each event. Protocol-specific events usually haveev_data
pointing to structures that hold protocol-specific informationc->fn_data
,void *
- A user-defined pointer for the connection, which is a placeholder for application-specific data. Thisfn_data
pointer is set during the*_listen()
or*_connect()
call. Listening connections copy the value ofc->fn_data
to the newly accepted connection, so all accepted connections initially share the samefn_data
pointer. It is fine to update/replace that pointer for any connection at any time by settingc->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:
enum {
MG_EV_ERROR, // Error char *error_message
MG_EV_OPEN, // Connection created NULL
MG_EV_POLL, // mg_mgr_poll iteration uint64_t *uptime_millis
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_HDRS, // HTTP headers struct mg_http_message *
MG_EV_HTTP_MSG, // Full HTTP request/response 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 *epoch_millis
MG_EV_WAKEUP, // mg_wakeup() data received struct mg_str *data
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:
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
};
Opening and closing connections
In order to open a listening (server) connection, call a corresponding function for the respective protocol. For example, to open HTTP and MQTT servers,
mg_http_listen(&mgr, "http://0.0.0.0:80", http_event_handler_fn, NULL);
mg_mqtt_listen(&mgr, "mqtt://0.0.0.0:1883", mqtt_event_handler_fn, NULL);
In order to open a listening connection for a custom protocol, use plain TCP (or UDP if you wish) listener:
mg_listen(&mgr, "tcp://0.0.0.0:1234", tcp_event_handler_fn, NULL);
In order to open a client connection, use the mg_*connect()
function for
the respective protocol:
mg_connect()
for plain TCP/UDP or custom protocolmg_http_connect()
for HTTPmg_ws_connect()
for Websocketmg_mqtt_connect()
for MQTTmg_sntp_connect()
for SNTP
Check out examples for SMTP client, SSDP client, and so on.
In order to close a connection, there is no dedicated function. You should tell Mongoose to close the connection using connection flags. Inside your event handler function, use one of these two flags:
c->is_draining = 1;
- send all remaining data in the send buffer ("drain" the connection), then properly close the connection.c->is_closing = 1;
- close straight away, remove the connection on the next poll cycle regardless of any buffered data or a clean TCP closure.
For example, this simple TCP echo server closes the connection right after echoing the first message back:
static void fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_READ) {
mg_send(c, c->recv.buf, c->recv.len); // Send received data back
c->recv.len = 0; // Clean receive buffer
c->is_draining = 1; // Close this connection when the response is sent
}
}
Best practices
- Debug log. To increase debug verbosity, call
mg_log_set()
:
Themg_log_set(MG_LL_DEBUG); mg_mgr_init(&mgr);
MG_INFO()
,MG_DEBUG()
logging macros useputchar()
by default, i.e. they use standard Cstdout
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 redirectsprintf()
to the UART0. Otherwise, IO retargeting can be implemented manually, see guide for more details. The alternative way is to redirect Mongoose logs: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:static void fn(struct mg_connection *c, int ev, void *ev_data) { if (ev == MG_EV_OPEN) { ... // Do your initialisation }
- If you need to keep some connection-specific data, you have two options:
- Use
c->fn_data
pointer. That pointer is passed to the event handler as last parameter:static void fn(struct mg_connection *c, int ev, void *ev_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 inherits a NULL pointer as c->fn_data, // but then we change it in its connection event handler to something else mg_http_listen(&mgr, "http://localhost:1234", fn, NULL);
- Use the
c->data
buffer, which can hold some amount of connection-specific data without extra memory allocation:static void fn(struct mg_connection *c, int ev, void *ev_data) { if (ev == MG_EV_WS_OPEN) { c->data[0] = 'W'; // Established websocket connection, store something ...
- Use
- Use the
mg_http_reply()
function to create HTTP responses. That function properly sets theContent-Length
header, which is important. Of course you can create responses manually, e.g. withmg_printf()
function, but be sure to set theContent-Length
header:
Alternatively, use chunked transfer encoding:mg_printf(c, "HTTP/1.1 200 OK\r\Content-Length: %d\r\n\r\n%s", 2, "hi");
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()
ormg_http_*_chunk()
, make sure to setc->is_resp = 0;
when your event handler finished writing its response. - Send and receive buffers, and the number of accepted connections, can grow indefinitely. If you need to keep a bound on them, you can do that in your respective event handlers:
static inline numconns(struct mg_mgr *mgr) { int n = 0; for (struct mg_connection *t = mgr->conns; t != NULL; t = t->next) n++; return n; } static void fn(struct mg_connection *c, int ev, void *ev_data) { if (ev == MG_EV_ACCEPT) { if (numconns(c->mgr) > LIMIT) { MG_ERROR(("Too many connections")); c->is_closing = 1; } } else if (ev == MG_EV_READ) { if (c->recv.len > LIMIT) { MG_ERROR(("Msg too large")); c->is_draining = 1; } } ... }
if (c->send.len > LIMIT) { MG_ERROR(("Stalled")); } else { // send }
- Mongoose uses this technique internally to shape traffic when serving large files; you don't need to do this unless you are dynamically sending data.
- On embedded environments, make sure the 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.- If using TLS, the
MG_MAX_RECV_SIZE
limit must accomodate the largest record (16424 bytes), and the value ofMG_IO_SIZE
will affect decryption performance when receiving large content.
- If using TLS, the
- On the other hand, in scenarios where data accumulates faster than it can be consumed, as for example, in TLS large file transfers, you might want to make
MG_IO_SIZE
larger to speed it up.
Architecture diagram
Mongoose Library can work on top of an existing TCP/IP stack - like on Windows, Mac, Linux, Zephyr RTOS, Azure RTOS, lwIP, etc. Also it implements its own TCP/IP stack with drivers - so in embedded environments, especially bare metal ones, it can be used stand-alone and does not need any exta software to implement networking.
Same goes with TLS. Mongoose can use 3rd party libraries like OpenSSL or mbedTLS, but it also implements its own TLS 1.3 stack. Hence, Mongoose Library can be a one-stop solution to provide the whole network functionality, including secure communication over TLS.
Build options
Mongoose source code ships in two files:
- mongoose.h - API definitions
- 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:
$ 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 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 (on ARM, see also MG_ARCH_CMSIS_RTOS2) |
MG_ARCH_AZURERTOS | Microsoft Azure RTOS |
MG_ARCH_CMSIS_RTOS1 | CMSIS-RTOS API v1 (Keil RTX) |
MG_ARCH_CMSIS_RTOS2 | CMSIS-RTOS API v2 (Keil RTX5, FreeRTOS) |
MG_ARCH_ZEPHYR | Zephyr RTOS |
MG_ARCH_TIRTOS | TI RTOS |
MG_ARCH_RP2040 | RP2040 SDK |
MG_ARCH_NEWLIB | Bare ARM GCC |
MG_ARCH_ARMCC | Keil MDK using ARM C Compiler v6 (armclang) or v5 (armcc in C99 mode) |
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 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_TLS | MG_TLS_NONE | Enable TLS support (disabled) |
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 for details. If the application uses large UDP messages, increase theMG_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, create a file called mongoose_config.h
,
with defines and includes that are relevant to your platform, for example:
#include <stdbool.h>
#include <stdarg.h>
#define MG_ARCH MG_ARCH_CUSTOM
#define MG_ENABLE_POSIX_FS 0
#define MG_IO_SIZE 256
Using JSON
Mongoose Library is often used to implement RESTful services, which use JSON format for data exchange. Therefore Mongoose provides functions to parse JSON strings and create JSON strings easily.
For example, the following event handler function handles a POST request
to the /api/sum
URI. The POST body is expected to be a JSON array of two
numbers, like [123.38, -2.72]
. Here is an example curl
command that
generates such request:
curl localhost:8000/api/sum -d '[123.38, -2.72]'
The handler returns the sum of those two numbers. The mg_json_get_num()
function is used to extract values from the JSON string, and mg_http_reply()
prints a JSON string back:
static void fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/sum"), NULL)) {
double num1 = 0.0, num2 = 0.0;
mg_json_get_num(hm->body, "$[0]", &num1); // Extract first number
mg_json_get_num(hm->body, "$[1]", &num2); // Extract second number
mg_http_reply(c, 200, "Content-Type: application/json\r\n",
"{%m:%g}\n", MG_ESC("result"), num1 + num2);
} else {
...
}
}
There is also a set of functions to ease server-side processing by means of RPC methods.
The following tutorials have working usage examples:
Built-in TCP/IP stack
Mongoose works on any system that provides BSD socket API. In other words,
it works on top of any BSD-compatible TCP/IP stack. That includes UNIX, Mac,
Windows systems, as well as some embedded TCP/IP stacks like lwIP.
However, Mongoose provides its own TCP/IP stack that can be activated by
the build option MG_ENABLE_TCPIP
set to 1
. It can be done either
by setting compiler flags or via mongoose_config.h
:
- Via the compiler flags (e.g.: gcc): add
-D MG_ENABLE_TCPIP=1
to the build flags - Via
mongoose_config.h
: add the following line:#define MG_ENABLE_TCPIP 1 // Enable built-in TCP/IP stack
Mongoose's TCP/IP stack provides
driver API
that makes it easy to create a driver. There is a bunch of built-in drivers
available, for example STM32 F2/F4/F7, STM32 H5/H7, SAME54, TM4C, W5500. You
can take a look at their implementation in the src/drivers/
directory. Every driver gets activated by its respective build option, for example the
STM32H5 driver needs MG_ENABLE_DRIVER_STM32H
set to 1
:
- Via the compiler flags: add
-DMG_ENABLE_DRIVER_STM32H=1
to the build flags - Via
mongoose_config.h
: add the following line:#define MG_ENABLE_DRIVER_STM32H 1 // Enable STM32H network driver
For more information on how to develop your own driver, follow the CMSIS-Driver tutorial
Built-in TLS1.3 stack
Mongoose implements a built-in TLS 1.3 stack. It can be enabled by one of the following ways:
- Via the compiler flags: add
-DMG_TLS=MG_TLS_BUILTIN
to the build flags - Via
mongoose_config.h
: add the following line:#define MG_TLS MG_TLS_BUILTIN // Enable built-in TLS 1.3 stack
Built-in OTA firmware updates
Along with networking functionality - Web UI, remote control, etc, developers need to develop Over-the-Air firmware updates. Thus Mongoose provides a built-in API for firmware updates. It is simple and can be inspected at src/ota.h file.
The default device dashboard example implements an example Web UI for firmware updates, which can be used as a skeleton for production implementation. You can take a look at the UI online at https://mongoose.ws/device-dashboard/ - login as admin/admin and click on the "Firmware Updates" sidebar link. It is going to look like this:
In essence, the update process requires 3 functions: ota_begin()
, ota_write()
, and ota_end()
.
The implementation is driven by the build option MG_OTA
. By default it is
set to #define MG_OTA MG_OTA_NONE
, which activates src/ota_dummy.c,
a stub implementation that does nothing. By setting #define MG_OTA MG_OTA_CUSTOM
in mongoose_config.h
you can create your own set of mg_ota_*
functions.
Devices with internal flash can set #define MG_OTA MG_OTA_FLASH
, which requires the mg_flash_*
API from src/device.h.
Those functions implement the mg_flash_*
API, including mg_flash_load()
and mg_flash_save()
. These are utility functions to load/save arbitrary data persistently in the last sector of the flash without a filesystem. See the
Nucleo-H563 baremetal example
with the device Web UI dashboard, firmware updates, and persistent storage of device configuration on flash. Note: the screenshot above
was taken from that example.
For more information on using this feature, follow the Firmware Update tutorial
Reference projects
Mongoose Library provides several reference projects - a "complete" projects that can be used as a base for a production firmware.
Web UI dashboard
IoT fleet management
See IoT fleet management guide
Mongoose Wizard
Mongoose Wizard is a no-code visual tool for creating connected applications on embedded devices. Mongoose Wizard generates ready-to-go projects for a variety of microcontrollers, as well as for Windows/Mac/Linux, which makes it very easy to develop/iterate/test. You can start using Mongoose Wizard immediately at https://mongoose.ws/wizard/.
Mongoose Wizard Concept
The core concept of Mongoose Wizard is the JSON configuration file, which describes your application options, target hardware / software, Web UI, and RESTful API.
Mongoose Wizard's frontend is a visual way of editing configuration file. Alternatively, it can be edited manually using your favorite editor.
Mongoose Wizard's backend takes a configuration file as an input, and generates a ready-to-go project as an output. You can download that project to your workstation and build/flash/run it. Alternatively, if you're using Chrome browser (which provides Web USB support), you can build and flash generated project directly from a browswer.
The network functionality is generated using Mongoose Library, and interfaces
with the rest of the firmware via a set of "glue" functions and structures.
The glue API is consolidated in the mongoose_glue.{c,h}
files, and is meant
to be modified by the user to "glue" the generated functionality to the
hardware.
For example, a Web UI can show a panel with a toggle button, and the generated glue code is:
- a structure with a boolean value
- a getter and setter functions for that structure
struct leds {
bool led1;
};
// Generated default code maps Web UI toggle button to the structure
struct leds s_leds = {false};
void glue_get_leds(struct leds *leds) {
// Insert your code here to sync s_leds to your hardware
*leds = s_leds;
}
void glue_set_leds(struct leds *leds) {
s_leds = *leds;
// Insert your code here to sync s_leds to your hardware
}
In order to glue that implementation to the hardware, a getter and setter function should be modified to "synchronise" the hardware to the structure:
void glue_get_leds(struct leds *leds) {
leds->led1 = gpio_read(LED1); // Read hardware, populate structure
}
void glue_set_leds(struct leds *leds) {
gpio_write(LED1, leds->led1); // Read structure, sync to hardware
}
Configuration file format
Configuration file has several sections, annotated below:
{
"version": "1.0.1", // format version
"api": { ... }, // RESTful API definitions
"ui": { ... }, // Web UI controls
"http": { ... }, // HTTP protocol support
"mqtt": { ... }, // MQTT protocol support
"dns": { ... }, // DNS/MDNS protocol support
"sntp": { ... }, // SNTP (network time sync) protocol support
"modbus": { ... }, // Modbus-TCP protocol support
"build": { ... } // Target hardware, IDE, OS
}
REST API
Below is the annotated REST API snippet from the configuration file:
"api": { // RESTful API endpoints. 4 types: object, array, action, upload, ota
"reboot": {
"type": "action", // An action endpoint maps to a button. A button click triggers an action
"read_level": 3, // Read access level
"write_level": 7, // Write access level
"value": false // Default action value - false (not triggered)
},
"firmware_update": {
"type": "ota", // A firmware update endpoint - maps to a button that trigger file upload
"read_level": 3, // Read access level
"write_level": 7 // Write access level
},
"file_upload": {
"type": "upload", // A file upload endpoint - maps to a button that trigger file upload
"read_level": 3, // Read access level
"write_level": 7 // Write access level
},
"state": { // struct state will be generated in the glue code
"type": "object", // An object endpoint maps to a Web UI panel, and a C structure
"readonly": true, // Optional attribute that prevents generation of the setter function
"read_level": 3, // Read access level
"attributes": { // Structure attributes
"speed": { "type": "int", "value": 42}, // Integer and its default value
"humidity": { "type": "double", "format": "%.4f", "value": 12.34}, // Double, its format and its default value
"version": { "type": "string", "size": 20, "value": "1.0.0"}, // String, its size and its default value
"online": { "type": "bool", "value": true} // A Boolean and its default value
}
},
"graph1": {
"type": "graph", // Graph endpoint, returns timeseries
"read_level": 3, // Read access level
"write_level": 7 // Write access level
"values": [ // Timeseries data: timestamp + value pairs
[1724576787, 20.3],
[1724576847, 27.2]
]
}
},
Web UI
"ui": {
"brand": "My Brand", // Brand name - shown on login and sidebar
"logo": "...", // A string with SVG logo
"buttons": "metal", // Buttons/toggle buttons color
"pages": [ // Describes UI pages. Each page is mapped on a sidebar
{
"title": "Dashboard", // Page title
"icon": "desktop", // Page icon
"level": 0, // Access level
"classes": "page", // CSS classes (optional)
"css": "", // Inline CSS styles (optional)
"layout": [ ... ] // Child elements (optional)
}
]
},
The "layout" attribute for pages describe UI elements, which could be nested. Here's the format for the UI element:
{
"type": "", // Optional. Can be "toggle", "action", "dropdown", "input", "upload", "ota". If absent, generates "div" element
"format": "hi!", // Optional. Static text / HTML code. Can contain references to API data: "Current temperature: ${state.temperature}"
"classes": "flex", // Optional. CSS classes
"css": "color: red;", // Optional. Inline CSS styles
"layout": [ ... ] // Optional. Nested elements
}
Widget types
Note: all elements can have classes
and css
attributes.
Container. Use flex
class for horizonal, flex flex-col
for vertical flex. The most common issue is to set the width of the child
elements - for example, panels, or titles. Here is the quick guide:
- To set a fixed width, use
"css": "flex: 0 0 10em;"
or"css": "flex: 0 0 20%;"
- To set an equal width, use
"classes": "flex-1"
- Otherwise, an item will be sized to accomodate its content
{ "classes": "flex", "layout": []}
Text / label
{ "format": "hi!", "classes": "text-sm font-bold", "css": "color: red;" }
Text can include HTML markup, and also special notation
${OBJECT.ATTRIBUTE}
, which substitutes by the respective API value. For example:Current temperature: <b>${state.temperature}</b>
Input
{ "type": "input", "ref": "OBJECT_API.ATTRIBUTE" }
Toggle
{ "type": "toggle", "ref": "OBJECT_API.ATTRIBUTE" }
Dropdown
{ "type": "dropdwon", "ref": "OBJECT_API.ATTRIBUTE", "options": "aa,bb,cc" }
Status (green or red circle)
{ "type": "status", "ref": "OBJECT_API.ATTRIBUTE" }
File Upload
{ "type": "updload", "ref": "UPLOAD_API_NAME" }
OTA (firmware update)
{ "type": "ota", "ref": "OTA_API_NAME" }
Action (rendered as a pushbutton, triggers an action on click)
{ "type": "action", "ref": "ACTION_API_NAME" }
Conditions
For any widget, it is possible to alter it's CSS class or style attribute
depending on the value of the API variables. That is implemented as an optional
"conditions"
array in the widget object, which contains a list of conditions.
Each condition has an expression and "classes"
and "css"
overrides. Example:
{
"classes": "flex-1 border rounded"",
"layout": [
/* Other elements... */
],
"conditions": [
{"expr": "state.temperature > 30", "css": "background: red;"},
]
},
API access levels
Each entry in the ui.api
generates a RESTful endpoint /api/ENTRY_NAME
.
An entry can be given a read and write access level. When a user logs in to
the UI, a user is given an access level, so the API's read and write level
can restrict who's able to read and write to the given API.
For example, a default dashboard project defines two users, "user" with
access level 3 and "admin" with access level 7. A "leds" API endpoint
allows "user" to read /api/leds
, but not modify it. "admin" can both
read and write to it:
$ curl -u admin:admin DEVICE_IP/api/leds
{"led1": false, "led2": false}
$ curl -u admin:admin DEVICE_IP/api/leds -d '{"led2": true}'
{"led1": false, "led2": true}
$ curl -u user:user DEVICE_IP/api/leds -d '{"led2": true}'
Forbidden
Web UI login
If Web UI login is activated on a settings page, then Wizard enables granular access control to pages, panels, and variables in the following way:
- Every user has an associated access level, visible to both UI and the device.
A User - defined
glue_authenticate()
function assigns group ID to the users. An access level is an integer from 1 to 9 - Access levels are hierarchical: the higher the level, the more privileged it is User group 9 is the most privileged. Level 1 is the least privileged
- Access level 0 means that a privilege access check is disabled, and anyone can access that API
HTTP protocol support
"http": {
"http": true, // Enable/disable HTTP server
"https": false, // Enable/disable HTTPS (secure) server
"ui": true, // Generate Web UI
"login": true // Enable/disable user login for the Web UI
},
Execute functions after a delay
Sometimes a certain function needs to be executed after a delay. For example,
device must be rebooted after a configuration change. In order to execute
such function after some delay, Mongoose API function mg_timer_add()
can
be used:
mg_timer_add(&g_mgr, 500, 0, my_func, NULL); // Run my_func after 500 ms delay
Tutorials
Development Environment
- Build tools - A guide on setting up a development environment for building and running Mongoose Library examples, as well as developing new applications.
Web UI
- Device dashboard - This tutorial shows an example of how to build a device dashboard, what can be very useful for headless devices.
- REST basics - This tutorial will show you the basics of how to implement and use a REST-based user interface (UI).
- Pure JavaScript UI - This tutorial will show you how to implement a plain JavaScript-based user interface (UI) over a REST-based backend.
- Preact UI - This tutorial will show you how to implement a Preact-based frontend for a user interface (UI) over a REST-based backend. We'll concentrate here on the basics of the Preact UI frontend.
- User authentication - This tutorial will show you how to implement a session login with a Preact-based user interface (UI) over a REST-based backend. We'll concentrate here on the basics of the login process.
- Data push - This tutorial will show you how to push data from the device to a JavaScript-based user interface (UI) running on the browser; either using WebSocket or a REST-based API.
- MQTT dashboard - This tutorial shows an example of how to build a remote device dashboard, what can be very useful to handle remote devices.
HTTP
- HTTP server - A basic HTTP server tutorials will show you how to configure a HTTP server, while you get familiar with the event manager and the server API.
- HTTP client - This tutorial will show you how to implement an HTTP client using Mongoose Library.
- HTTP proxy client - This tutorial will show you how to use Mongoose as an HTTP client in places where connections must be done through a proxy.
- HTTP reverse proxy - This tutorial will show you how to use Mongoose to implement a reverse proxy.
- File uploads - This tutorial will show you how to upload a file to a Mongoose web server.
- Huge response - This tutorial will show you how to send large amounts of data, larger than available buffer memory.
- Video stream - This tutorial will show you how to send a video stream as a series of MJPEG frames.
Websocket
- Websocket server - This tutorial demonstrates how Mongoose Library can be used to implement a Websocket server.
- Websocket client - This tutorial demonstrates how Mongoose Library can be used to implement a Websocket client.
- JSON-RPC over WS - This tutorial demonstrates how Mongoose Library can be used to implement JSON-RPC functionality over WebSocket.
MQTT
- MQTT client - This tutorial demonstrates how Mongoose Library can be used to implement an MQTT client.
- MQTT server - This tutorial demonstrates how Mongoose Library can be used to implement a simple MQTT 3.1.1 server.
- MQTT over WS client - This tutorial demonstrates how Mongoose Library can be used to implement an MQTT client that connects to the broker over WebSocket.
- AWS IoT - This tutorial demonstrates how Mongoose Library can be used to communicate with the AWS IoT service.
- MQTT dashboard - This tutorial shows an example of how to build an MQTT-controlled headless device.
SSL/TLS
- SSL/TLS - In this tutorial we describe how to enable SSL/TLS for servers and clients.
Firmware Update
- Firmware Update - This tutorial will show you how to use Mongoose Library functions to implement firmware updates.
Core
- Timers - This tutorial will guide you to configure a timer callback, a mechanism to perform some periodic actions.
- Multithreading - This tutorial will show you how to work with Mongoose on a multithreaded environment.
- Embedded filesystem - This tutorial shows an example of how to embed files in a packed filesystem that is linked into the server binary; forming a read-only file system that can be used to hold credentials and/or web files to be served.
Misc
- Error handling - If a connection fails for some reason, you can find answers what to do in this section.
- UART bridge - This tutorial shows an example of how to send UART data over the network.
- CMSIS driver - Write your own driver, port our generic CMSIS-Driver to your ARM device
SMTP
- SMTP client - This simple tutorial demonstrates how Mongoose Library can be used to implement an SMTP client over TLS.
TCP
- TCP client and server - This simple tutorial demonstrates how Mongoose Library can be used to implement TCP clients and servers, even over TLS.
- SOCKS5 server - This tutorial will show you how to use Mongoose Library functions to implement a TCP-based server, in this case a SOCKS5 proxy server.
UDP
- Captive DNS server - This tutorial demonstrates how Mongoose Library can be used to implement a captive DNS portal. It is usually required for device configuration.
- SNTP time sync - This tutorial will show you how to synchronize time with a remote SNTP server.
- SSDP search - This tutorial demonstrates how to use Mongoose Library for UDP communication, by performing an SSDP search.
TCP/IP stack drivers
- CMSIS Driver - Write your own driver, port our generic CMSIS-Driver to your ARM device
- Driver for RNDIS - Write your own driver, use TinyUSB and use your computer to control your device via USB
- RMII driver with the RP2040 PIOs - Write your own driver, low-level drive of a PHY chip with a software minimalistic MAC controller
STM32
Nucleo-F207ZG
Nucleo-F429ZI
Framework | OS | IP stack | Tutorial |
---|---|---|---|
Cube | baremetal | built-in | Wizard |
Cube | FreeRTOS | built-in | Wizard |
GCC+make | baremetal | built-in | Wizard |
GCC+make | FreeRTOS | built-in | Wizard |
Cube | FreeRTOS | lwIP | tutorial |
Keil | baremetal | built-in | tutorial |
Keil | FreeRTOS | built-in | tutorial |
Zephyr | Zephyr | Zephyr | Wizard |
GCC+make | baremetal | built-in USB | tutorial |
Nucleo-F439ZI
Framework | OS | IP stack | Tutorial |
---|---|---|---|
Cube | baremetal | built-in | Wizard |
Cube | FreeRTOS | built-in | Wizard |
GCC+make | baremetal | built-in | Wizard |
GCC+make | FreeRTOS | built-in | Wizard |
Cube | FreeRTOS | lwIP | tutorial |
Keil | baremetal | built-in | tutorial |
Keil | FreeRTOS | built-in | tutorial |
Zephyr | Zephyr | Zephyr | Wizard |
GCC+make | baremetal | built-in USB | tutorial |
Nucleo-F746ZG
Framework | OS | IP stack | Tutorial |
---|---|---|---|
Cube | baremetal | built-in | Wizard |
Cube | FreeRTOS | built-in | Wizard |
GCC+make | baremetal | built-in | Wizard |
GCC+make | FreeRTOS | built-in | Wizard |
Cube | FreeRTOS | lwIP | tutorial |
Keil | baremetal | built-in | tutorial |
Keil | FreeRTOS | built-in | tutorial |
Keil | CMSIS-RTOS v1 (RTX) | built-in | tutorial |
Keil | CMSIS-RTOS v2 | built-in | tutorial |
Keil | CMSIS-RTOS v2 | lwIP | tutorial |
Keil | FreeRTOS | lwIP | tutorial |
Keil | FreeRTOS | FreeRTOS+TCP | tutorial |
Keil | RTX | MDK | tutorial |
Keil | RTX5 | MDK | tutorial |
Zephyr | Zephyr | Zephyr | Wizard |
GCC+make | FreeRTOS | FreeRTOS+TCP | example |
GCC+make | baremetal | built-in USB | tutorial |
Nucleo-F756ZG
Framework | OS | IP stack | Tutorial |
---|---|---|---|
Cube | baremetal | built-in | Wizard |
Cube | FreeRTOS | built-in | Wizard |
GCC+make | baremetal | built-in | Wizard |
GCC+make | FreeRTOS | built-in | Wizard |
Cube | FreeRTOS | lwIP | tutorial |
Keil | baremetal | built-in | Wizard |
Keil | FreeRTOS | built-in | tutorial |
Keil | CMSIS-RTOS v1 (RTX) | built-in | tutorial |
Keil | CMSIS-RTOS v2 | built-in | tutorial |
Keil | CMSIS-RTOS v2 | lwIP | tutorial |
Keil | FreeRTOS | lwIP | tutorial |
Keil | FreeRTOS | FreeRTOS+TCP | tutorial |
Keil | RTX | MDK | tutorial |
Keil | RTX5 | MDK | tutorial |
Zephyr | Zephyr | Zephyr | Wizard |
GCC+make | FreeRTOS | FreeRTOS+TCP | example |
GCC+make | baremetal | built-in USB | tutorial |
Nucleo-F767ZI
Nucleo-H563ZI
STM32H573I-DK
Nucleo-H723ZG
Framework | OS | IP stack | Tutorial |
---|---|---|---|
Cube | baremetal | built-in | Wizard |
Cube | FreeRTOS | built-in | Wizard |
GCC+make | baremetal | built-in | Wizard |
GCC+make | FreeRTOS | built-in | Wizard |
Zephyr | Zephyr | Zephyr | Wizard |
GCC+make | baremetal | built-in | device dashboard |
GCC+make | baremetal | built-in | MQTT dashboard device |
STM32H735G-DK
Nucleo-H743ZI2
STM32H745I-Disco
STM32H747I-Disco
Nucleo-H753ZI
Nucleo-H755ZI-Q
Nucleo-H7S3L8
Framework | OS | IP stack | Tutorial |
---|---|---|---|
GCC+make | baremetal | built-in | Wizard |
Nucleo-G031K8 + W5500 module
Framework | OS | IP stack | Example |
---|---|---|---|
GCC+make | baremetal | built-in | example |
NXP
FRDM-MCXN947
MIMXRT1020-EVK
MIMXRT1024-EVK
Framework | OS | IP stack | Tutorial |
---|---|---|---|
GCC+make | baremetal | built-in | Wizard |
MIMXRT1040-EVK
Framework | OS | IP stack | Tutorial |
---|---|---|---|
GCC+make | baremetal | built-in | Wizard |
MIMXRT1060-EVKB
MIMXRT1064-EVK
MIMXRT1170-EVKB
Teensy 4.1 + expansion board
Framework | OS | IP stack | Tutorial |
---|---|---|---|
Arduino | built-in | Wizard |
FRDM-K64F
Framework | OS | IP stack | Example |
---|---|---|---|
MCUXpresso | FreeRTOS | LwIP | example |
FRDM-K66F
Framework | OS | IP stack | Tutorial |
---|---|---|---|
MCUXpresso | FreeRTOS | LwIP | tutorial |
NXP LPC54S018M-EVK
Framework | OS | IP stack | Tutorial |
---|---|---|---|
MCUXpresso | FreeRTOS | LwIP | tutorial |
ESP
ESP32 DevkitC
Framework | OS | IP stack | Tutorial |
---|---|---|---|
ESP-IDF | FreeRTOS | LwIP | device dashboard |
ESP-IDF | FreeRTOS | LwIP | UART bridge |
M5 STAMP PICO
Framework | OS | IP stack | Tutorial |
---|---|---|---|
ESP-IDF | FreeRTOS | LwIP | tutorial |
XIAO-ESP32-C3
Framework | OS | IP stack | Tutorial |
---|---|---|---|
ESP-IDF | FreeRTOS | LwIP | tutorial |
ESP8266 DevkitC
Framework | OS | IP stack | Tutorial |
---|---|---|---|
RTOS SDK | SDK (FreeRTOS) | SDK (LwIP) | tutorial |
Texas Instruments
EK-TM4C1294xxx
Infineon
XMC-4700
Framework | OS | IP stack | Example |
---|---|---|---|
Keil | RTX | LwIP | example |
Raspberry Pi RPxxxx
Pico (RP2040)
Framework | OS | IP stack | Tutorial |
---|---|---|---|
RPI PICO C SDK | baremetal | built-in USB RNDIS | device dashboard |
RPI PICO C SDK | baremetal | built-in USB RNDIS | MQTT dashboard device |
Pico 2 (RP2350)
Pico W (RP2040)
Framework | OS | IP stack | Tutorial |
---|---|---|---|
RPI PICO C SDK | FreeRTOS | LwIP | tutorial |
Pico + LAN8720 module
Framework | OS | IP stack | Tutorial |
---|---|---|---|
RPI PICO C SDK | baremetal | built-in | tutorial |
Pico + W5500 module
Framework | OS | IP stack | Tutorial |
---|---|---|---|
RPI PICO C SDK | baremetal | built-in | tutorial |
W5500-EVB-Pico
W5500-EVB-Pico2
Framework | OS | IP stack | Tutorial |
---|---|---|---|
RPI PICO C SDK | baremetal | built-in | tutorial |
XIAO RP2040
Framework | OS | IP stack | Tutorial |
---|---|---|---|
RPI PICO C SDK | baremetal | built-in USB RNDIS | tutorial |
Microchip SAMxx
SAM E54 Xplained Pro
Framework | OS | IP stack | Example |
---|---|---|---|
GCC+make | baremetal | built-in | device dashboard |
GCC+make | baremetal | built-in | MQTT client |
XIAO M0 + W5500 module
Framework | OS | IP stack | Tutorial |
---|---|---|---|
Arduino | built-in | tutorial |
WCH
CH32V307
Framework | OS | IP stack | Example |
---|---|---|---|
GCC+make | baremetal | built-in | example |
Embedded Linux
Raspberry Pi
Framework | Code | Tutorial |
---|---|---|
Raspberry Pi OS | tutorial |
Video Tutorials
API Reference
Core
struct mg_addr
struct mg_addr {
uint8_t ip[16]; // Holds IPv4 or IPv6 address, in network byte order
uint16_t port; // TCP or UDP port in network byte order
uint8_t scope_id; // IPv6 scope ID
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
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
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()
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 tomg_mgr
structure that needs to be initialized
Return value: none
Usage example:
struct mg_mgr mgr;
mg_mgr_init(&mgr);
mg_mgr_poll()
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, sendMG_EV_READ
event - See if there is data in the
c->send
buffer, and write it, sendMG_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 usems
- a timeout in milliseconds
Return value: none
Usage example:
while (running == true) mg_mgr_poll(&mgr, 1000 /* 1 sec */);
mg_mgr_free()
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:
struct mg_mgr mgr;
mg_mgr_init(&mgr);
while (running == true) mg_mgr_poll(&mgr, 1000); // Event loop
mg_mgr_free(&mgr);
mg_listen()
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 useurl
- a URL. Specifies local IP address and port to listen on, e.g.tcp://127.0.0.1:1234
orudp://0.0.0.0:9000
fn
- an event handler functionfn_data
- an arbitrary pointer, which will be stored in the connection structure asc->fn_data
, so the event handler can use it when called.
Return value: created connection, or NULL
on error.
Usage example:
struct mg_connection *c = mg_listen(&mgr, "tcp://127.0.0.1:8080", fn, NULL);
mg_connect()
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 useurl
- A URL, specifies remote IP address/port to connect to, e.g.http://a.com
fn
- An event handler functionfn_data
- an arbitrary pointer, which will be stored in the connection structure asc->fn_data
, so the event handler can use it when called.
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:
struct mg_connection *c = mg_connect(&mgr, "http://example.org", fn, NULL);
mg_send()
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 pointerdata
- A pointer to data to append to the send buffersize
- A data size
Return value: true
if data appended successfully and false
otherwise
Usage example:
mg_send(c, "hi", 2); // Append string "hi" to the output buffer
mg_wakeup()
void mg_wakeup(struct mg_mgr *mgr, unsigned long id, const void *data, size_t size);
Any thread/task can send data
, size
to Mongoose manager executing in
another thread. This is the only Mongoose function that can be called from a
different task/thread. Calling
this function wakes up the event manager and generates an MG_EV_WAKEUP
event
in the respective event handler. Call mg_wakeup_init()
in the event manager thread before first using it.
The data could be anything. It could be a structure. Or it could be a pointer.
The receiving connection gets MG_EV_WAKEUP
, and gets that data as a chunk
of memory: struct mg_str *data = ev_data
. Note that the sent data should be
small, ideally less than 512 bytes. If you need to send a large piece of data,
allocate it and send a pointer instead - see examples below.
Parameters:
mgr
- An event managerid
- A destination connection IDdata
- A pointer to data to append to the send buffersize
- A data size
Usage example:
Sending small data
// Sender side:
struct foo foo = {0}; // Small structure, size < 512 bytes
mg_wakeup(mgr, id, &foo, sizeof(foo)); // Send a structure
// Receiver side:
if (ev == MG_EV_WAKEUP) {
struct mg_str *data = (struct mg_str *) ev_data;
struct foo *foo = (struct foo *) data->buf;
}
Sending large data. Sender allocates it, receiver deallocates
// Sender side:
struct foo *foo = malloc(sizeof(*foo)); // Big structure, allocate it
mg_wakeup(mgr, id, &foo, sizeof(foo)); // Send a pointer to structure
// Receiver side:
if (ev == MG_EV_WAKEUP) {
struct mg_str *data = (struct mg_str *) ev_data;
struct foo *foo = * (struct foo **) data->buf;
// Do something with foo ...
free(foo); // Deallocate foo
}
mg_wakeup_init()
void mg_wakeup_init(struct mg_mgr *mgr);
Initialize the wakeup scheme used by mg_wakeup()
Parameters:
mgr
- An event manager
Usage example:
mg_wakeup_init(&mgr); // Initialise wakeup socket pai
mg_printf(), mg_vprintf()
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 for the list of supported format specifiers
Parameters:
c
- a connection pointerfmt
- a format string inprintf
semantics
Return value: number of bytes appended to the output buffer.
Usage example:
mg_printf(c, "Hello, %s!", "world"); // Add "Hello, world!" to output buffer
mg_wrapfd()
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 wrapmgr
- An event managerfn
- A pointer to event handler functionfn_data
- an arbitrary pointer, which will be stored in the connection structure asc->fn_data
, so the event handler can use it when called.
Return value: Pointer to the created connection or NULL
in case of error
mg_hello()
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 examplehttp://0.0.0.0:8000
HTTP
struct mg_http_header
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
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()
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 managerurl
- A URL, specifies local IP address and port to listen on, e.g.http://0.0.0.0:8000
fn
- An event handler functionfn_data
- an arbitrary pointer, which will be stored in the connection structure asc->fn_data
, so the event handler can use it when called.
Return value: Pointer to created connection or NULL
in case of error
Usage example:
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()
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 managerurl
- A URL, specifies remote URL, e.g.http://google.com
fn
- An event handler functionfn_data
- an arbitrary pointer, which will be stored in the connection structure asc->fn_data
, so the event handler can use it when called.
Return value: Pointer to created connection or NULL
in case of error
Usage example:
struct mg_connection *c = mg_http_connect(&mgr, "http://google.com", fn, NULL);
if (c == NULL) fatal_error("Cannot create connection");
mg_http_status()
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()
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 requestbuf_len
- Buffer length
Return value: -1 on error, 0 if a message is incomplete, or the length of request
Usage example:
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()
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 stringlen
- A request string lengthhm
- A pointer to a structure to store parsed request
Return value: request length (see mg_http_get_request_len()
)
Usage example:
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()
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 pointerfmt
- A string, format specified inprintf
semantics
Return value: None
Usage example:
mg_http_printf_chunk(c, "Hello, %s!", "world");
mg_http_write_chunk()
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 pointerbuf
- Data to writelen
- Data length
Return value: None
Usage example:
mg_http_write_chunk(c, "hi", 2);
struct mg_http_serve_opts
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.
In addition to overwriting autodetection based on an extension, you can also use *
as an extension in mime_types
to force a particular MIME type for unknown extensions:
sopts.mime_types = "*=preferred/default,txt=override/text"
mg_http_serve_dir()
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()
NOTE: In order to enable SSI, you need to set the
-DMG_ENABLE_SSI=1
build flag.
NOTE: Avoid double dots
..
in theroot_dir
. If you need to reference an upper-level directory, use an absolute path.
Parameters:
c
- Connection to usehm
- HTTP message, that should be servedopts
- Serve options. Note thatopts.root_dir
can optionally accept extra comma-separateduri=path
pairs, see example below
Return value: None
Usage example:
// Mongoose events handler
void fn(struct mg_connection *c, int ev, void *ev_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()
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
, if not NULL, must end with\r\n
.
Parameters:
c
- Connection to usehm
- HTTP message to servepath
- Path to file to serveopts
- Serve options
Return value: None
Usage example:
// Mongoose events handler
void fn(struct mg_connection *c, int ev, void *ev_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()
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 usestatus_code
- An HTTP response codeheaders
- 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:
mg_http_reply(c, 200, "Content-Type: application/json\r\n", "{\"result\": %d}", 123);
Send JSON response:
char *json = mg_mprintf("{%m:%d}", MG_ESC("name"), 123);
mg_http_reply(c, 200, "Content-Type: application/json\r\n", "%s\n", json);
free(json);
Send a 302 redirect:
mg_http_reply(c, 302, "Location: /\r\n", "");
Send error:
mg_http_reply(c, 403, "", "%s", "Not Authorized\n");
mg_http_get_header()
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 headername
- Header name
Return value: HTTP header value or NULL
if not found
Usage example:
// Mongoose event handler
void fn(struct mg_connection *c, int ev, void *ev_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->buf);
} else {
mg_http_reply(c, 200, "", "Oh no, header is not set...");
}
}
}
mg_http_get_header_var()
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 headername
- variable name name
Return value: a requested variable, or an empty string.
Usage example:
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()
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 stringname
- a variable name to fetch
Return value: variable's value. If not found, it is a NULL string.
// 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()
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 bodyname
- Variable namebuf
- Buffer to write decoded variablelen
- Buffer size
Return value: Length of decoded variable. A zero or negative value means error
Usage example:
char buf[100] = "";
mg_http_get_var(&hm->body, "key1", buf, sizeof(buf)) {
...
}
mg_http_creds()
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 credentialsuser
- buffer to receive user nameuserlen
- size ofuser
bufferpass
- buffer to receive passwordpasslen
- size ofpass
buffer
Return value: None
Usage example:
// Mongoose events handler
void fn(struct mg_connection *c, int ev, void *ev_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_bauth()
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 useuser
- User namepass
- Password
Return value: None
Usage example which uses Basic auth to create Stripe subscription:
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
// 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()
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 bodyoffset
- Start offsetpart
- Pointer tostruct 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 ):
struct mg_http_part part;
size_t pos = 0;
while ((pos = mg_http_next_multipart(body, pos, &part)) != 0) {
MG_INFO(("Chunk name: [%.*s] filename: [%.*s] length: %lu bytes",
part.name.len, part.name.buf,
part.filename.len, part.filename.buf, part.body.len));
// Use this chunk ....
}
A diagram below shows how mg_http_next_multipart()
in action:
mg_http_upload()
long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
struct mg_fs *fs, const char *dir, 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?file=firmware.bin&offset=2048 HTTP/1.1
. If the offset is 0, then the
file is truncated. It is the client's responsibility to divide files into
smaller chunks and send a sequence of POST requests that will be handled by
this function. The full path will be checked for sanity
Parameters:
c
- a connectionhm
- a parsed HTTP messagefs
- a filesystem where to write the files, e.g.&mg_fs_posix
dir
- a directory path where to write the filesmax_size
- maximum allowed file size
Return value: file size after write, or negative number on error
Usage example:
static void fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/upload"), NULL)) {
mg_http_upload(c, hm, &mg_fs_posix, "/tmp", 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
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.
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:
#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
// Mongoose events handler
void fn(struct mg_connection *c, int ev, void *ev_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 for data frames. when calling mg_ws_send() or mg_ws_printf() below
mg_ws_connect()
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 useurl
- Specifies remote URL, e.g.http://google.com
fn
- An event handler functionfn_data
- an arbitrary pointer, which will be stored in the connection structure asc->fn_data
, so the event handler can use it when called.fmt
- format string inprintf
semantics for additional HTTP headers, or NULL. See mg_snprintf for the list of supported format specifiers
Return value: Pointer to created connection or NULL
on error
Usage example:
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()
void mg_ws_upgrade(struct mg_connection *c, struct mg_http_message *,
const char *fmt, ...);
Upgrade given HTTP connection to WebSocket. Parameter fmt
is a printf-like
format string for the extra HTTP headers returned to the client in a
WebSocket handshake. Set it to NULL
if no extra headers need to be passed.
Parameters:
c
- Connection to usehm
- HTTP messagefmt
- format string inprintf
semantics for additional HTTP headers, or NULL. See mg_snprintf for the list of supported format specifiers
Return value: None
Usage example:
// Mongoose events handler
void fn(struct mg_connection *c, int ev, void *ev_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()
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 usebuf
- Data to sendlen
- Data sizeop
- WebSocket message type, see WebSocket message type above
Return value: sent bytes count
Usage example:
// Mongoose events handler
void fn(struct mg_connection *c, int ev, void *ev_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()
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 useop
- WebSocket message type, see WebSocket message type abovefmt
- format string inprintf
semantics. See mg_snprintf for the list of supported format specifiers
Return value: sent bytes count
Usage example:
mg_ws_printf(c, WEBSOCKET_OP_TEXT, "Hello, %s!", "world");
mg_ws_wrap()
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 for full example.
Parameters:
c
- Connection to uselen
- Bytes count to convertop
- Websocket message type (seemg_ws_send
)
Return value: New size of connection output buffer
Usage example:
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()
struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, const char *url,
mg_event_handler_t fn, void *fn_data)
Connect to an SNTP server.
Parameters:
mgr
- Event manager to useurl
- Specifies remote URL,time.google.com
if NULL.fn
- A user event handler function, use NULL if you don't need onefn_data
- an arbitrary pointer, which will be stored in the connection structure asc->fn_data
, so the event handler can use it when called.
Return value: Pointer to created connection or NULL
on error
Simplest usage example: see mg_now()
Full usage example:
static void sntp_cb(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_SNTP_TIME) {
// Time received, the internal protocol handler updates what mg_now() returns
uint64_t curtime = mg_now();
// otherwise, you can process the server returned data yourself
uint64_t epoch_millis = *(uint64_t *) ev_data;
}
}
...
mg_sntp_connect(mgr&, NULL /* connect to time.google.com */, sntp_cb, NULL);
mg_sntp_request()
void mg_sntp_request(struct mg_connection *c)
Send time request to SNTP server
Parameters:
c
- Connection to use
Return value: None
Usage example:
mg_sntp_request(c);
MQTT
struct mg_mqtt_opts
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 topic; // message/subscription topic
struct mg_str message; // message content
uint8_t qos; // 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
uint16_t retransmit_id; // For PUBLISH, init to 0
bool retain; // Retain flag
bool clean; // Clean session flag
struct mg_mqtt_prop *props; // MQTT5 props array
size_t num_props; // number of props
struct mg_mqtt_prop *will_props; // Valid only for CONNECT packet (MQTT5)
size_t num_will_props; // Number of will props
};
Structure used when connecting to a broker and when sending messages, to specify connection options and last-will message or to specify message and options
struct mg_mqtt_message
struct mg_mqtt_message {
struct mg_str topic; // Parsed topic for PUBLISH
struct mg_str data; // Parsed message for PUBLISH
struct mg_str dgram; // Whole MQTT packet, including headers
uint16_t id; // 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
size_t props_start; // Offset to the start of the properties (MQTT5)
size_t props_size; // Length of the properties
};
Structure representing an MQTT packet, either control or data
mg_mqtt_connect()
struct mg_connection *mg_mqtt_connect(struct mg_mgr *mgr, const char *url,
const struct mg_mqtt_opts *opts,
mg_event_handler_t fn, void *fn_data);
Create a client MQTT connection
Note: This function does not connect to a broker; it allocates the required resources and
starts the TCP connection process. Once that connection is established, an MG_EV_CONNECT
event is
sent to the connection event handler, then the MQTT connection process is started (by means of mg_mqtt_login()); and once the MQTT connection request gets a response from the broker, an MG_EV_MQTT_OPEN
event is sent to the connection event handler; connection results are inside a struct mg_mqtt_message
Parameters:
mgr
- Event manager to useurl
- Specifies the broker URL, e.g.mqtt://cloud.hivemq.com
opts
- pointer to MQTT options like client ID, clean session, last will, etc. Can be NULLfn
- The event handler functionfn_data
- an arbitrary pointer, which will be stored in the connection structure asc->fn_data
, so the event handler can use it when called.
Return value: pointer to the created connection or NULL
on error
Usage example:
void fn(struct mg_connection *c, int ev, void *evd, void *fnd) {
if (ev == MG_EV_CONNECT) {
// TCP connection succeeded,
// If target URL is TLS, set it up
} else if (ev == MG_EV_MQTT_OPEN) {
// MQTT connection process finished
struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data;
if(mm->ack) // MQTT connection succeeded
}
}
mg_mqtt_connect(&mgr, "mqtt://test.org:1883", NULL, fn, NULL);
or
struct mg_mqtt_opts opts = {.qos = 1,
.retain = true,
.topic = mg_str("mytopic"),
.message = mg_str("goodbye")};
mg_mqtt_connect(&mgr, "mqtt://test.org:1883", &opts, fn, NULL);
mg_mqtt_listen()
struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url,
mg_event_handler_t fn, void *fn_data);
Create an MQTT listener (act like a broker).
Parameters:
mgr
- Event manager to useurl
- Specifies the local IP address and port to listen on, e.g.mqtt://0.0.0.0:1883
fn
- The event handler functionfn_data
- an arbitrary pointer, which will be stored in the connection structure asc->fn_data
, so the event handler can use it when called.
Return value: Pointer to the created connection or NULL
on error
Usage example:
struct mg_connection *c = mg_mqtt_listen(&mgr, "mqtt://0.0.0.0:1883", fn, arg);
if (c == NULL) return -1; // Could not create connection
mg_mqtt_login()
void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts);
Send MQTT CONNECT request. Once the MQTT connection request gets a response from the broker, an MG_EV_MQTT_OPEN
event is sent to the connection event handler.
This function is usually called by mg_mqtt_connect(), you will only need to call it when you manually start the MQTT connect process, e.g: when using MQTT over WebSocket. Connection results are inside a struct mg_mqtt_message
Parameters:
c
- Connection to useopts
- pointer to MQTT connect options, containing user name and password to use, if any, and other options
Return value: None
Usage example:
// Mongoose event handler
void fn(struct mg_connection *c, int ev, void *evd, void *fnd) {
if (ev == MG_EV_WS_OPEN) {
// WS connection established. Perform MQTT login
struct mg_mqtt_opts opts = {.qos = 1,
.retain = true,
.topic = mg_str("mytopic"),
.message = mg_str("goodbye")};
mg_mqtt_login(c, &opts);
} else if (ev == MG_EV_MQTT_OPEN) {
// MQTT connection process finished
struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data;
if(mm->ack) // MQTT connection succeeded
}
}
mg_mqtt_pub()
uint16_t mg_mqtt_pub(struct mg_connection *c, const struct mg_mqtt_opts *opts);
Publish a message to a specified topic, each contained in a struct msg_str
Note: This function does not actually send the message, it delivers to the underlying TCP/IP stack which will be checked later when the manager runs.
Note that Mongoose does not handle retries for QoS 1 and 2. That has to be handled by the application in the event handler, if needed. You can check if a publish request with QoS 1 or 2 succeeded by catching MG_EV_MQTT_CMD
events and checking for reception of PUBACK/PUBREC and PUBCOMP messsages; and their result codes inside a struct mg_mqtt_message
Parameters:
c
- Connection to useopts
- pointer to publish MQTT options, like QoS, and retain flag. The message body is expected atopts->message
, the topic atopts->topic
; both as mg_str
Return value: When using QoS other than 0, this function returns the id
field sent to the broker, suitable to be held in opts->retransmit_id
for a possible retransmission. See this tutorial. Discard the returned value if not interested in doing retransmissions, and initialize opts->retransmit_id
as 0
.
Usage example:
struct mg_mqtt_opts pub_opts = {.topic = mg_str("mytopic"),
.message = mg_str("hello"),
.qos = 1,
.retain = false};
mg_mqtt_pub(c, &pub_opts);
mg_mqtt_sub()
void mg_mqtt_sub(struct mg_connection *, const struct mg_mqtt_opts *opts);
Subscribe to a topic specified as a struct msg_str
. You can check if a subscription request succeeded by catching MG_EV_MQTT_CMD
events and checking for reception of a PUBACK messsage and its result code inside the struct mg_mqtt_message
Reception of a message will trigger an MG_EV_MQTT_MSG
event providing a struct mg_mqtt_message. Note that Mongoose does not handle broker retries for QoS 2 and duplicated messages have to be handled by the application in the event handler, if required
Parameters:
c
- Connection to useopts
- pointer to subscription MQTT options, like QoS. The topic is expected atopts->topic
as an mg_str
Return value: None
// Mongoose event handler
void fn(struct mg_connection *c, int ev, void *evd, void *fnd) {
if (ev == MG_EV_MQTT_MSG) {
struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data;
MG_INFO(("%.*s\t%.*s", (int) mm->topic.len, mm->topic.buf),
(int) mm->data.len, mm->data.buf);
}
}
struct mg_mqtt_opts sub_opts = {.topic = mg_str("mytopic"),
.qos = 1};
mg_mqtt_sub(c, &sub_opts);
mg_mqtt_send_header()
void mg_mqtt_send_header(struct mg_connection *c, uint8_t cmd, uint8_t flags, uint32_t len);
Send an MQTT command header. Useful in handling QoS 2 and MQTT server implementations. The command can be one of the following:
#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 usecmd
- Command (see above)flags
- Command flags (see MQTT specs)len
- Size of what follows this header
Return value: None
Usage example:
// Mongoose event handler
void fn(struct mg_connection *c, int ev, void *ev_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()
void mg_mqtt_ping(struct mg_connection *c);
Send an MQTT_CMD_PINGREQ
command via mg_mqtt_send_header()
Parameters:
c
- Connection to use
Return value: None
Usage example:
// Send periodic pings to all (MQTT over) 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()
int mg_mqtt_parse(const uint8_t *buf, size_t len, struct mg_mqtt_message *m);
Parse a buffer and fill an mg_mqtt_message
structure if it contains a valid MQTT packet.
Parameters:
buf
- buffer with MQTT packet to parselen
- buffer lengthm
- pointer to a struct mg_mqtt_message to be filled with the parsed message
Return value: MQTT_OK
if message is successfully parsed, MQTT_INCOMPLETE
if message
isn't fully received and MQTT_MALFORMED
if message has a wrong format.
Usage example:
// 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;
}
mg_mqtt_disconnect()
void mg_mqtt_disconnect(struct mg_connection *c, const struct mg_mqtt_opts *opts);
End an client MQTT connection
Note: This function does not destroy a connection; it just sends a disconnect request to the broker. Once the connection is terminated, an MG_EV_CLOSE
event will be sent to the connection event handler
Parameters:
c
- Connection to useopts
- pointer to MQTT options, can be NULL for MQTT 3.1.1 connections
Return value: None
TLS
struct mg_tls_opts
struct mg_tls_opts {
struct mg_str ca; // CA certificate; for both listeners and clients. PEM or DER
struct mg_str cert; // Certificate. PEM or DER
struct mg_str key; // Private key. PEM or DER
struct mg_str name; // If not empty, enable server name verification
};
TLS options structure:
ca
- Certificate Authority, an mg_str. Used to verify the certificate that the other end sends to us. If NULL, then server authentication for clients and client authentication for servers are disabledcert
- Our own certificate; an mg_str. If NULL, then we don't authenticate ourselves to the other peerkey
- Our own private key; an mg_str. Sometimes, a certificate and its key are bundled in a single PEM file, in which case the values forcert
andkey
could be the samename
- Server name; an mg_str. If not empty, enable server name verification
NOTE: if both
ca
andcert
are set, then two-way (mutual) TLS authentication is enabled, both sides authenticate each other. Usually, for one-way (server) TLS authentication, server connections set bothkey
andcert
, whilst clients onlyca
and/or possiblyname
.
For more information on developing TLS clients and servers, and how to load credentials, see the TLS tutorial
mg_tls_init()
void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *);
Initialise TLS on a given connection.
NOTE: The mbedTLS implementation uses
mg_random
as RNG. Themg_random
function can be overridden by settingMG_ENABLE_CUSTOM_RANDOM=1
and defining your ownmg_random()
implementation.
Parameters:
c
- Connection, for which TLS should be initializedopts
- TLS initialization parameters
Return value: None
Usage example:
// client event handler:
if (ev == MG_EV_CONNECT) {
struct mg_tls_opts opts = {.ca = mg_str(s_tls_ca)};
mg_tls_init(c, &opts);
// server event handler:
if (ev == MG_EV_ACCEPT) {
struct mg_tls_opts opts = {.cert = mg_str(s_tls_cert),
.key = mg_str(s_tls_key)};
mg_tls_init(c, &opts);
For more information on developing TLS clients and servers, see the TLS tutorial
Timer
mg_timer_add()
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 tomg_mgr
event manager structurems
- An interval in millisecondsflags
- Timer flags bitmask,MG_TIMER_REPEAT
andMG_TIMER_RUN_NOW
fn
- Function to invokefn_data
- Function argument to be passed on call
Return value: Pointer to created timer
Usage example:
void timer_fn(void *data) {
// ...
}
mg_timer_add(mgr, 1000, MG_TIMER_REPEAT, timer_fn, NULL);
struct mg_timer
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()
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 tomg_timer
list headt
- Pointer tomg_timer
that should be initializedms
- An interval in millisecondsflags
- Timer flags bitmask,MG_TIMER_REPEAT
andMG_TIMER_RUN_NOW
fn
- Function to invokefn_data
- Function argument to be passed on call
Return value: None
Usage example:
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()
void mg_timer_free(struct mg_timer **head, struct mg_timer *t);
Free timer, remove it from the internal timers list.
Parameters:
head
- Pointer tomg_timer
list headt
- Timer to free
Return value: None
Usage example:
struct mg_timer timer;
// ...
mg_timer_free(&timer);
mg_timer_poll()
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 tomg_timer
list headuptime_ms
- current timestamp
Return value: None
Usage example:
mg_timer_poll(mg_millis());
Time
mg_millis()
uint64_t mg_millis(void);
Return current uptime in milliseconds.
Parameters: None
Return value: Current uptime
Usage example:
uint64_t uptime = mg_millis();
mg_now()
uint64_t mg_now(void);
Return current time in milliseconds, requires an SNTP server connection (see mg_sntp_connect())
Parameters: None
Return value: If an SNTP server connection has been configured, returns current time. Otherwise, returns current uptime just like mg_millis()
Usage example:
mg_sntp_connect(mgr&, NULL /* connect to time.google.com */, NULL, NULL);
...
uint64_t curtime = mg_now();
String
struct mg_str
struct mg_str {
const char *buf; // 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
buf
is not necessarily zero-terminated, do not use libc string functions against it - likestrlen()
orsscanf()
.
mg_str()
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:
struct mg_str str = mg_str("Hello, world!);
mg_str_n()
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 createdmg_str
n
- String length
Return value: Created Mongoose string
Usage example:
struct mg_str str = mg_str_n("hi", 2);
mg_casecmp()
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:
if (mg_casecmp("hello", "HELLO") == 0) {
// Strings are equal
}
mg_strcmp()
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 than the second, and less than zero otherwise
Usage example:
struct mg_str str1 = mg_str("hello");
struct mg_str str2 = mg_str("hello");
if (mg_strcmp(str1, str2) == 0) {
// Strings are equal
}
mg_strcasecmp()
int mg_strcasecmp(const struct mg_str str1, const struct mg_str str2);
Compare two mongoose strings, ignoring the case of the characters.
Parameters:
str1
,str2
- Pointers to Mongoose strings to compare
Return value: Zero if strings are equal, more than zero if first argument is greater than the second, and less than zero otherwise
Usage example:
struct mg_str str1 = mg_str("hello");
struct mg_str str2 = mg_str("HELLO");
if (mg_strcasecmp(str1, str2) == 0) {
// Strings are equal
}
mg_strdup()
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:
struct mg_str str1 = mg_str("hello");
struct mg_str str2 = mg_strdup(str1);
//...
free((void *)str2.buf);
mg_match()
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 thecaps
array size must be at least the number of wildcard symbols inpattern
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 matchpattern
- a pattern to match againstcaps
- an optional array of captures for wildcard symbols?
,*
, '#'
Return value: true
if matches, false
otherwise
Usage example:
// 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_span()
bool mg_span(struct mg_str s, struct mg_str *a, struct mg_str *b, char delim);
Span string s
at the first occurence of character delim
. Sstore the
first part in a
, and the rest of string in b
. Both a
and b
can be NULL.
If delim
is not present in s
, then a
spans to the end of s
.
Parameters:
s
- String being scanneda
- Pointer tomg_str
to store the prefix. Can beNULL
b
- Pointer tomg_str
to store the rest. Can beNULL
delim
- A delimiter character
Return value: true
if s
is non-empty, false
if it is empty
Usage example - scan comma-separated key=value pairs:
struct mg_str entry, key, val;
struct mg_str s = mg_str("a=333,b=777,hello");
while (mg_span(&s, &entry, &s, ',')) {
mg_span(entry, &key, &val, '=');
printf("key: %.*s, val: %.*s\n", (int) key.len, k.buf, (int) val.len, val.buf);
}
// This loop outputs the following:
// key: a, val: 333
// key: b, val: 777
// key: hello, val:
mg_str\to\num()
bool mg_str_to_num(struct mg_str s, int base, void *val, size_t val_len);
Parse the string s
for unsigned numbers in base base
. The result is stored at the address pointed to by val
. No white space allowed.
Parameters:
s
- String to parsebase
- Number base:2
for binary,10
for decimal,16
for hex; or0
for auto, in which case binary numbers must start with0b
and hexadecimal numbers with0x
. When the base is specified, do not prepend these.val
- Where to store the numberval_len
- destination size; e.g.:sizeof(uint8_t)
tosizeof(uint64_t)
Return value: Returns true
if a number has been parsed successfully
Usage example:
uint32_t val;
mg_str_to_num(mg_str_n("010203", 6), 16, &val, sizeof(val)); // returns true and val is now 0x10203
mg_path_is_sane()
bool mg_path_is_sane(struct mg_str path);
Check path
for starting with double dots in it.
Parameters:
path
- Mongoose string to check
Return value: true if OK, false otherwise
Usage example:
char data[] = "../../a.txt";
bool res = mg_path_is_sane(mg_str(data)); // returns false
mg_snprintf(), mg_vsnprintf()
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 supports two non-standard specifiers: %M
and %m
, for invoking custom print functions
Parameters:
buf
- Pointer to pointer to output bufferlen
- Buffer sizefmt
- format string inprintf
semantics.
Return value: Number of bytes printed
Supported format specifiers:
%c
- expectchar
%f
,%g
- expectdouble
%hhd
,%hd
,%d
,%ld
,%lld
- forchar
,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%p
- expects any pointer, prints0x.....
hex value%s
- expectschar *
%%
- prints the%
character itself%X.Y
- optional width and precision modifiers (e.g.:%1.2d
)%.*
- optional precision modifier, expected asint
argument (e.g.:%.*d
)%M
- (EXTENSION) expects a pointer to a custom print function and its arguments%m
- (EXTENSION) exactly like%M
but double-quotes the output
List of built-in print functions for %m
or %M
:
- mg_print_base64 - prints a buffer as a base64-encoded string
- mg_print_esc - prints a JSON-escaped string
- mg_print_hex - prints a buffer as a hex string
- mg_print_ip - prints an IP address in a
struct mg_str
- mg_print_ip_port - prints IP address and port in a
struct mg_str
- mg_print_ip4 - prints an IPv4 address
- mg_print_ip6 - prints an IPv6 address
- mg_print_mac - prints a MAC address
Usage example:
mg_snprintf(buf, sizeof(buf), "%lld", (int64_t) 123); // 123 (64-bit integer)
mg_snprintf(buf, sizeof(buf), "%.2s", "abcdef"); // ab (part of a string)
mg_snprintf(buf, sizeof(buf), "%.*s", 2, "abcdef"); // ab (part of a string)
mg_snprintf(buf, sizeof(buf), "%05x", 123); // 00123 (padded integer)
mg_snprintf(buf, sizeof(buf), "%%-%3s", "a"); // %- a (padded string)
mg_snprintf(buf, sizeof(buf), "hi, %m", mg_print_base64, 1, "a"); // hi, "YWJj" (base64-encode)
mg_snprintf(buf, sizeof(buf), "[%m]", mg_print_esc, 0, "two\nlines"); // ["two\nlines"] (JSON-escaped string)
mg_snprintf(buf, sizeof(buf), "{%m:%g}", mg_print_esc, 0, "val", 1.2); // {"val": 1.2} (JSON object)
mg_snprintf(buf, sizeof(buf), "hi, %M", mg_print_hex, 3, "abc"); // hi, 616263 (hex-encode)
mg_snprintf(buf, sizeof(buf), "IP: %M", mg_print_ip, &c->rem); // IP: 1.2.3.4 (struct mg_addr)
mg_snprintf(buf, sizeof(buf), "Peer: %M", mg_print_ip_port, &c->rem); // Peer: 1.2.3.4:21345 (struct mg_addr with port)
mg_snprintf(buf, sizeof(buf), "%M", mg_print_ip4, "abcd"); // 97.98.99.100 (IPv4 address)
mg_snprintf(buf, sizeof(buf), "%M", mg_print_ip6, "abcdefghijklmnop"); // [4142:4344:4546:4748:494a:4b4c:4d4e:4f50]
mg_snprintf(buf, sizeof(buf), "%M", mg_print_mac, "abcdef"); // 61:62:63:64:65:66 (MAC address)
It is easy to implement a custom print function. For example, to format a structure as JSON string:
struct a { int a, b; };
size_t print_a(void (*out)(char, void *), void *ptr, va_list *ap) {
struct a *ptr = va_arg(*ap, struct a *);
return mg_xprintf(out, ptr, "{%m:%d}", MG_ESC("a"), ptr->a); // MG_ESC invokes mg_print_esc
}
struct a temp = { 42, 43 };
mg_snprintf(buf, sizeof(buf), "%M", print_a, &temp); // {"a":42}
mg_mprintf(), mg_vmprintf()
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
- format string inprintf
semantics. See mg_snprintf for the list of supported format specifiers
Return value: allocated memory buffer
Usage example:
char *msg = mg_mprintf("Build the message: %s", "hi!");
free(msg);
mg_xprintf(), mg_vxprintf()
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 charsparam
- argument to be passed toout
fmt
- format string inprintf
semantics. See mg_snprintf for the list of supported format specifiers
Return value: Number of bytes printed
Usage example:
void myfn(char c, void *p);
size_t len = mg_xprintf(myfn, myfn_p, "Print the string: %s!", "hi");
mg_pfn_iobuf()
void mg_pfn_iobuf(char ch, void *param);
Print a character to a Generic IO buffer
Parameters:
ch
- char to be printedparam
- must bestruct mg_iobuf *
Usage example:
mg_xprintf(mg_pfn_iobuf, &c->send, "hi!"); // Append to the output buffer
mg_aton()
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 example1.2.3.4
,[::1]
,01:02::03
addr
- Pointer tomg_addr
string to receive parsed value
Return value: true
on success, false
otherwise
Usage example:
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
Mongoose library is often used to exchange data in JSON format, therefore we have provided utility functions to format JSON strings easily:
mg_http_reply(c, 200, "Content-Type: application/json\r\n",
"{%m: %u}", MG_ESC("value"), 123); // {"value": 123}
Therefore, for full JSON support, a set of parsing functions is required - which is described below.
mg_json_get()
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 JSONpath
- 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:
// Create a json string: { "a": 1, "b": [2, 3] }
char *buf = mg_mprintf("{ %m: %d, %m: [%d, %d] }",
MG_ESC("a"), 1,
MG_ESC("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_tok()
struct mg_str mg_json_get_tok(struct mg_str json, const char *path);
Parse JSON string json
and return a struct mg_str
pointing to the value of the element
specified by the JSON path
. Useful to check if a token is present, or inspect when it can have different types.
Parameters:
json
- a string containing valid JSONpath
- a JSON path. Must start with$
, e.g.$.user
Return value: a struct mg_str
pointing to the value of the element, or with a NULL pointer on error.
Usage example:
json = mg_str("{\"a\":\"b:c\"}");
val = mg_json_get_tok(json, "$.a"); // "b:c"
mg_json_get_num()
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 JSONpath
- a JSON path. Must start with$
v
- a placeholder for value
Return value: true on success, false on error
Usage example:
double d = 0.0;
mg_json_get_num(mg_str("[1,2,3]", "$[1]", &d)); // d == 2
mg_json_get_num(mg_str("{\"a\":1.23}", "$.a", &d)); // d == 1.23
mg_json_get_bool()
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 JSONpath
- a JSON path. Must start with$
v
- a placeholder for value
Return value: true on success, false on error
Usage example:
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()
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 JSONpath
- 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:
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()
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 JSONpath
- a JSON path. Must start with$
Return value: non-NULL on success, NULL on error
Usage example:
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()
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 JSONpath
- 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:
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()
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 JSONpath
- 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:
struct mg_str json = mg_str("{\"a\": \"YWJj\"}"); // json = {"a": "YWJj"}
char *str = mg_json_get_b64(json, "$.a", NULL); // str = "abc"
free(str);
mg_json_unescape()
bool mg_json_unescape(struct mg_str str, char *buf, size_t len);
Unescape a JSON string
Parameters:
str
- a string containing valid JSON to be unescapedbuf
- buffer where to place the resultlen
- buffer length
Return value: true on success, false on error
Usage example:
struct mg_str str = mg_str("{\"a\": \"b\\u0063d\"}"); // escaped json = {"a": "b\u0063d"}
char json[20];
bool result = mg_json_unescape(str, result, 20); // json = {"a": "bcd"}
mg_json_next()
size_t mg_json_next(struct mg_str obj, size_t ofs, struct mg_str *key, struct mg_str *val);
Iterate over elements of an object or array. An initial value for ofs
must be
0, then on each iteration a previously returned value should be passed.
Parameters:
json
- a string containing valid JSONofs
- an offset of the elementkey
- a pointer that receives key. For arrays, set to empty. Can be NULLval
- a pointer that receives value. Can be NULL
Return value: non-0 on success, 0 when there are no more elements
Usage example:
struct mg_str key, val, obj = mg_str("{\"a\": [true], \"b\": 12345}");
size_t ofs = 0;
while ((ofs = mg_json_next(obj, ofs, &key, &val)) > 0) {
printf("%.*s -> %.*s\n", (int) key.len, key.buf, (int) val.len, val.buf);
}
For an example on how to iterate over an arbitrary JSON string, see json_scan() function in the unit test.
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.
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() or mg_pfn_iobuf()
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()
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 pointermethod_pattern
- the name of the methodhandler
- the RPC function performing the action for this methodhandler_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:
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()
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 pointerhandler
- the RPC function performing the action for this method, use NULL to deallocate all
Usage example:
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()
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:
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()
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 requestfmt
- format string inprintf
semantics. See mg_snprintf for the list of supported format specifiers
Usage example:
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()
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 requestfmt
- format string inprintf
semantics. See mg_snprintf for the list of supported format specifiers
Usage example:
static void rpc_dosome(struct mg_rpc_req *r) {
...
mg_rpc_err(r, -32109, "\"%.*s not found\"", len, &r->frame.buf[offset]);
}
mg_rpc_list()
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:
mg_rpc_add(&s_rpc_head, mg_str("rpc.list"), mg_rpc_list, &s_rpc_head);
(see also mg_rpc_add())
Utility
mg_call()
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 eventev
- Event to sendev_data
- Additional event parameter
Return value: None
Usage example:
// 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()
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 eventfmt
- Format string inprintf
semantics
Return value: None
Usage example:
mg_error(c, "Operation failed, error code: %d", errno);
mg_md5_init()
void mg_md5_init(mg_md5_ctx *c);
Initialize context for MD5 hashing.
Parameters:
c
- Pointer tomg_md5_ctx
structure to initialize
Return value: None
Usage example:
mg_md5_ctx ctx;
mg_md5_init(&ctx);
mg_md5_update()
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 contextdata
- Data to hashlen
- Data length
Return value: None
Usage example:
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()
void mg_md5_final(mg_md5_ctx *c, unsigned char buf[16]);
Get current MD5 hash for context.
Parameters:
c
- MD5 contextbuf
- Pointer to buffer to write MD5 hash value
Return value: None
Usage example:
mg_md5_ctx ctx;
// Context initialization
// ...
unsigned char buf[16];
mg_md5_final(&ctx, buf); // `buf` is now MD5 hash
mg_sha1_init()
void mg_sha1_init(mg_sha1_ctx *c);
Initialize context for calculating SHA1 hash
Parameters:
c
- pointer tomg_sha1_ctx
structure to initialize
Return value: none
Usage example:
mg_sha1_ctx ctx;
mg_sha1_init(&ctx);
mg_sha1_update()
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 contextdata
- Data to hashlen
- Data length
Return value: None
Usage example:
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()
void mg_sha1_final(unsigned char digest[20], mg_sha1_ctx *c);
Get current SHA1 hash for context.
Parameters:
c
- SHA1 contextdigest
- Pointer to buffer to receive hash value
Return value: None
Usage example:
mg_sha1_ctx ctx;
// Context initialization
// ...
unsigned char buf[20];
mg_sha1_final(buf, &ctx); // `buf` is now SHA1 hash
mg_base64_update()
size_t mg_base64_update(unsigned char p, char *buf, size_t len);
Encode p
byte to base64 and write result into buf
buffer
Parameters:
p
- Byte to encodebuf
- Pointer to buffer to write resultlen
- Buffer length
Return value: Number of chars written into buffer
Usage example:
char buf[10];
mg_base64_update((unsigned char)"a", buf, 10); // Encode "a" into base64 and write it to buf
mg_base64_final()
size_t mg_base64_final(char *buf, size_t len);
Add base64 finish mark and \0
symbol to buf
Parameters:
buf
- Pointer to buffer to write finish marklen
- Buffer length
Return value: Number of chars written into buffer
char buf[10];
// ...
mg_base64_final(buf, 10);
mg_base64_encode()
size_t mg_base64_encode(const unsigned char *p, size_t n, char *buf, size_t len);
Encode n
bytes data pointed to by p
using base64 and write result into buf
.
Parameters:
p
- Pointer to data to encoden
- Data lengthbuf
- Pointer to buffer to write resultlen
- Buffer length
Return value: Number of chars written into buffer
Usage example:
char buf[128];
mg_base64_encode((uint8_t *) "abcde", 5, buf, 128); // buf is now YWJjZGU=
mg_base64_decode()
size_t mg_base64_decode(const char *src, size_t n, char *dst, size_t len);
Decode n
bytes of base64-ed src
and write it to dst
.
Parameters:
src
- Data to decoden
- Data lengthdst
- Pointer to output bufferlen
- Buffer length
Return value: Number of chars written into buffer
Usage example:
char buf[128];
mg_base64_decode("Q2VzYW50YQ==", 12, buf, 128); // buf is now "Cesanta"
mg_random()
bool 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 datalen
- Buffer size
Return value: This function returns false
when the system rand()
has to be used, because a stronger PRNG for the configured MG_ARCH could not be found.
Usage example:
char buf[10];
mg_random(buf, sizeof(buf)); // `buf` is now random bytes
mg_random_str()
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 bufferlen
- a buffer size
Return value: buf
value.
Usage example:
char buf[10];
printf("Random: %s\n", mg_random_str(buf, sizeof(buf)));
mg_ntohs()
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:
uint16_t val = mg_ntohs(0x1234);
mg_ntohl()
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:
uint32_t val = mg_ntohl(0x12345678);
mg_htons()
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:
uint16_t val = mg_htons(0x1234);
mg_htonl()
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:
uint32_t val = mg_htonl(0x12345678);
mg_crc32()
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 valuebuf
- Data to calculate CRC32len
- Data size
Return value: Calculated CRC32 checksum
Usage example:
char data[] = "hello";
uint32_t crc = mg_crc32(0, data, sizeof(data));
mg_check_ip_acl()
int mg_check_ip_acl(struct mg_str acl, struct mg_addr *remote_ip);
Check IP address remote_ip
against the IP ACL acl
.
Currently, only the IPv4 address format is supported for the ACL string.
Parameters:
acl
- an ACL string, e.g.-0.0.0.0/0,+1.2.3.4
remote_ip
- an IP address
Return value: 1 if remote_ip
is allowed, 0 if not, and <0 if acl
is invalid
Usage example:
if (mg_check_ip_acl(mg_str("-0.0.0.0/0,+1.2.3.4"), c->rem) != 1) {
LOG(LL_INFO, ("NOT ALLOWED!"));
}
mg_url_decode()
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 encoden
- String to encode lengthto
- Pointer to output bufferto_len
- Output buffer sizeform
- If non-zero, then+
is decoded as whitespace.
Return value: Decoded bytes count or negative value on error
Usage example:
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
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 encoden
- String to encode lengthbuf
- Output bufferlen
- Output buffer size
Return value: Number of characters written to buf
Usage example:
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_base64
size_t mg_print_base64(void (*out)(char, void *), void *param, va_list *ap);
Print a buffer as a base64-encoded string. Expects data length and a pointer to the data as next arguments in the va_list ap
Parameters:
out
- function to be used for printing charsparam
- argument to be passed toout
Return value: Number of bytes printed
Usage example:
mg_snprintf(buf, sizeof(buf), "hi, %m", mg_print_base64, 1, "a"); // hi, "YWJj"
mg_print_esc
size_t mg_print_esc(void (*out)(char, void *), void *param, va_list *ap);
Print a JSON-escaped string. Expects string length and a pointer to the string in the va_list ap
. For null-terminated strings use 0
for string length, or the macro MG_ESC()
Parameters:
out
- function to be used for printing charsparam
- argument to be passed toout
Return value: Number of bytes printed
Usage example:
mg_snprintf(buf, sizeof(buf), "{%m: %u}", MG_ESC("value"), 123); // {"value": 123}
mg_snprintf(buf, sizeof(buf), "{%m: %u}", mg_print_esc, 0, "value", 123); // {"value": 123}
mg_print_hex
size_t mg_print_hex(void (*out)(char, void *), void *param, va_list *ap);
Print a buffer as a hex-encoded string. Expects data length and a pointer to the data as next arguments in the va_list ap
Parameters:
out
- function to be used for printing charsparam
- argument to be passed toout
Return value: Number of bytes printed
Usage example:
mg_snprintf(buf, sizeof(buf), "hi, %M", mg_print_hex, 1, 255); // hi, ff
mg_print_ip
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_addr
as the next argument in the va_list ap
Parameters:
out
- function to be used for printing charsparam
- argument to be passed toout
Return value: Number of bytes printed
Usage example:
struct mg_addr 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
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_addr
as the next argument in the va_list ap
Parameters:
out
- function to be used for printing charsparam
- argument to be passed toout
Return value: Number of bytes printed
Usage example:
struct mg_addr 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
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 charsparam
- argument to be passed toout
Return value: Number of bytes printed
Usage example:
mg_snprintf(buf, sizeof(buf), "%M", mg_print_ip4, "abcd"); // 97.98.99.100
mg_print_ip6
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 charsparam
- argument to be passed toout
Return value: Number of bytes printed
Usage example:
mg_snprintf(buf, sizeof(buf), "%M", mg_print_ip6, "abcdefghijklmnop"); // [4142:4344:4546:4748:494a:4b4c:4d4e:4f50]
mg_print_mac
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 charsparam
- argument to be passed toout
Return value: Number of bytes printed
Usage example:
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
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()
int mg_iobuf_init(struct mg_iobuf *io, size_t size, size_t align);
Initialize IO buffer, allocate size
bytes.
Parameters:
io
- Pointer tomg_iobuf
structure to initializesize
- Amount of bytes to allocatealign
- Alignsize
to thealign
mem boundary.0
means no alignment
Return value: 1 on success, 0 on allocation failure
Usage example:
struct mg_iobuf io;
if (mg_iobuf_init(&io, 0, 64)) {
// io successfully initialized
}
mg_iobuf_resize()
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 resizesize
- New size
Return value: 1 on success, 0 on allocation failure
Usage example:
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()
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:
struct mg_iobuf io;
// IO buffer initialization
// ...
// Time to cleanup
mg_iobuf_free(&io);
mg_iobuf_add()
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 dataoffset
- Offset to add databuf
- Data to addlen
- Data length
Return value: new io
length
Usage example:
struct mg_iobuf io; // Declare buffer
mg_iobuf_init(&io, 0, 16); // Initialise empty buffer with 16 byte alignment
mg_iobuf_add(&io, io.len, "hello", 5); // Append "hello"
mg_iobuf_del()
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 dataoffset
- Start offsetlen
- Amount of bytes to delete
Return value: New io
length
Usage example:
struct mg_iobuf io;
mg_iobuf_init(&io, 0, 16); // Empty buffer, 16-bytes aligned
mg_iobuf_add(&io, 0, "hello", 2); // io->len is 5, io->size is 16
mg_iobuf_del(&io, 1, 3); // io->len is 2, io->size is still 16
Queue
Single-producer single-consumer non-blocking queue
struct mg_queue
struct mg_queue {
char *buf;
size_t size;
volatile size_t tail;
volatile size_t head;
};
mg_queue_init
void mg_queue_init(struct mg_queue *q, char *buf, size_t size);
Initialize a queue
Parameters:
q
- pointer to anmg_queue
structuresize
- queue size in bytes
Usage example:
struct mg_queue q;
char buf[100];
mg_queue_init(&q, buf, sizeof(buf));
mg_queue_book
size_t mg_queue_book(struct mg_queue *q, char **ptr, size_t len);
Reserve space in a queue
Parameters:
q
- pointer to anmg_queue
structureptr
- pointer to where to store the address of the reserved space in the queuelen
- number of bytes requested
Return value: number of bytes actually reserved
Usage example:
struct mg_queue q;
char buf[100];
mg_queue_init(&q, buf, sizeof(buf));
char *ptr;
if (mg_queue_book(&q, &ptr, len) < len) {
// Not enough space
} else {
// Go ahead, memory area pointed to by ptr
}
mg_queue_add
void mg_queue_add(struct mg_queue *q, size_t len);
Add a new message to a queue
Parameters:
q
- pointer to anmg_queue
structurelen
- Data length
Usage example:
struct mg_queue q;
char buf[100];
mg_queue_init(&q, buf, sizeof(buf));
char *ptr;
if (mg_queue_book(&q, &ptr, len) < len) {
// Not enough space
} else {
memcpy(ptr, my_data, len); // Copy data to the queue
mg_queue_add(&q, len); // Add a new message to the queue
}
mg_queue_next
size_t mg_queue_next(struct mg_queue *q, char **ptr);
Get the oldest message in a queue
Parameters:
q
- pointer to anmg_queue
structureptr
- pointer to where to store the address of the message in the queue
Return value: number of bytes in message, 0 if no outstanding messages
Usage example:
struct mg_queue q;
char buf[100];
mg_queue_init(&q, buf, sizeof(buf));
...
char *ptr;
size_t len;
if ((len = mg_queue_next(&q, &ptr)) > 0) {
// message data pointed to by ptr
} else {
// no messages
}
mg_queue_del
void mg_queue_del(struct mg_queue *q, size_t len);
Delete len
bytes, oldest message in queue
Parameters:
q
- pointer to anmg_queue
structurelen
- number of bytes to delete
Usage example:
struct mg_queue q;
char buf[100];
mg_queue_init(&q, buf, sizeof(buf));
...
char *ptr;
if ((len = mg_queue_next(&q, &ptr)) > 0) {
memcpy(somewhere, ptr, len);
mg_queue_del(&q, len);
} else {
// no messages
}
mg_queue_printf(), mg_queue_vprintf()
size_t mg_queue_vprintf(struct mg_queue *q, const char *fmt, va_list *);
size_t mg_queue_printf(struct mg_queue *q, const char *fmt, ...);
Print message into a queue. Internally calls mg_queue_book() and mg_queue_add(), with the conveniency of printf. The format string is evaluated first to calculate needed room and then again to actually print if there is available room in the queue; pay attention to side effects when calling.
Parameters:
q
- pointer to anmg_queue
structurefmt
- format string inprintf
semantics. See mg_snprintf for the list of supported format specifiers
Return value: number of bytes printed
Usage example:
struct mg_queue q;
char buf[100];
mg_queue_init(&q, buf, sizeof(buf));
mg_queue_printf(&q, "hi");
URL
mg_url_port()
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:
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()
int mg_url_is_ssl(const char *url);
Check if given URL uses an encrypted scheme
Parameters:
url
- URL to check
Return value: non-zero if given URL uses an encrypted scheme, zero otherwise
Usage example:
if (mg_url_is_ssl("https://example.org")) {
// scheme is encrypted
}
mg_url_host()
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:
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()
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:
struct mg_str user_name = mg_url_user("https://user@password@my.example.org"); // user_name is now "user"
mg_url_pass()
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:
struct mg_str pwd = mg_url_user("https://user@password@my.example.org"); // pwd is now "password"
mg_url_uri()
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:
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()
#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:
MG_INFO(("Hello %s!", "world")); // Output "Hello, world"
mg_log_set()
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 logging1
- Log errors only2
- Log errors and info messages3
- Log errors, info and debug messages4
- 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:
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()
void mg_hexdump(const void *buf, int len);
Log a hex dump of binary data buf
, len
.
Parameters:
buf
- Data pointerlen
- Data length
Return value: none
Usage example:
mg_hexdump(c->recv.buf, c->recv.len); // Hex dump incoming data
mg_log_set_fn()
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 characterparam
- a parameter for a logging function
Usage example: redirecting logs to syslog.
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
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 *); // List directory entries: call fn(file_name, fn_data) for each directory entry
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:
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
struct mg_fd {
void *fd;
struct mg_fs *fs;
};
Opened file abstraction.
mg_fs_open()
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 implementationpath
- a file pathflags
- desired flags, a combination ofMG_FS_READ
andMG_FS_WRITE
Return value: a non-NULL opened descriptor, or NULL on failure.
Usage example:
struct mg_fd *fd = mg_fs_open(&mg_fs_posix, "/tmp/data.json", MG_FS_WRITE);
mg_fs_close()
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()
struct mg_str mg_file_read(struct mg_fs *fs, const char *path);
Read the whole file in memory.
Parameters:
fs
- a filesystem implementationpath
- a file path
Return value: on success, the struct mg_str
points to file data, which is guaranteed
to be nul-terminated, and its len
field contains file length. On error, it contains a NULL pointer.
Usage example:
struct mg_str data = mg_file_read(&mg_fs_packed, "/data.json"); // size = data.len
free(data.buf);
mg_file_write()
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 implementationpath
- a file pathbuf
- a pointer to data to be writtenlen
- data size
Return value: true on success, false on error
Usage example:
mg_file_write(&mg_fs_fat, "/test.txt", "hi\n", 3);
mg_file_printf()
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 implementationpath
- a file pathfmt
- format string inprintf
semantics. See mg_snprintf for the list of supported format specifiers
Return value: true on success, false on error
Usage example:
mg_file_printf(&mg_fs_fat, "/test.txt", "%s\n", "hi");
mg_fs_ls()
bool mg_fs_ls(struct mg_fs *fs, const char *path, char *buf, size_t len);
Helper function to scan a filesystem in a sequential way, without using a callback function. Each call will return one entry until the list is exhausted
Parameters:
fs
- a filesystem implementationpath
- a file pathbuf
- a pointer to where to store the resultslen
- buffer size
Return value: true if there are more entries, need to call again; false when no more entries left.
Usage example:
char buf[100] = "";
while (mg_fs_ls(&mg_fs_posix, "./", buf, sizeof(buf)))
puts(buf);