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.

If you have an STM32 or NXP development board at hand, you can try Mongoose right now, directly in your browser.

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 connections
  • struct mg_connection - A single connection descriptor

Connections could be listening, outbound, or inbound. Outbound connections are created by the mg_connect() call. Listening connections are created by the mg_listen() call. Inbound connections are those accepted by a listening connection. Each connection is described by a struct mg_connection structure, which has a number of fields. All fields are exposed to the application by design, to give an application full visibility into Mongoose's internals.

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 peer
  • struct mg_connection::recv - Data received from a peer

When data arrives, Mongoose appends received data to the recv and triggers a MG_EV_READ event. The user may send data back by calling one of the output functions, like mg_send(), mg_printf() or a protocol-specific function like mg_ws_send. Output functions append data to the send buffer. When Mongoose successfully writes data to the socket, it discards data from struct mg_connection::send and sends an MG_EV_WRITE event.

Event handler function

Each connection has an event handler function associated with it, which must be implemented by the user. Event handler is the key element of Mongoose, since it defines the connection's behavior. See below for an example of an event handler function:

// 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 event
  • int ev - An event number, defined in mongoose.h. For example, when data arrives on an inbound connection, ev would be MG_EV_READ
  • void *ev_data - Points to the event-specific data, and it has a different meaning for different events. For example, for an MG_EV_READ event, ev_data is a long * pointing to the number of bytes received from a remote peer and saved into the c->recv IO buffer. The exact meaning of ev_data is described for each event. Protocol-specific events usually have ev_data pointing to structures that hold protocol-specific information
  • c->fn_data, void * - A user-defined pointer for the connection, which is a placeholder for application-specific data. This fn_data pointer is set during the *_listen() or *_connect() call. Listening connections copy the value of c->fn_data to the newly accepted connection, so all accepted connections initially share the same fn_data pointer. It is fine to update/replace that pointer for any connection at any time by setting c->fn_data = new_value;

Events

Below is the list of events triggered by Mongoose, taken as-is from mongoose.h. For each event, a comment describes the meaning of the ev_data pointer passed to an event handler:

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 protocol
  • mg_http_connect() for HTTP
  • mg_ws_connect() for Websocket
  • mg_mqtt_connect() for MQTT
  • mg_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():
    mg_log_set(MG_LL_DEBUG);
    mg_mgr_init(&mgr);
    
    The MG_INFO(), MG_DEBUG() logging macros use putchar() by default, i.e. they use standard C stdout stream. That works fine on the traditional OS. In the embedded environment, in order to see debug output, two ways are possible: IO retargeting or Mongoose log redirection. IO retargeting can already be implemented by an embedded SDK - for example ESP32 SDK redirects printf() to the UART0. Otherwise, IO retargeting can be implemented manually, see guide 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 the mg_http_reply() function to create HTTP responses. That function properly sets the Content-Length header, which is important. Of course you can create responses manually, e.g. with mg_printf() function, but be sure to set the Content-Length header:
    mg_printf(c, "HTTP/1.1 200 OK\r\Content-Length: %d\r\n\r\n%s", 2, "hi");
    
    Alternatively, use chunked transfer enconding:
    mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
    mg_http_printf_chunk(c, "%s", "foo");
    mg_http_printf_chunk(c, "%s", "bar");
    mg_http_printf_chunk(c, "");  // Don't forget the last empty chunk
    

    NOTE: if you are not using mg_http_reply() or mg_http_*_chunk(), make sure to set c->is_resp = 0; when your event handler finished writing 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.

Architecture diagram

Mongoose Architecture

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:

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 the MG_IO_SIZE limit accordingly.

Custom build

A custom build should be used for cases not covered by the existing architecture options (e.g., an embedded architecture that uses some proprietary RTOS and network stack).

In order to build on such systems, 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_http_match_uri(hm, "/api/sum")) {
      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:

Web UI for OTA firmware update

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

See Web UI dashboard guide

IoT fleet management

See IoT fleet management guide

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.
  • Embedded filesystem - This tutorial shows an example of how to embed all the web files in a packed filesystem that is linked into the server binary; forming a read-only file system.
  • 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.

Misc

  • Error handling - If a connection fails for some reason, you can find answers what to do in this section.
  • Multithreading - This tutorial will show you how to work with Mongoose on a multithreaded environment.
  • 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.
  • Timers - This tutorial will guide you to configure a timer callback, a mechanism to perform some periodic actions.
  • TCP client and server - This simple tutorial demonstrates how Mongoose Library can be used to implement TCP clients and servers, even over TLS.
  • UART bridge - This tutorial shows an example of how to send UART data over the network.
  • SMTP client - This simple tutorial demonstrates how Mongoose Library can be used to implement an SMTP client over TLS.
  • Drivers for Mongoose built-in TCP/IP stack - Write your own driver, port our generic CMSIS-Driver to your ARM device

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.

STM32

Nucleo-F429ZI

ram: 256k flash: 2m freq: 180MHz net: Ethernet
Nucleo-F429ZI board
Framework OS IP stack Tutorial
Cube baremetal built-in tutorial
Cube FreeRTOS built-in tutorial
Cube FreeRTOS lwIP tutorial
Keil baremetal built-in tutorial
Keil FreeRTOS built-in tutorial
Zephyr Zephyr Zephyr tutorial
make baremetal built-in tutorial
make FreeRTOS built-in tutorial
make baremetal built-in USB tutorial

Nucleo-F746ZG

ram: 320k flash: 1m freq: 216MHz net: Ethernet
Nucleo-F746ZG board
Framework OS IP stack Tutorial
Cube baremetal built-in tutorial
Cube FreeRTOS built-in tutorial
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 tutorial
make baremetal built-in tutorial
make FreeRTOS built-in tutorial
make FreeRTOS FreeRTOS+TCP example
make baremetal built-in USB tutorial

Nucleo-H563ZI

ram: 640k flash: 2m freq: 250MHz net: Ethernet
Nucleo-H563ZI board
Framework OS IP stack Tutorial
make baremetal built-in tutorial
make FreeRTOS built-in tutorial

STM32H573I-DK

ram: 640k flash: 2m freq: 250MHz net: Ethernet
STM32H573I-DK board
Framework OS IP stack Tutorial
make baremetal built-in tutorial
make FreeRTOS built-in tutorial

Nucleo-H723ZG

ram: 564k flash: 1m freq: 550MHz net: Ethernet
Nucleo-H723ZG board
Framework OS IP stack Tutorial
make baremetal built-in device dashboard
make baremetal built-in MQTT dashboard device
make FreeRTOS built-in tutorial

Nucleo-H743ZI2

ram: 1m flash: 2m freq: 480MHz net: Ethernet
Nucleo-H743ZI2 board
Framework OS IP stack Tutorial
Cube baremetal built-in tutorial
Cube FreeRTOS built-in tutorial
make baremetal built-in tutorial
make FreeRTOS built-in tutorial
Cube FreeRTOS LwIP tutorial

STM32H747I-Disco

ram: 1m flash: 2m freq: 480MHz net: Ethernet
H747I-DISCO board
Framework OS IP stack Tutorial
make baremetal built-in tutorial

Nucleo-G031K8 + W5500 module

ram: 8k flash: 64k freq: 64MHz net: Ethernet
Nucleo-G031K8 board + W5500 mini
Framework OS IP stack Example
make baremetal built-in example

NXP

MIMXRT1020-EVK

ram: 256k flash: 8m freq: 500MHz net: Ethernet
NXP MIMXRT1020-EVK board
Framework OS IP stack Tutorial
make baremetal built-in example
make FreeRTOS built-in example
MCUXpresso FreeRTOS LwIP tutorial
MCUXpresso AzureRTOS Azure tutorial

MIMXRT1060-EVKB

ram: 512k flash: 8m freq: 600MHz net: Ethernet
NXP MIMXRT1060-EVKB board
Framework OS IP stack Tutorial
make baremetal built-in example
make FreeRTOS built-in example

Teensy 4.1 + expansion board

ram: 512k flash: 8m freq: 600MHz net: Ethernet
Teensy 4.1 board
Framework OS IP stack Tutorial
Arduino built-in example

FRDM-K64F

ram: 256k flash: 1m freq: 120MHz net: Ethernet
NXP FRDM-K64F board
Framework OS IP stack Example
MCUXpresso FreeRTOS LwIP example

FRDM-K66F

ram: 256k flash: 2m freq: 180MHz net: Ethernet
NXP FRDM-K66F board
Framework OS IP stack Tutorial
MCUXpresso FreeRTOS LwIP tutorial

NXP LPC54S018M-EVK

ram: 128m flash: 128m freq: 180MHz net: Ethernet
NXP LPC54S018M-EVK board
Framework OS IP stack Tutorial
MCUXpresso FreeRTOS LwIP tutorial

ESP

ESP32 DevkitC

ram: 220k flash: 2/4m@40MHz freq: 160/240MHz net: WiFi
ESP32 DevkitC board
Framework OS IP stack Tutorial
ESP-IDF FreeRTOS LwIP device dashboard
ESP-IDF FreeRTOS LwIP UART bridge

M5 STAMP PICO

ram: 220k flash: 4m@80MHz freq: 240MHz net: WiFi
M5 STAMP PICO board
Framework OS IP stack Tutorial
ESP-IDF FreeRTOS LwIP tutorial

XIAO-ESP32-C3

ram: 400k flash: 4m@80MHz freq: 160MHz net: WiFi
XIAO-ESP32-C3 board
Framework OS IP stack Tutorial
ESP-IDF FreeRTOS LwIP tutorial

ESP8266 DevkitC

ram: 50k flash: 2m freq: 80MHz net: WiFi
ESP8266 DevkitC board
Framework OS IP stack Tutorial
RTOS SDK SDK (FreeRTOS) SDK (LwIP) tutorial

Texas Instruments

EK-TM4C1294xxx

ram: 256k flash: 1m freq: 120MHz net: Ethernet
TI EK-TM4C1294XL board
Framework OS IP stack Tutorial
make baremetal built-in tutorial
make FreeRTOS built-in tutorial
CCS TI-RTOS TI-RTOS tutorial
make baremetal built-in USB example

Infineon

XMC-4700

ram: 352k flash: 2m+4m freq: 144MHz net: Ethernet
Infineon XMC-4700 board
Framework OS IP stack Example
Keil RTX LwIP example

Raspberry Pi RP2040

Pico

ram: 264k flash: 2m freq: 133MHz net: USB (RNDIS/CDC-ECM)
Raspberry Pi Pico board
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 W

ram: 264k flash: 2m freq: 133MHz net: WiFi
Raspberry Pi Pico W board
Framework OS IP stack Tutorial
RPI PICO C SDK FreeRTOS LwIP tutorial

Pico + LAN8720 module

ram: 264k flash: 2m freq: 133MHz net: Ethernet
Raspberry Pi Pico and LAN8720 module board
Framework OS IP stack Tutorial
RPI PICO C SDK baremetal built-in tutorial

Pico + W5500 module

ram: 264k flash: 2m freq: 133MHz net: Ethernet
Raspberry Pi Pico and W5500 module board
Framework OS IP stack Tutorial
RPI PICO C SDK baremetal built-in tutorial

W5500-EVB-Pico

ram: 264k flash: 2m freq: 133MHz net: Ethernet
W5500-EVB-Pico (RP2040 + W5500) board
Framework OS IP stack Tutorial
RPI PICO C SDK baremetal built-in tutorial

XIAO RP2040

ram: 264k flash: 2m freq: 133MHz net: USB (RNDIS/CDC-ECM)
Seeed Xiao RP2040 board
Framework OS IP stack Tutorial
RPI PICO C SDK baremetal built-in USB RNDIS tutorial

Microchip SAMxx

SAM E54 Xplained Pro

ram: 256k flash: 1m freq: 120MHz net: Ethernet
SAM E54 Xplained Pro
Framework OS IP stack Example
make baremetal built-in device dashboard
make baremetal built-in MQTT client

XIAO M0 + W5500 module

ram: 32k flash: 256k freq: 48MHz net: Ethernet
XIAO M0 + W5500 module board
Framework OS IP stack Tutorial
Arduino built-in tutorial

WCH

CH32V307

ram: 64k flash: 256k freq: 144MHz net: Ethernet
WCH CH32V307 board
Framework OS IP stack Example
make baremetal built-in example

Embedded Linux

Raspberry Pi

ram: 1G flash: SD freq: 1.2+ GHz net: WiFi
Raspberry Pi board
Framework Code Tutorial
Raspberry Pi OS tutorial

Raspberry Pi 4 model B

ram: 1G~8G flash: SD freq: 1.5 GHz net: Ethernet net: WiFi
net: USB (RNDIS/CDC-ECM)
Raspberry Pi board
Framework Code Tutorial
Raspberry Pi OS coming soon

Video Tutorials

Mongoose WebServer for Microcontrollers - setting up tools on Windows and building a basic example.

Introduction - setting up tools on Windows and building a basic example

A detailed HTTP server example walk-through.

A detailed HTTP server example walk-through

Implementing Web UI device dashboard on STM32F7 board using Keil RTX.

Implementing Web UI device dashboard on STM32F7 board using Keil RTX

Implementing Web UI device dashboard on STM32F7 board using Cube IDE.

Implementing Web UI device dashboard on STM32F7 board using Cube IDE

How does TCP/CP stack work on embedded device.

How does TCP/CP stack work on embedded device - an animated tutorial

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 to mg_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, send MG_EV_READ event
  • See if there is data in the c->send buffer, and write it, send MG_EV_WRITE event
  • If a connection is listening, accept an incoming connection if any, and send MG_EV_ACCEPT event to it
  • Send MG_EV_POLL event

Parameters:

  • mgr - an event manager to use
  • ms - a timeout in milliseconds

Return value: none

Usage example:

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 use
  • url - a URL. Specifies local IP address and port to listen on, e.g. tcp://127.0.0.1:1234 or udp://0.0.0.0:9000
  • fn - an event handler function
  • fn_data - an arbitrary pointer, which will be stored in the connection structure as c->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 use
  • url - A URL, specifies remote IP address/port to connect to, e.g. http://a.com
  • fn - An event handler function
  • fn_data - an arbitrary pointer, which will be stored in the connection structure as c->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 pointer
  • data - A pointer to data to append to the send buffer
  • size - A data size

Return value: true if data appended successfully and false otherwise

Usage example:

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 manager
  • id - A destination connection ID
  • data - A pointer to data to append to the send buffer
  • size - 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->ptr;
}

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->ptr;
  // 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 pointer
  • fmt - a format string in printf 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 wrap
  • mgr - An event manager
  • fn - A pointer to event handler function
  • fn_data - an arbitrary pointer, which will be stored in the connection structure as c->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 example http://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.

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 manager
  • url - A URL, specifies local IP address and port to listen on, e.g. http://0.0.0.0:8000
  • fn - An event handler function
  • fn_data - an arbitrary pointer, which will be stored in the connection structure as c->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 manager
  • url - A URL, specifies remote URL, e.g. http://google.com
  • fn - An event handler function
  • fn_data - an arbitrary pointer, which will be stored in the connection structure as c->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 request
  • buf_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
Function mg_http_get_request_len()

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 string
  • len - A request string length
  • hm - A pointer to a structure to store parsed request

Return value: request length (see mg_http_get_request_len())

Usage example:

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 pointer
  • fmt - A string, format specified in printf 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 pointer
  • buf - Data to write
  • len - 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.

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 the root_dir. If you need to reference an upper-level directory, use an absolute path.

Parameters:

  • c - Connection to use
  • hm - HTTP message, that should be served
  • opts - Serve options. Note that opts.root_dir can optionally accept extra comma-separated uri=path pairs, see example below

Return value: None

Usage example:

// 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 use
  • hm - HTTP message to serve
  • path - Path to file to serve
  • opts - 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 use
  • status_code - An HTTP response code
  • headers - Extra headers, default NULL. If not NULL, must end with \r\n
  • fmt - A format string for the HTTP body, in a printf semantics

Return value: None

Function mg_http_reply()

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 header
  • name - 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->ptr);
    } 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 header
  • name - 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 string
  • name - 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 body
  • name - Variable name
  • buf - Buffer to write decoded variable
  • len - Buffer size

Return value: Length of decoded variable. A zero or negative value means error

Usage example:

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 credentials
  • user - buffer to receive user name
  • userlen - size of user buffer
  • pass - buffer to receive password
  • passlen - size of pass buffer

Return value: None

Usage example:

// 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_match_uri()

bool mg_http_match_uri(const struct mg_http_message *hm, const char *glob);

Check if HTTP request matches a given glob pattern.

Parameters:

  • hm - HTTP message to match
  • glob - Pattern

Return value: True if HTTP request matches a given glob pattern; false otherwise

Usage example:

// 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;
    if (mg_http_match_uri(hm, "/secret")) {
      mg_http_reply(c, 200, NULL, "Very big secret!");
    } else {
      mg_http_reply(c, 200, NULL, "hello world..");
    }
  }
}

mg_http_bauth()

void mg_http_bauth(struct mg_connection *c, const char *user, const char *pass);

Write a Basic Authorization header to the output buffer.

Parameters:

  • c - Connection to use
  • user - User name
  • pass - Password

Return value: None

Usage example which uses Basic auth to create Stripe subscription:

  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.

HTTP part

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 body
  • offset - Start offset
  • part - Pointer to struct mg_http_part to fill

Return value: offset to the next chunk, or 0 if there are no more chunks.

Usage example (or see form upload tutorial ):

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.ptr,
           part.filename.len, part.filename.ptr, part.body.len));
  // Use this chunk ....
}

A diagram below shows how mg_http_next_multipart() in action:

Function mg_http_next_multipart()

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 connection
  • hm - a parsed HTTP message
  • fs - a filesystem where to write the files, e.g. &mg_fs_posix
  • dir - a directory path where to write the files
  • max_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_http_match_uri(hm, "/upload")) {
      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 use
  • url - Specifies remote URL, e.g. http://google.com
  • fn - An event handler function
  • fn_data - an arbitrary pointer, which will be stored in the connection structure as c->fn_data, so the event handler can use it when called.
  • fmt - format string in printf 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 use
  • hm - HTTP message
  • fmt - format string in printf 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 use
  • buf - Data to send
  • len - Data size
  • op - 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 use
  • op - WebSocket message type, see WebSocket message type above
  • fmt - format string in printf 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 use
  • len - Bytes count to convert
  • op - Websocket message type (see mg_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 use
  • url - Specifies remote URL, time.google.com if NULL.
  • fn - An event handler function
  • fn_data - an arbitrary pointer, which will be stored in the connection structure as c->fn_data, so the event handler can use it when called.

Return value: Pointer to created connection or NULL on error

Usage example:

static void sntp_cb(struct mg_connection *c, int ev, void *evd, void *fnd) {
  if (ev == MG_EV_SNTP_TIME) {
    // Time received
    struct timeval *tv = (struct timeval *tv)evd;
  }
}
...
mg_sntp_connect(mgr&, NULL /* connect to time.google.com */, sntp_cb, NULL);

mg_sntp_request()

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
  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 use
  • url - 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 NULL
  • fn - The event handler function
  • fn_data - an arbitrary pointer, which will be stored in the connection structure as c->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 use
  • url - Specifies the local IP address and port to listen on, e.g. mqtt://0.0.0.0:1883
  • fn - The event handler function
  • fn_data - an arbitrary pointer, which will be stored in the connection structure as c->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 use
  • opts - 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()

void 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 use
  • opts - pointer to publish MQTT options, like QoS, and retain flag. The message body is expected at opts->message, the topic at opts->topic; both as mg_str

Return value: None

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 use
  • opts - pointer to subscription MQTT options, like QoS. The topic is expected at opts->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.ptr),
             (int) mm->data.len, mm->data.ptr);
  }
}

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 use
  • cmd - 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 parse
  • len - buffer length
  • m - 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 use
  • opts - 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 disabled
  • cert - Our own certificate; an mg_str. If NULL, then we don't authenticate ourselves to the other peer
  • key - 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 for cert and key could be the same
  • name - Server name; an mg_str. If not empty, enable server name verification

NOTE: if both ca and cert 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 both key and cert, whilst clients only ca and/or possibly name.

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. The mg_random function can be overridden by setting MG_ENABLE_CUSTOM_RANDOM=1 and defining your own mg_random() implementation.

Parameters:

  • c - Connection, for which TLS should be initialized
  • opts - TLS initialization parameters

Return value: None

Usage example:

// 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 to mg_mgr event manager structure
  • ms - An interval in milliseconds
  • flags - Timer flags bitmask, MG_TIMER_REPEAT and MG_TIMER_RUN_NOW
  • fn - Function to invoke
  • fn_data - Function argument 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 to mg_timer list head
  • t - Pointer to mg_timer that should be initialized
  • ms - An interval in milliseconds
  • flags - Timer flags bitmask, MG_TIMER_REPEAT and MG_TIMER_RUN_NOW
  • fn - Function to invoke
  • fn_data - Function argument 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 to mg_timer list head
  • t - 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 to mg_timer list head
  • uptime_ms - current timestamp

Return value: None

Usage example:

mg_timer_poll(mg_millis());

Time

mg_millis()

int64_t mg_millis(void);

Return current uptime in milliseconds.

Parameters: None

Return value: Current uptime

Usage example:

int64_t uptime = mg_millis();

String

struct mg_str

struct mg_str {
  const char *ptr;  // Pointer to string data
  size_t len;       // String len
};

This structure represent an arbitrary chunk of memory, not necessarily zero-terminated. This is a "mongoose string", and it gets used extensively in the codebase instead of C zero-terminated strings.

For example, when an HTTP request is received, Mongoose created a struct mg_http_message which has a collection of struct mg_str pointing to request method, URI, headers, and so on. This way, Mongoose avoids any heap allocations and does not modify the received buffer - instead, it uses struct mg_str to describe various parts of HTTP request.

Same goes with many other cases.

NOTE: since ptr is not necessarily zero-terminated, do not use libc string functions against it - like strlen() or sscanf().

mg_str()

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 created mg_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_ncasecmp()

int mg_ncasecmp(const char *s1, const char *s2, size_t len);

Case insensitive compare two C-strings, not more than len symbols or until meet \0 symbol.

Parameters:

  • s1, s2 - Pointers to strings to compare
  • len - Maximum length to compare

Return value: Zero if strings are equal, more than zero if first argument is greater then second, and less than zero otherwise

Usage example:

if (mg_ncasecmp("hello1", "HELLO2", 5) == 0) {
  // Strings are equal
}

mg_vcmp()

int mg_vcmp(const struct mg_str *s1, const char *s2);

Compare mongoose string and C-string.

Parameters:

  • s1 - Pointer to Mongoose string to compare
  • s2 - Pointer to C-string to compare

Return value: 0 if strings are equal, more than zero if first argument is greater then second, and less than zero otherwise

Usage example:

struct mg_str str = mg_str("hello");
if (mg_vcmp(str, "hello") == 0) {
  // Strings are equal
}

mg_vcasecmp()

int mg_vcasecmp(const struct mg_str *str1, const char *str2);

Case insensitive compare mongoose string and C-string.

Parameters:

  • str1 - Mongoose string to compare
  • str2 - C-string to compare

Return value: Zero if strings are equal, more than zero if first argument is greater then second, and less than zero otherwise

Usage example:

struct mg_str str = mg_str("hello");
if (mg_vcasecmp(str, "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 then 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_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.ptr);

mg_strstr()

const char *mg_strstr(const struct mg_str haystack, const struct mg_str needle)

Search for needle substring in haystack string.

Parameters:

  • haystack - Mongoose sting to search for substring
  • needle - Mongoose string to search

Return value: pointer to needle occurrence within haystack or NULL if not found.

Usage example:

struct mg_str str = mg_str("Hello, world");
struct mg_str sub_str = mg_str("world");

if (mg_strstr(str, sub_str) != NULL) {
  // Found
}

mg_strstrip()

struct mg_str mg_strstrip(struct mg_str s)

Remove heading and trailing whitespace from mongoose string s.

Parameters:

  • s - Mongoose string for trimming

Return value: Input string

Usage example:

struct mg_str str = mg_strstrip(mg_str("   Hello, world   "));
if (mg_vcmp(str, "Hello, world") == 0) {
  // Strings are equal
}

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 the caps array size must be at least the number of wildcard symbols in pattern plus 1. The last cap will be initialized to an empty string.

The glob pattern matching rules are as follows:

  • ? matches any single character
  • * matches zero or more characters except /
  • # matches zero or more characters
  • any other character matches itself

Parameters:

  • str - a string to match
  • pattern - a pattern to match against
  • caps - an optional array of captures for wildcard symbols ?, *, '#'

Return value: true if matches, false otherwise

Usage example:

// 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 scanned
  • a - Pointer to mg_str to store the prefix. Can be NULL
  • b - Pointer to mg_str to store the rest. Can be NULL
  • 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.ptr, (int) val.len, val.ptr); 
}

// This loop outputs the following:
// key: a, val: 333
// key: b, val: 777
// key: hello, val:
Function mg_commalist()

mg_hex()

char *mg_hex(const void *buf, size_t len, char *dst);

Hex-encode binary data buf, len into a buffer dst and nul-terminate it. The output buffer must be at least 2 x len + 1 big.

Parameters:

  • buf - Data to hex-encode
  • len - Data length
  • dst - Pointer to output buffer

Return value: dst pointer. The encoded characters are lowercase

Usage example:

char data[] = "\x1\x2\x3";
char buf[sizeof(data)*2];
char *hex = mg_hex(data, sizeof(data) - 1, buf);
LOG(LL_INFO, ("%s", hex)); // Output "010203";
free(hex);

mg_unhex()

void mg_unhex(const char *buf, size_t len, unsigned char *to);

Hex-decode string buf, len into a buffer to. The to buffer should be at least lsn / 2 big.

Parameters:

  • buf - Data to hex-decode
  • len - Data length
  • to - Pointer to output buffer

Return value: None

Usage example:

char data[] = "010203";
char *buf[sizeof(data)/2];
char *hex = mg_unhex(data, sizeof(data) - 1, buf); // buf is now [1,2,3]
free(hex);

mg_unhexn()

unsigned long mg_unhexn(const char *s, size_t len);

Parse len characters of the hex-encoded string s. The maximum value of len is the width of the long x 2, for example on 32-bit platforms it is 8.

Parameters:

  • s - String to parse
  • len - String length

Return value: Return parsed value

Usage example:

char data[] = "010203";
char *buf[sizeof(data)/2];
unsigned long val = mg_unhex(data, sizeof(data) - 1); // val is now 123

mg_path_is_sane()

bool mg_path_is_sane(char *path);

Check path for starting with double dots in it.

Parameters:

  • path - String to check

Return value: true if OK, false otherwise

Usage example:

char data[] = "../../a.txt";
bool res = mg_path_is_sane(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 buffer
  • len - Buffer size
  • fmt - format string in printf semantics.

Return value: Number of bytes printed

Supported format specifiers:

  • %c - expect char
  • %f, %g - expect double
  • %hhd, %hd, %d, %ld, %lld - for char, short, int, long, int64_t
  • %hhu, %hu, %u, %lu, %llu - same but for unsigned variants
  • %hhx, %hx, %x, %lx, %llx - same, unsigned and hex output
  • %p - expects any pointer, prints 0x..... hex value
  • %s - expects char *
  • %% - prints the % character itself
  • %X.Y - optional width and precision modifiers (e.g.: %1.2d)
  • %.* - optional precision modifier, expected as int 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:

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 in printf 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 chars
  • param - argument to be passed to out
  • fmt - format string in printf 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 printed
  • param - must be struct 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 example 1.2.3.4, [::1], 01:02::03
  • addr - Pointer to mg_addr string to receive parsed value

Return value: true on success, false otherwise

Usage example:

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 JSON
  • path - a JSON path. Must start with $, e.g. $.user
  • toklen - a pointer that receives element's length, can be NULL

Return value: offset of the element, or negative MG_JSON_* on error.

Usage example:

// 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 JSON
  • path - 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 JSON
  • path - 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 JSON
  • path - 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 JSON
  • path - a JSON path. Must start with $
  • default_val - a default value for the failure case

Return value: found value, or default_val value

Usage example:

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 JSON
  • path - 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 JSON
  • path - a JSON path. Must start with $
  • len - a pointer that receives decoded length. Can be NULL

Return value: non-NULL on success, NULL on error

Usage example:

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 JSON
  • path - a JSON path. Must start with $
  • len - a pointer that receives decoded length. Can be NULL

Return value: non-NULL on success, NULL on error

Usage example:

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 unescaped
  • buf - buffer where to place the result
  • len - 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 JSON
  • ofs - an offset of the element
  • key - a pointer that receives key. For arrays, set to empty. Can be NULL
  • val - 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.ptr, (int) val.len, val.ptr);
}

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 pointer
  • method_pattern - the name of the method
  • handler - the RPC function performing the action for this method
  • handler_data - Arbitrary function data

NOTE: if method_pattern is an empty string, this handler will be called to process JSON-RPC responses. Handling responses might be necessary if the JSON requests are initiated by both sides.

Usage example:

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 pointer
  • handler - 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 request
  • fmt - format string in printf 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 request
  • fmt - format string in printf 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.ptr[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 event
  • ev - Event to send
  • ev_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 event
  • fmt - Format string in printf 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 to mg_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 context
  • data - Data to hash
  • len - 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 context
  • buf - 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 to mg_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 context
  • data - Data to hash
  • len - 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 context
  • digest - 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 encode
  • buf - Pointer to buffer to write result
  • len - 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 mark
  • len - 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 encode
  • n - Data length
  • buf - Pointer to buffer to write result
  • len - 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 decode
  • n - Data length
  • dst - Pointer to output buffer
  • len - 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()

void mg_random(void *buf, size_t len);

Fill in buffer buf, len with random data. Note: Mongoose uses this function for TLS and some other routines that require RNG (random number generator). It is possible to override a built-in mg_random() by specifying a MG_ENABLE_CUSTOM_RANDOM=1 build preprocessor constant.

Parameters:

  • buf - Pointer to buffer to receive random data
  • len - Buffer size

Return value: None

Usage example:

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 buffer
  • len - 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 value
  • buf - Data to calculate CRC32
  • len - 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 encode
  • n - String to encode length
  • to - Pointer to output buffer
  • to_len - Output buffer size
  • form - If non-zero, then + is decoded as whitespace.

Return value: Decoded bytes count or negative value on error

Usage example:

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 encode
  • n - String to encode length
  • buf - Output buffer
  • len - 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 chars
  • param - argument to be passed to out

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 chars
  • param - argument to be passed to out

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 chars
  • param - argument to be passed to out

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 chars
  • param - argument to be passed to out

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 chars
  • param - argument to be passed to out

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 chars
  • param - argument to be passed to out

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 chars
  • param - argument to be passed to out

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 chars
  • param - argument to be passed to out

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.

struct mg_iobuf diagram

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 to mg_iobuf structure to initialize
  • size - Amount of bytes to allocate
  • align - Align size to the align mem boundary. 0 means no alignment

Return value: 1 on success, 0 on allocation failure

Usage example:

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 resize
  • size - 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 data
  • offset - Offset to add data
  • buf - Data to add
  • len - 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
Function mg_iobuf_init()
mg_iobuf_add(&io, io.len, "hello", 5);  // Append "hello"
Function mg_iobuf_add()

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 data
  • offset - Start offset
  • len - 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
Function mg_iobuf_del()

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 an mg_queue structure
  • size - 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 an mg_queue structure
  • ptr - pointer to where to store the address of the reserved space in the queue
  • len - 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 an mg_queue structure
  • len - 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 an mg_queue structure
  • ptr - 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 an mg_queue structure
  • len - 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 an mg_queue structure
  • fmt - format string in printf 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 encrypted scheme

Parameters:

  • url - URL to check

Return value: 0 is given URL uses encrypted scheme and non-zero otherwise

Usage example:

if (mg_url_is_ssl("https://example.org") == 0) {
  // 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 logging
    • 1 - Log errors only
    • 2 - Log errors and info messages
    • 3 - Log errors, info and debug messages
    • 4 - Log everything

Return value: None

It is possible to override log level per source file basis. For example, if there is a file called foo.c, and you'd like to set a global level to 2 (info) but increase log level for file foo.c to debug, then, a spec should look like "2,foo.c=3". There could be several comma-separated overrides.

Usage example:

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 pointer
  • len - 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 character
  • param - 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 implementation
  • path - a file path
  • flags - desired flags, a combination of MG_FS_READ and MG_FS_WRITE

Return value: a non-NULL opened descriptor, or NULL on failure.

Usage example:

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 implementation
  • path - 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.ptr);

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 implementation
  • path - a file path
  • buf - a pointer to data to be written
  • len - 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 implementation
  • path - a file path
  • fmt - format string in printf 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 implementation
  • path - a file path
  • buf - a pointer to where to store the results
  • len - 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);
arrow up