Multi-Protocol Networking Library
Usage Example
-
Copy
mongoose.c
andmongoose.h
to your build tree -
Write code that uses Mongoose API, e.g. in
my_app.c
-
Compile application:
$ cc my_app.c mongoose.c
#include "mongoose.h"
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
struct iobuf *io = &nc->recv_iobuf;
switch (ev) {
case MG_EV_RECV:
// This event handler implements simple TCP echo server
mg_send(nc, io->buf, io->len); // Echo received data back
iobuf_remove(io, io->len); // Discard data from recv buffer
break;
default:
break;
}
}
int main(void) {
struct mg_mgr mgr;
mg_mgr_init(&mgr, NULL);
// Note that many connections can be added to a single event manager
// Connections can be created at any point, e.g. in event handler function
mg_bind(&mgr, "1234", ev_handler, NULL);
for (;;) {
mg_mgr_poll(&mgr, 1000);
}
mg_mgr_free(&mgr);
return 0;
}
Include Mongoose API definitions | |
Define an event handler function | |
Initialize event manager object | |
Create listening connection and add it to the event manager | |
Start infinite event loop |
Design Concept
Mongoose is a multi-protocol networking library that implements non-blocking, asyncronous IO and provides event-based API. It has three basic data structures:
-
struct mg_mgr
is an event manager that holds all active connections -
struct mg_connection
describes a connection -
struct mbuf
describes data buffer (received or sent data)
Connections could be either listening, outbound or inbound.
Outbound connections are created by mg_connect()
call.
Listening connections are created by mg_bind()
call.
Inbound connections are those accepted by a listening connection.
Each connection is described by
struct mg_connection
structure, which has
a number of fields like socket, event handler function, send/receive buffer,
flags, et cetera.
Mongoose usage pattern is to declare and initialize event manager, create
connections and create an event loop by calling
mg_mgr_poll()
in a loop.
mg_mgr_poll()
iterates over all sockets,
accepts new connections, sends and receives data, closes connections,
and calls event handler functions for the respective events.
Memory buffers
Each connection has send and receive buffer,
struct mg_connection::send_mbuf
and
struct mg_connection::recv_mbuf
respectively.
When data arrives,
Mongoose appends received data to the recv_mbuf
and
triggers MG_EV_RECV
event. User may send data back by calling one of the
output functions, like mg_send()
or
mg_printf()
. Output functions append data to the
send_mbuf
. When Mongoose
successfully writes data to the socket, it discards data from
mg_connection::send_mbuf
and
sends MG_EV_SEND
event. When connection is closed, MG_EV_CLOSE
event is sent.
Event handler function
Each connection has an event handler function associated with it. That function must be implemented by user. Event handler is the key element of the Mongoose application, since it defines application’s behavior. This is how an event handler function looks like:
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
switch (ev) {
/* Event handler code that defines behavior of the connection */
...
}
}
- struct mg_connection *nc
-
Connection that has received an event.
- int ev
-
Event number, defined in
mongoose.h
. For example, when data arrives on inbound connectionev
would beMG_EV_RECV
. - void *ev_data
-
This pointer points to the event-specific data, and it has different meaning for different events. For example, for
MG_EV_RECV
event,ev_data
is anint *
pointer, pointing to the number of bytes received from the remote peer and saved into the receive IO buffer. Exact meaning ofev_data
is described for each event. Protocol-specific events usually haveev_data
pointing to structures that hold protocol-specific information.
struct mg_connection has void *user_data
which is a placeholder for a application-specific data. Mongoose does not use
that pointer. Event handler can store any kind of information there.
|
Events
Mongoose accepts incoming connections, reads and writes data, and calls specified event handler for each connection when appropriate. Typical event sequence is this:
-
For outbound connection:
MG_EV_CONNECT
→ (MG_EV_RECV
,MG_EV_SEND
,MG_EV_POLL
…) →MG_EV_CLOSE
-
For inbound connection:
MG_EV_ACCEPT
→ (MG_EV_RECV
,MG_EV_SEND
,MG_EV_POLL
…) →MG_EV_CLOSE
Below is a list of core events triggered by Mongoose (note that each protocol triggers protocol-specific events in addition to the core ones):
- MG_EV_ACCEPT
-
sent when new server connection is accepted by a listening connection.
void *ev_data
isunion socket_address
of the remote peer. - MG_EV_CONNECT
-
sent when a new outbound connection created by
mg_connect()
either failed or succeeded.void *ev_data
isint *success
. Ifsuccess
is 0, then connection has been established, otherwise it contains error code. Seemg_connect_opt()
function for code example. - MG_EV_RECV
-
New data is received and appended to the end of
recv_mbuf
.void *ev_data
isint *num_received_bytes
. Typically, event handler should check received data innc→recv_mbuf
, discard processed data by callingmbuf_remove()
, set connection flagsnc→flags
if necessary (seestruct mg_connection
), and write data the remote peer by output functions likemg_send()
.
Mongoose uses realloc() to expand receive buffer.
It is user’s responsibility to discard processed
data from the beginning of receive buffer, note the mbuf_remove()
call in the example above.
|
- MG_EV_SEND
-
Mongoose has written data to the remote peer and discarded written data from the
mg_connection::send_mbuf
.void *ev_data
isint *num_sent_bytes
.
Mongoose output functions only append
data to the mg_connection::send_mbuf , they do not do any socket writes.
An actual IO is done by mg_mgr_poll() . MG_EV_SEND event
is just a notification about an IO has been done.
|
- MG_EV_POLL
-
Sent to all connections on each invocation of
mg_mgr_poll()
. This event could be used to do any housekeeping, for example check whether certain timeout has expired and close the connection, or send heartbeet message, et cetera.
Connection flags
Each connection has a flags
bit field. Some flags are set by Mongoose, for
example if a user creates an outbound UDP connection using udp://1.2.3.4:5678
address, Mongoose is going to set MG_F_UDP
flag for that connection. Other flags
are meant to be set only by user event handler to tell Mongoose how to behave.
Below is a list of connection flags that are meant to be set by event handlers:
-
MG_F_FINISHED_SENDING_DATA
tells Mongoose that all data has been appended to thesend_mbuf
. As soon as Mongoose sends it to the socket, the connection will be closed. -
MG_F_BUFFER_BUT_DONT_SEND
tells Mongoose to append data to thesend_mbuf
but hold on sending it, because the data will be modified later and then will be sent by clearingMG_F_BUFFER_BUT_DONT_SEND
flag. -
MG_F_CLOSE_IMMEDIATELY
tells Mongoose to close the connection immediately, usually after some error -
MG_F_USER_1
,MG_F_USER_2
,MG_F_USER_3
,MG_F_USER_4
could be used by a developer to store application-specific state
Flags below are set by Mongoose:
-
MG_F_SSL_HANDSHAKE_DONE
SSL only, set when SSL handshake is done. -
MG_F_CONNECTING
set when connection is in connecting state aftermg_connect()
call but connect did not finish yet. -
MG_F_LISTENING
set for all listening connections -
MG_F_UDP
set if connection is UDP -
MG_F_IS_WEBSOCKET
set if connection is a Websocket connection -
MG_F_WEBSOCKET_NO_DEFRAG
should be set by a user if user wants to switch off automatic Websocket frame defragmentation
Build Options
Mongoose source code ships in a single .c file that contains functionality for all supported protocols (modules). Modules can be disabled at compile time which reduces executable size. That can be done by setting preprocessor flags. Also, some preprocessor flags can be used to tune internal Mongoose parameters.
To set a preprocessor flag during compile time, use -D <PREPROCESSOR_FLAG>
compiler option. For example, to disable both MQTT and JSON-RPC,
compile the application my_app.c
like this (assumed UNIX system):
$ cc my_app.c mongoose.c -D MG_DISABLE_MQTT -D MG_DISABLE_JSON_RPC
Enabling flags
-
MG_ENABLE_SSL
Enable OpenSSL support -
MG_ENABLE_IPV6
Enable IPV6 support -
MG_ENABLE_THREADS
enablemg_start_thread()
API -
MG_ENABLE_MQTT_BROKER
enable MQTT broker -
MG_ENABLE_DNS_SERVER
enable DNS server -
MG_ENABLE_COAP
enable CoAP protocol -
MG_ENABLE_GETADDRINFO
enablegetaddrinfo()
inmg_resolve2()
Disabling flags
-
MG_DISABLE_HTTP_WEBSOCKET
disable HTTP + Websocket protocol support -
MG_DISABLE_HTTP_DIGEST_AUTH
disable HTTP Digest (MD5) authorization support -
MG_DISABLE_MQTT
disable MQTT support -
MG_DISABLE_SHA1
disable SHA1 support (used by Websocket) -
MG_DISABLE_MD5
disable MD5 support (used by HTTP auth) -
MG_DISABLE_JSON_RPC
disable JSON-RPC support -
MG_DISABLE_SOCKETPAIR
disablemg_broadcast()
API
Tunables
-
MG_MALLOC
,MG_CALLOC
,MG_REALLOC
,MG_FREE
allow to use custom memory allocator, e.g.-DMG_MALLOC=my_malloc
-
MG_USE_READ_WRITE
when set replaces calls torecv
withread
andsend
withwrite
, thus enabling to add any kind of file descriptor (files, serial devices) to an event manager. -
MG_SSL_CRYPTO_MODERN
,MG_SSL_CRYPTO_OLD
- choose either "Modern" or "Old" ciphers instead of the default "Intermediate" setting. See [this article](https://wiki.mozilla.org/Security/Server_Side_TLS#Recommended_configurations) for details.
API Reference
Memory Buffers
Mbufs are mutable/growing memory buffers, like C++ strings. Mbuf can append data to the end of a buffer, or insert data into arbitrary position in the middle of a buffer. The buffer grows automatically when needed.
struct mbuf
struct mbuf {
char *buf; /* Buffer pointer */
size_t len; /* Data length. Data is located between offset 0 and len. */
size_t size; /* Buffer size allocated by realloc(1). Must be >= len */
};
Memory buffer descriptor
mbuf_init()
void mbuf_init(struct mbuf *, size_t initial_capacity);
Initialize an Mbuf.
initial_capacity
specifies the initial capacity of the mbuf.
mbuf_free()
void mbuf_free(struct mbuf *);
Free the space allocated for the mbuffer and resets the mbuf structure.
mbuf_append()
size_t mbuf_append(struct mbuf *, const void *data, size_t data_size);
Appends data to the Mbuf.
Return the number of bytes appended, or 0 if out of memory.
mbuf_insert()
size_t mbuf_insert(struct mbuf *, size_t, const void *, size_t);
Insert data at a specified offset in the Mbuf.
Existing data will be shifted forwards and the buffer will be grown if necessary. Return the number of bytes inserted.
mbuf_remove()
void mbuf_remove(struct mbuf *, size_t data_size);
Remove data_size
bytes from the beginning of the buffer.
mbuf_resize()
void mbuf_resize(struct mbuf *, size_t new_size);
Resize an Mbuf.
If new_size
is smaller than buffer’s len
, the
resize is not performed.
mbuf_trim()
void mbuf_trim(struct mbuf *);
Shrink an Mbuf by resizing its size
to len
.
Core: TCP/UDP/SSL
Mongoose manager is single threaded. It does not protect
its data structures by mutexes, therefore all functions that are dealing
with particular event manager should be called from the same thread,
with exception of mg_broadcast() function. It is fine to have different
event managers handled by different threads.
|
struct mg_str
struct mg_str {
const char *p; /* Memory chunk pointer */
size_t len; /* Memory chunk length */
};
Describes chunk of memory
struct mg_mgr
struct mg_mgr {
struct mg_connection *active_connections;
const char *hexdump_file; /* Debug hexdump file path */
sock_t ctl[2]; /* Socketpair for mg_wakeup() */
void *user_data; /* User data */
void *mgr_data; /* Implementation-specific event manager's data. */
#ifdef MG_ENABLE_JAVASCRIPT
struct v7 *v7;
#endif
};
Mongoose event manager.
struct mg_connection
struct mg_connection {
struct mg_connection *next, *prev; /* mg_mgr::active_connections linkage */
struct mg_connection *listener; /* Set only for accept()-ed connections */
struct mg_mgr *mgr; /* Pointer to containing manager */
sock_t sock; /* Socket to the remote peer */
union socket_address sa; /* Remote peer address */
size_t recv_mbuf_limit; /* Max size of recv buffer */
struct mbuf recv_mbuf; /* Received data */
struct mbuf send_mbuf; /* Data scheduled for sending */
SSL *ssl;
SSL_CTX *ssl_ctx;
time_t last_io_time; /* Timestamp of the last socket IO */
mg_event_handler_t proto_handler; /* Protocol-specific event handler */
void *proto_data; /* Protocol-specific data */
mg_event_handler_t handler; /* Event handler function */
void *user_data; /* User-specific data */
void *priv_1; /* Used by mg_enable_multithreading() */
void *priv_2; /* Used by mg_enable_multithreading() */
void *mgr_data; /* Implementation-specific event manager's data. */
unsigned long flags;
/* Flags set by Mongoose */
#define MG_F_LISTENING (1 << 0) /* This connection is listening */
#define MG_F_UDP (1 << 1) /* This connection is UDP */
#define MG_F_RESOLVING (1 << 2) /* Waiting for async resolver */
#define MG_F_CONNECTING (1 << 3) /* connect() call in progress */
#define MG_F_SSL_HANDSHAKE_DONE (1 << 4) /* SSL specific */
#define MG_F_WANT_READ (1 << 5) /* SSL specific */
#define MG_F_WANT_WRITE (1 << 6) /* SSL specific */
#define MG_F_IS_WEBSOCKET (1 << 7) /* Websocket specific */
/* Flags that are settable by user */
#define MG_F_SEND_AND_CLOSE (1 << 10) /* Push remaining data and close */
#define MG_F_DONT_SEND (1 << 11) /* Do not send data to peer */
#define MG_F_CLOSE_IMMEDIATELY (1 << 12) /* Disconnect */
#define MG_F_WEBSOCKET_NO_DEFRAG (1 << 13) /* Websocket specific */
#define MG_F_DELETE_CHUNK (1 << 14) /* HTTP specific */
#define MG_F_USER_1 (1 << 20) /* Flags left for application */
#define MG_F_USER_2 (1 << 21)
#define MG_F_USER_3 (1 << 22)
#define MG_F_USER_4 (1 << 23)
#define MG_F_USER_5 (1 << 24)
#define MG_F_USER_6 (1 << 25)
};
Mongoose connection.
struct mg_add_sock_opts
struct mg_add_sock_opts {
void *user_data; /* Initial value for connection's user_data */
unsigned int flags; /* Initial connection flags */
const char **error_string; /* Placeholder for the error string */
};
Optional parameters to mg_add_sock_opt()
flags
is an initial struct mg_connection::flags
bitmask to set,
see MG_F_*
flags definitions.
struct mg_bind_opts
struct mg_bind_opts {
void *user_data; /* Initial value for connection's user_data */
unsigned int flags; /* Extra connection flags */
const char **error_string; /* Placeholder for the error string */
};
Optional parameters to mg_bind_opt()
flags
is an initial struct mg_connection::flags
bitmask to set,
see MG_F_*
flags definitions.
struct mg_connect_opts
struct mg_connect_opts {
void *user_data; /* Initial value for connection's user_data */
unsigned int flags; /* Extra connection flags */
const char **error_string; /* Placeholder for the error string */
};
Optional parameters to mg_connect_opt()
mg_mgr_init()
void mg_mgr_init(struct mg_mgr *mgr, void *user_data);
Initialize Mongoose manager. Side effect: ignores SIGPIPE signal.
mgr→user_data
field will be initialized with user_data
parameter.
That is an arbitrary pointer, where user code can associate some data
with the particular Mongoose manager. For example, a C++ wrapper class
could be written, in which case user_data
can hold a pointer to the
class instance.
mg_mgr_free()
void mg_mgr_free(struct mg_mgr *);
De-initializes Mongoose manager.
Close and deallocate all active connections.
mg_mgr_poll()
time_t mg_mgr_poll(struct mg_mgr *, int milli);
This function performs the actual IO, and must be called in a loop
(an event loop). Returns the current timestamp.
milli
is the maximum number of milliseconds to sleep.
mg_mgr_poll()
checks all connection for IO readiness. If at least one
of the connections is IO-ready, mg_mgr_poll()
triggers respective
event handlers and returns.
mg_broadcast()
void mg_broadcast(struct mg_mgr *, mg_event_handler_t func, void *, size_t);
Pass a message of a given length to all connections.
Must be called from a thread that does NOT call mg_mgr_poll()
.
Note that mg_broadcast()
is the only function
that can be, and must be, called from a different (non-IO) thread.
func
callback function will be called by the IO thread for each
connection. When called, event would be MG_EV_POLL
, and message will
be passed as ev_data
pointer. Maximum message size is capped
by MG_CTL_MSG_MESSAGE_SIZE
which is set to 8192 bytes.
mg_next()
struct mg_connection *mg_next(struct mg_mgr *, struct mg_connection *);
Iterate over all active connections.
Returns next connection from the list
of active connections, or NULL
if there is no more connections. Below
is the iteration idiom:
for (c = mg_next(srv, NULL); c != NULL; c = mg_next(srv, c)) {
// Do something with connection `c`
}
mg_add_sock()
struct mg_connection *mg_add_sock(struct mg_mgr *, sock_t, mg_event_handler_t);
Create a connection, associate it with the given socket and event handler, and add it to the manager.
For more options see the mg_add_sock_opt
variant.
mg_add_sock_opt()
struct mg_connection *mg_add_sock_opt(struct mg_mgr *, sock_t,
mg_event_handler_t,
struct mg_add_sock_opts);
Create a connection, associate it with the given socket and event handler, and add to the manager.
See the mg_add_sock_opts
structure for a description of the options.
mg_bind()
struct mg_connection *mg_bind(struct mg_mgr *, const char *,
mg_event_handler_t);
Create listening connection.
See mg_bind_opt
for full documentation.
mg_bind_opt()
struct mg_connection *mg_bind_opt(struct mg_mgr *, const char *,
mg_event_handler_t, struct mg_bind_opts);
Create listening connection.
address
parameter tells which address to bind to. It’s format is the same
as for the mg_connect()
call, where HOST
part is optional. address
can be just a port number, e.g. :8000
. To bind to a specific interface,
an IP address can be specified, e.g. 1.2.3.4:8000
. By default, a TCP
connection is created. To create UDP connection, prepend udp://
prefix,
e.g. udp://:8000
. To summarize, address
paramer has following format:
[PROTO://][IP_ADDRESS]:PORT
, where PROTO
could be tcp
or udp
.
See the mg_bind_opts
structure for a description of the optional
parameters.
Return a new listening connection, or NULL
on error.
NOTE: Connection remains owned by the manager, do not free().
mg_connect()
struct mg_connection *mg_connect(struct mg_mgr *, const char *,
mg_event_handler_t);
Connect to a remote host.
See mg_connect_opt()
for full documentation.
mg_connect_opt()
struct mg_connection *mg_connect_opt(struct mg_mgr *, const char *,
mg_event_handler_t,
struct mg_connect_opts);
Connect to a remote host.
address
format is [PROTO://]HOST:PORT
. PROTO
could be tcp
or udp
.
HOST
could be an IP address,
IPv6 address (if Mongoose is compiled with -DMG_ENABLE_IPV6
), or a host
name. If HOST
is a name, Mongoose will resolve it asynchronously. Examples
of valid addresses: google.com:80
, udp://1.2.3.4:53
, 10.0.0.1:443
,
[::1]:80
See the mg_connect_opts
structure for a description of the optional
parameters.
Returns a new outbound connection, or NULL
on error.
Connection remains owned by the manager, do not free(). |
To enable IPv6 addresses, -DMG_ENABLE_IPV6 should be specified
in the compilation flags.
|
New connection will receive MG_EV_CONNECT as it’s first event
which will report connect success status.
If asynchronous resolution fail, or connect() syscall fail for whatever
reason (e.g. with ECONNREFUSED or ENETUNREACH ), then MG_EV_CONNECT
event report failure. Code example below:
|
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
int connect_status;
switch (ev) {
case MG_EV_CONNECT:
connect_status = * (int *) ev_data;
if (connect_status == 0) {
// Success
} else {
// Error
printf("connect() error: %s\n", strerror(connect_status));
}
break;
...
}
}
...
mg_connect(mgr, "my_site.com:80", ev_handler);
mg_set_ssl()
const char *mg_set_ssl(struct mg_connection *nc, const char *cert,
const char *ca_cert);
Enable SSL for a given connection.
cert
is a server certificate file name for a listening connection,
or a client certificate file name for an outgoing connection.
Certificate files must be in PEM format. Server certificate file
must contain a certificate, concatenated with a private key, optionally
concatenated with parameters.
ca_cert
is a CA certificate, or NULL if peer verification is not
required.
Return: NULL on success, or error message on error.
mg_send()
int mg_send(struct mg_connection *, const void *buf, int len);
Send data to the connection.
Return number of written bytes. Note that sending functions do not actually push data to the socket. They just append data to the output buffer. The exception is UDP connections. For UDP, data is sent immediately, and returned value indicates an actual number of bytes sent to the socket.
mg_printf()
int mg_printf(struct mg_connection *, const char *fmt, ...);
Send printf
-style formatted data to the connection.
See mg_send
for more details on send semantics.
mg_vprintf()
int mg_vprintf(struct mg_connection *, const char *fmt, va_list ap);
Same as mg_printf()
, but takes va_list ap
as an argument.
mg_socketpair()
int mg_socketpair(sock_t[2], int sock_type);
Create a socket pair.
sock_type
can be either SOCK_STREAM
or SOCK_DGRAM
.
Return 0 on failure, 1 on success.
mg_resolve()
int mg_resolve(const char *domain_name, char *ip_addr_buf, size_t buf_len);
Convert domain name into IP address.
This is a utility function. If compilation flags have
-DMG_ENABLE_GETADDRINFO
, then getaddrinfo()
call is used for name
resolution. Otherwise, gethostbyname()
is used.
this function can block. Return 1 on success, 0 on failure. |
mg_check_ip_acl()
int mg_check_ip_acl(const char *acl, uint32_t remote_ip);
Verify given IP address against the ACL.
remote_ip
- an IPv4 address to check, in host byte order
acl
- a comma separated list of IP subnets: x.x.x.x/x
or x.x.x.x
.
Each subnet is
prepended by either a - or a + sign. A plus sign means allow, where a
minus sign means deny. If a subnet mask is omitted, such as -1.2.3.4
,
this means to deny only that single IP address.
Subnet masks may vary from 0 to 32, inclusive. The default setting
is to allow all accesses. On each request the full list is traversed,
and the last match wins. Example:
-0.0.0.0/0,+192.168/16
- deny all acccesses, only allow 192.168/16 subnet
To learn more about subnet masks, see the Wikipedia page on Subnetwork
Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed.
mg_enable_multithreading()
void mg_enable_multithreading(struct mg_connection *nc);
Enable multi-threaded handling for the given listening connection nc
.
For each accepted connection, Mongoose will create a separate thread
and run event handler in that thread. Thus, if an event hanler is doing
a blocking call or some long computation, that will not slow down
other connections.
mg_enable_javascript()
enum v7_err mg_enable_javascript(struct mg_mgr *m, struct v7 *v7,
const char *init_js_file_name);
Enable server-side JavaScript scripting.
Requires -DMG_ENABLE_JAVASCRIPT
compilation flag, and V7 engine sources.
v7 instance must not be destroyed during manager’s lifetime.
Return V7 error.
HTTP + Websocket
struct http_message
struct http_message {
struct mg_str message; /* Whole message: request line + headers + body */
struct mg_str proto; /* "HTTP/1.1" -- for both request and response */
/* HTTP Request line (or HTTP response line) */
struct mg_str method; /* "GET" */
struct mg_str uri; /* "/my_file.html" */
/* For responses, code and response status message are set */
int resp_code;
struct mg_str resp_status_msg;
/*
* Query-string part of the URI. For example, for HTTP request
* GET /foo/bar?param1=val1¶m2=val2
* | uri | query_string |
*
* Note that question mark character doesn't belong neither to the uri,
* nor to the query_string
*/
struct mg_str query_string;
/* Headers */
struct mg_str header_names[MG_MAX_HTTP_HEADERS];
struct mg_str header_values[MG_MAX_HTTP_HEADERS];
/* Message body */
struct mg_str body; /* Zero-length for requests with no body */
};
HTTP message
struct mg_serve_http_opts
struct mg_serve_http_opts {
/* Path to web root directory */
const char *document_root;
/* List of index files. Default is "" */
const char *index_files;
/*
* Leave as NULL to disable authentication.
* To enable directory protection with authentication, set this to ".htpasswd"
* Then, creating ".htpasswd" file in any directory automatically protects
* it with digest authentication.
* Use `mongoose` web server binary, or `htdigest` Apache utility to
* create/manipulate passwords file.
* Make sure `auth_domain` is set to a valid domain name.
*/
const char *per_directory_auth_file;
/* Authorization domain (domain name of this web server) */
const char *auth_domain;
/*
* Leave as NULL to disable authentication.
* Normally, only selected directories in the document root are protected.
* If absolutely every access to the web server needs to be authenticated,
* regardless of the URI, set this option to the path to the passwords file.
* Format of that file is the same as ".htpasswd" file. Make sure that file
* is located outside document root to prevent people fetching it.
*/
const char *global_auth_file;
/* Set to "no" to disable directory listing. Enabled by default. */
const char *enable_directory_listing;
/* SSI files pattern. If not set, "**.shtml$|**.shtm$" is used. */
const char *ssi_pattern;
/* IP ACL. By default, NULL, meaning all IPs are allowed to connect */
const char *ip_acl;
/* URL rewrites.
*
* Comma-separated list of `uri_pattern=file_or_directory_path` rewrites.
* When HTTP request is received, Mongoose constructs a file name from the
* requested URI by combining `document_root` and the URI. However, if the
* rewrite option is used and `uri_pattern` matches requested URI, then
* `document_root` is ignored. Instead, `file_or_directory_path` is used,
* which should be a full path name or a path relative to the web server's
* current working directory. Note that `uri_pattern`, as all Mongoose
* patterns, is a prefix pattern.
*
* If uri_pattern starts with `@` symbol, then Mongoose compares it with the
* HOST header of the request. If they are equal, Mongoose sets document root
* to `file_or_directory_path`, implementing virtual hosts support.
*/
const char *url_rewrites;
/* DAV document root. If NULL, DAV requests are going to fail. */
const char *dav_document_root;
/* Glob pattern for the files to hide. */
const char *hidden_file_pattern;
/* Set to non-NULL to enable CGI, e.g. **.cgi$|**.php$" */
const char *cgi_file_pattern;
/* If not NULL, ignore CGI script hashbang and use this interpreter */
const char *cgi_interpreter;
/*
* Comma-separated list of Content-Type overrides for path suffixes, e.g.
* ".txt=text/plain; charset=utf-8,.c=text/plain"
*/
const char *custom_mime_types;
};
This structure defines how mg_serve_http()
works.
Best practice is to set only required settings, and leave the rest as NULL.
mg_set_protocol_http_websocket()
void mg_set_protocol_http_websocket(struct mg_connection *nc);
Attach built-in HTTP event handler to the given connection. User-defined event handler will receive following extra events:
-
MG_EV_HTTP_REQUEST: HTTP request has arrived. Parsed HTTP request is passed *as
struct http_message
through the handler’svoid *ev_data
pointer. -
MG_EV_HTTP_REPLY: HTTP reply has arrived. Parsed HTTP reply is passed as
struct http_message
through the handler’svoid *ev_data
pointer. -
MG_EV_HTTP_CHUNK: HTTP chunked-encoding chunk has arrived. Parsed HTTP reply is passed as
struct http_message
through the handler’svoid *ev_data
pointer.http_message::body
would contain incomplete, reassembled HTTP body. It will grow with every new chunk arrived, and potentially can consume a lot of memory. An event handler may process the body as chunks are coming, and signal Mongoose to delete processed body by settingMG_F_DELETE_CHUNK
inmg_connection::flags
. When the last zero chunk is received, Mongoose sendsMG_EV_HTTP_REPLY
event with full reassembled body (if handler did not signal to delete chunks) or with empty body (if handler did signal to delete chunks). -
MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: server has received websocket handshake request.
ev_data
contains parsed HTTP request. -
MG_EV_WEBSOCKET_HANDSHAKE_DONE: server has completed Websocket handshake.
ev_data
isNULL
. -
MG_EV_WEBSOCKET_FRAME: new websocket frame has arrived.
ev_data
isstruct websocket_message *
mg_send_websocket_handshake()
void mg_send_websocket_handshake(struct mg_connection *nc, const char *uri,
const char *extra_headers);
Send websocket handshake to the server.
nc
must be a valid connection, connected to a server. uri
is an URI
to fetch, extra_headers` is extra HTTP headers to send or NULL
.
This function is intended to be used by websocket client.
mg_send_websocket_frame()
void mg_send_websocket_frame(struct mg_connection *nc, int op_and_flags,
const void *data, size_t data_len);
Send websocket frame to the remote end.
op_and_flags
specifies frame’s type, one of:
-
WEBSOCKET_OP_CONTINUE
-
WEBSOCKET_OP_TEXT
-
WEBSOCKET_OP_BINARY
-
WEBSOCKET_OP_CLOSE
-
WEBSOCKET_OP_PING
-
WEBSOCKET_OP_PONG
Orred with one of the flags:
-
WEBSOCKET_DONT_FIN: Don’t set the FIN flag on the frame to be sent.
data
and data_len
contain frame data.
mg_send_websocket_framev()
void mg_send_websocket_framev(struct mg_connection *nc, int op_and_flags,
const struct mg_str *strings, int num_strings);
Send multiple websocket frames.
Like mg_send_websocket_frame()
, but composes a frame from multiple buffers.
mg_printf_websocket_frame()
void mg_printf_websocket_frame(struct mg_connection *nc, int op_and_flags,
const char *fmt, ...);
Send websocket frame to the remote end.
Like mg_send_websocket_frame()
, but allows to create formatted message
with printf()
-like semantics.
mg_send_http_chunk()
void mg_send_http_chunk(struct mg_connection *nc, const char *buf, size_t len);
Send buffer buf
of size len
to the client using chunked HTTP encoding.
This function first sends buffer size as hex number + newline, then
buffer itself, then newline. For example,
mg_send_http_chunk(nc, "foo", 3)
whill append 3\r\nfoo\r\n
string to
the nc→send_mbuf
output IO buffer.
HTTP header "Transfer-Encoding: chunked" should be sent prior to using this function. |
do not forget to send empty chunk at the end of the response, to tell the client that everything was sent. Example: |
mg_printf_http_chunk(nc, "%s", "my response!");
mg_send_http_chunk(nc, "", 0); // Tell the client we're finished
mg_printf_http_chunk()
void mg_printf_http_chunk(struct mg_connection *, const char *, ...);
Send printf-formatted HTTP chunk.
Functionality is similar to mg_send_http_chunk()
.
mg_printf_html_escape()
void mg_printf_html_escape(struct mg_connection *, const char *, ...);
Send printf-formatted HTTP chunk, escaping HTML tags.
mg_parse_http()
int mg_parse_http(const char *s, int n, struct http_message *hm, int is_req);
Parse a HTTP message.
is_req
should be set to 1 if parsing request, 0 if reply.
Return number of bytes parsed. If HTTP message is
incomplete, 0
is returned. On parse error, negative number is returned.
mg_get_http_header()
struct mg_str *mg_get_http_header(struct http_message *hm, const char *name);
Search and return header name
in parsed HTTP message hm
.
If header is not found, NULL is returned. Example:
struct mg_str *host_hdr = mg_get_http_header(hm, "Host");
mg_http_parse_header()
int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
size_t buf_size);
Parse HTTP header hdr
. Find variable var_name
and store it’s value
in the buffer buf
, buf_size
. Return 0 if variable not found, non-zero
otherwise.
This function is supposed to parse cookies, authentication headers, etcetera. Example (error handling omitted):
char user[20]; struct mg_str *hdr = mg_get_http_header(hm, "Authorization"); mg_http_parse_header(hdr, "username", user, sizeof(user));
Return length of the variable’s value. If buffer is not large enough, or variable not found, 0 is returned.
mg_parse_multipart()
size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name,
size_t var_name_len, char *file_name,
size_t file_name_len, const char **chunk,
size_t *chunk_len);
Parse buffer buf
, buf_len
that contains multipart form data chunks.
Store chunk name in a var_name
, var_name_len
buffer.
If a chunk is an uploaded file, then file_name
, file_name_len
is
filled with an uploaded file name. chunk
, chunk_len
points to the chunk data.
Return: number of bytes to skip to the next chunk, or 0 if there are no more chunks.
Usage example:
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { switch(ev) { case MG_EV_HTTP_REQUEST: { struct http_message *hm = (struct http_message *) ev_data; char var_name[100], file_name[100]; const char *chunk; size_t chunk_len, n1, n2;
n1 = n2 = 0; while ((n2 = mg_parse_multipart(hm->body.p + n1, hm->body.len - n1, var_name, sizeof(var_name), file_name, sizeof(file_name), &chunk, &chunk_len)) > 0) { printf("var: %s, file_name: %s, size: %d, chunk: [%.*s]\n", var_name, file_name, (int) chunk_len, (int) chunk_len, chunk); n1 += n2; } } break;
mg_get_http_var()
int mg_get_http_var(const struct mg_str *, const char *, char *dst, size_t);
Fetch an HTTP form variable.
Fetch a variable name
from a buf
into a buffer specified by
dst
, dst_len
. Destination is always zero-terminated. Return length
of a fetched variable. If not found, 0 is returned. buf
must be
valid url-encoded buffer. If destination is too small, -1
is returned.
mg_http_create_digest_auth_header()
int mg_http_create_digest_auth_header(char *buf, size_t buf_len,
const char *method, const char *uri,
const char *auth_domain, const char *user,
const char *passwd);
Create Digest authentication header for client request.
mg_connect_http()
struct mg_connection *mg_connect_http(struct mg_mgr *,
mg_event_handler_t event_handler,
const char *url,
const char *extra_headers,
const char *post_data);
Helper function that creates outbound HTTP connection.
url
is a URL to fetch. It must be properly URL-encoded, e.g. have
no spaces, etc. By default, mg_connect_http()
sends Connection and
Host headers. extra_headers
is an extra HTTP headers to send, e.g.
"User-Agent: my-app\r\n"
.
If post_data
is NULL, then GET request is created. Otherwise, POST request
is created with the specified POST data. Examples:
nc1 = mg_connect_http(mgr, ev_handler_1, "http://www.google.com", NULL,
NULL);
nc2 = mg_connect_http(mgr, ev_handler_1, "https://github.com", NULL, NULL);
nc3 = mg_connect_http(mgr, ev_handler_1, "my_server:8000/form_submit/",
NULL, "var_1=value_1&var_2=value_2");
mg_serve_http()
void mg_serve_http(struct mg_connection *, struct http_message *,
struct mg_serve_http_opts);
Serve given HTTP request according to the options
.
Example code snippet:
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
struct http_message *hm = (struct http_message *) ev_data;
struct mg_serve_http_opts opts = { .document_root = "/var/www" }; // C99
switch (ev) {
case MG_EV_HTTP_REQUEST:
mg_serve_http(nc, hm, opts);
break;
default:
break;
}
}
JSON-RPC
struct mg_rpc_request
struct mg_rpc_request {
struct json_token *message; /* Whole RPC message */
struct json_token *id; /* Message ID */
struct json_token *method; /* Method name */
struct json_token *params; /* Method params */
};
JSON-RPC request
struct mg_rpc_reply
struct mg_rpc_reply {
struct json_token *message; /* Whole RPC message */
struct json_token *id; /* Message ID */
struct json_token *result; /* Remote call result */
};
JSON-RPC response
struct mg_rpc_error
struct mg_rpc_error {
struct json_token *message; /* Whole RPC message */
struct json_token *id; /* Message ID */
struct json_token *error_code; /* error.code */
struct json_token *error_message; /* error.message */
struct json_token *error_data; /* error.data, can be NULL */
};
JSON-RPC error
mg_rpc_parse_reply()
int mg_rpc_parse_reply(const char *buf, int len, struct json_token *toks,
int max_toks, struct mg_rpc_reply *,
struct mg_rpc_error *);
Parse JSON-RPC reply contained in buf
, len
into JSON tokens array
toks
, max_toks
. If buffer contains valid reply, reply
structure is
populated. The result of RPC call is located in reply.result
. On error,
error
structure is populated. Returns: the result of calling
parse_json(buf, len, toks, max_toks)
:
On success, an offset inside json_string
is returned
where parsing has finished. On failure, a negative number is
returned, one of:
-
#define JSON_STRING_INVALID -1
-
#define JSON_STRING_INCOMPLETE -2
-
#define JSON_TOKEN_ARRAY_TOO_SMALL -3
mg_rpc_create_request()
int mg_rpc_create_request(char *buf, int len, const char *method,
const char *id, const char *params_fmt, ...);
Create JSON-RPC request in a given buffer.
Return length of the request, which
can be larger then len
that indicates an overflow.
params_fmt
format string should conform to json_emit()
API,
see https://github.com/cesanta/frozen
mg_rpc_create_reply()
int mg_rpc_create_reply(char *buf, int len, const struct mg_rpc_request *req,
const char *result_fmt, ...);
Create JSON-RPC reply in a given buffer.
Return length of the reply, which
can be larger then len
that indicates an overflow.
result_fmt
format string should conform to json_emit()
API,
see https://github.com/cesanta/frozen
mg_rpc_create_error()
int mg_rpc_create_error(char *buf, int len, struct mg_rpc_request *req,
int code, const char *message, const char *fmt, ...);
Create JSON-RPC error reply in a given buffer.
Return length of the error, which
can be larger then len
that indicates an overflow.
fmt
format string should conform to json_emit()
API,
see https://github.com/cesanta/frozen
mg_rpc_create_std_error()
int mg_rpc_create_std_error(char *, int, struct mg_rpc_request *, int code);
Create JSON-RPC error in a given buffer.
Return length of the error, which
can be larger then len
that indicates an overflow. See
JSON_RPC_*_ERROR definitions for standard error values:
-
#define JSON_RPC_PARSE_ERROR (-32700)
-
#define JSON_RPC_INVALID_REQUEST_ERROR (-32600)
-
#define JSON_RPC_METHOD_NOT_FOUND_ERROR (-32601)
-
#define JSON_RPC_INVALID_PARAMS_ERROR (-32602)
-
#define JSON_RPC_INTERNAL_ERROR (-32603)
-
#define JSON_RPC_SERVER_ERROR (-32000)
mg_rpc_dispatch()
int mg_rpc_dispatch(const char *buf, int, char *dst, int dst_len,
const char **methods, mg_rpc_handler_t *handlers);
Dispatches a JSON-RPC request.
Parses JSON-RPC request contained in buf
, len
.
Then, dispatches the request to the correct handler method.
Valid method names should be specified in NULL
terminated array methods
, and corresponding handlers in handlers
.
Result is put in dst
, dst_len
. Return: length of the result, which
can be larger then dst_len
that indicates an overflow.
Overflown bytes are not written to the buffer.
If method is not found, an error is automatically generated.
DNS
struct mg_dmg_resource_record
struct mg_dmg_resource_record {
struct mg_str name; /* buffer with compressed name */
int rtype;
int rclass;
int ttl;
enum mg_dmg_resource_record_kind kind;
struct mg_str rdata; /* protocol data (can be a compressed name) */
};
DNS resource record.
struct mg_dmg_message
struct mg_dmg_message {
struct mg_str pkt; /* packet body */
uint16_t flags;
uint16_t transaction_id;
int num_questions;
int num_answers;
struct mg_dmg_resource_record questions[MG_MAX_DNS_QUESTIONS];
struct mg_dmg_resource_record answers[MG_MAX_DNS_ANSWERS];
};
DNS message (request and response).
mg_dmg_parse_record_data()
int mg_dmg_parse_record_data(struct mg_dmg_message *,
struct mg_dmg_resource_record *, void *, size_t);
Parse the record data from a DNS resource record.
-
A: struct in_addr *ina
-
AAAA: struct in6_addr *ina
-
CNAME: char buffer
Returns -1 on error.
TODO(mkm): MX
mg_send_dmg_query()
void mg_send_dmg_query(struct mg_connection *, const char *, int);
Send a DNS query to the remote end.
mg_dmg_insert_header()
int mg_dmg_insert_header(struct mbuf *, size_t, struct mg_dmg_message *);
Insert a DNS header to an IO buffer.
Return number of bytes inserted.
mg_dmg_copy_body()
int mg_dmg_copy_body(struct mbuf *, struct mg_dmg_message *);
Append already encoded body from an existing message.
This is useful when generating a DNS reply message which includes all question records.
Return number of appened bytes.
mg_dmg_encode_record()
int mg_dmg_encode_record(struct mbuf *, struct mg_dmg_resource_record *,
const char *, size_t, const void *, size_t);
Encode and append a DNS resource record to an IO buffer.
The record metadata is taken from the rr
parameter, while the name and data
are taken from the parameters, encoded in the appropriate format depending on
record type, and stored in the IO buffer. The encoded values might contain
offsets within the IO buffer. It’s thus important that the IO buffer doesn’t
get trimmed while a sequence of records are encoded while preparing a DNS
*reply.
This function doesn’t update the name
and rdata
pointers in the rr
*struct
because they might be invalidated as soon as the IO buffer grows again.
Return the number of bytes appened or -1 in case of error.
mg_parse_dns()
int mg_parse_dns(const char *, int, struct mg_dmg_message *);
Low-level: parses a DNS response.
mg_dmg_uncompress_name()
size_t mg_dmg_uncompress_name(struct mg_dmg_message *, struct mg_str *, char *,
int);
Uncompress a DNS compressed name.
The containing dns message is required because the compressed encoding and reference suffixes present elsewhere in the packet.
If name is less than dst_len
characters long, the remainder
of dst
is terminated with \0' characters. Otherwise, `dst
is not
*terminated.
If dst_len
is 0 dst
can be NULL.
Return the uncompressed name length.
mg_set_protocol_dns()
void mg_set_protocol_dns(struct mg_connection *);
Attach built-in DNS event handler to the given listening connection.
DNS event handler parses incoming UDP packets, treating them as DNS
requests. If incoming packet gets successfully parsed by the DNS event
handler, a user event handler will receive MG_DNS_REQUEST
event, with
ev_data
pointing to the parsed struct mg_dmg_message
.
See captive_dmg_server example on how to handle DNS request and send DNS reply.
DNS server
Disabled by default; enable with -DMG_ENABLE_DNS_SERVER
.
mg_dmg_create_reply()
struct mg_dmg_reply mg_dmg_create_reply(struct mbuf *, struct mg_dmg_message *);
Create a DNS reply.
The reply will be based on an existing query message msg
.
The query body will be appended to the output buffer.
"reply + recursion allowed" will be added to the message flags and
message’s num_answers will be set to 0.
Answer records can be appended with mg_dmg_send_reply
or by lower
level function defined in the DNS API.
In order to send the reply use mg_dmg_send_reply
.
It’s possible to use a connection’s send buffer as reply buffers,
and it will work for both UDP and TCP connections.
Example:
reply = mg_dmg_create_reply(&nc->send_mbuf, msg);
for (i = 0; i < msg->num_questions; i++) {
rr = &msg->questions[i];
if (rr->rtype == MG_DNS_A_RECORD) {
mg_dmg_reply_record(&reply, rr, 3600, &dummy_ip_addr, 4);
}
}
mg_dmg_send_reply(nc, &reply);
mg_dmg_reply_record()
int mg_dmg_reply_record(struct mg_dmg_reply *, struct mg_dmg_resource_record *,
const char *, int, int, const void *, size_t);
Append a DNS reply record to the IO buffer and to the DNS message.
The message num_answers field will be incremented. It’s caller’s duty to ensure num_answers is propertly initialized.
Returns -1 on error.
mg_dmg_send_reply()
int mg_dmg_send_reply(struct mg_connection *, struct mg_dmg_reply *);
Send a DNS reply through a connection.
The DNS data is stored in an IO buffer pointed by reply structure in r
.
This function mutates the content of that buffer in order to ensure that
the DNS header reflects size and flags of the mssage, that might have been
updated either with mg_dmg_reply_record
or by direct manipulation of
r→message
.
Once sent, the IO buffer will be trimmed unless the reply IO buffer is the connection’s send buffer and the connection is not in UDP mode.
MQTT
mg_set_protocol_mqtt()
void mg_set_protocol_mqtt(struct mg_connection *);
Attach built-in MQTT event handler to the given connection.
The user-defined event handler will receive following extra events:
-
MG_EV_MQTT_CONNACK
-
MG_EV_MQTT_PUBLISH
-
MG_EV_MQTT_PUBACK
-
MG_EV_MQTT_PUBREC
-
MG_EV_MQTT_PUBREL
-
MG_EV_MQTT_PUBCOMP
-
MG_EV_MQTT_SUBACK
mg_send_mqtt_handshake()
void mg_send_mqtt_handshake(struct mg_connection *nc, const char *client_id);
Send MQTT handshake.
mg_send_mqtt_handshake_opt()
void mg_send_mqtt_handshake_opt(struct mg_connection *, const char *client_id,
struct mg_send_mqtt_handshake_opts);
Send MQTT handshake with optional parameters.
mg_mqtt_publish()
void mg_mqtt_publish(struct mg_connection *nc, const char *topic,
uint16_t message_id, int flags, const void *data,
size_t len);
Publish a message to a given topic.
mg_mqtt_subscribe()
void mg_mqtt_subscribe(struct mg_connection *nc,
const struct mg_mqtt_topic_expression *topics,
size_t topics_len, uint16_t message_id);
Subscribe to a bunch of topics.
mg_mqtt_unsubscribe()
void mg_mqtt_unsubscribe(struct mg_connection *nc, char **topics,
size_t topics_len, uint16_t message_id);
Unsubscribe from a bunch of topics.
mg_mqtt_disconnect()
void mg_mqtt_disconnect(struct mg_connection *nc);
Send a DISCONNECT command.
mg_mqtt_connack()
void mg_mqtt_connack(struct mg_connection *, uint8_t);
Send a CONNACK command with a given return_code
.
mg_mqtt_puback()
void mg_mqtt_puback(struct mg_connection *, uint16_t);
Send a PUBACK command with a given message_id
.
mg_mqtt_pubrec()
void mg_mqtt_pubrec(struct mg_connection *, uint16_t);
Send a PUBREC command with a given message_id
.
mg_mqtt_pubrel()
void mg_mqtt_pubrel(struct mg_connection *, uint16_t);
Send a PUBREL command with a given message_id
.
mg_mqtt_pubcomp()
void mg_mqtt_pubcomp(struct mg_connection *, uint16_t);
Send a PUBCOMP command with a given message_id
.
mg_mqtt_suback()
void mg_mqtt_suback(struct mg_connection *, uint8_t *, size_t, uint16_t);
Send a SUBACK command with a given message_id
and a sequence of granted QoSs.
mg_mqtt_unsuback()
void mg_mqtt_unsuback(struct mg_connection *, uint16_t);
Send a UNSUBACK command with a given message_id
.
mg_mqtt_ping()
void mg_mqtt_ping(struct mg_connection *);
Send a PINGREQ command.
mg_mqtt_pong()
void mg_mqtt_pong(struct mg_connection *);
Send a PINGRESP command.
mg_mqtt_next_subscribe_topic()
int mg_mqtt_next_subscribe_topic(struct mg_mqtt_message *, struct mg_str *,
uint8_t *, int);
Extract the next topic expression from a SUBSCRIBE command payload.
Topic expression name will point to a string in the payload buffer. Return the pos of the next topic expression or -1 when the list of topics is exhausted.
MQTT Broker
struct mg_mqtt_session
struct mg_mqtt_session {
struct mg_mqtt_broker *brk; /* Broker */
struct mg_mqtt_session *next, *prev; /* mg_mqtt_broker::sessions linkage */
struct mg_connection *nc; /* Connection with the client */
size_t num_subscriptions; /* Size of `subscriptions` array */
struct mg_mqtt_topic_expression *subscriptions;
void *user_data; /* User data */
};
MQTT session (Broker side).
struct mg_mqtt_broker
struct mg_mqtt_broker {
struct mg_mqtt_session *sessions; /* Session list */
void *user_data; /* User data */
};
MQTT broker.
mg_mqtt_broker_init()
void mg_mqtt_broker_init(struct mg_mqtt_broker *, void *);
Initialize a MQTT broker.
mg_mqtt_broker()
void mg_mqtt_broker(struct mg_connection *, int, void *);
Process a MQTT broker message.
Listening connection expects a pointer to an initialized mg_mqtt_broker
structure in the user_data
field.
Basic usage:
mg_mqtt_broker_init(&brk, NULL);
if ((nc = mg_bind(&mgr, address, mg_mqtt_broker)) == NULL) {
// fail;
}
nc->user_data = &brk;
New incoming connections will receive a mg_mqtt_session
structure
in the connection user_data
. The original user_data
will be stored
in the user_data
field of the session structure. This allows the user
handler to store user data before mg_mqtt_broker
creates the session.
Since only the MG_EV_ACCEPT message is processed by the listening socket,
for most events the user_data
will thus point to a mg_mqtt_session
.
mg_mqtt_next()
struct mg_mqtt_session *mg_mqtt_next(struct mg_mqtt_broker *,
struct mg_mqtt_session *);
Iterate over all mqtt sessions connections. Example:
struct mg_mqtt_session *s; for (s = mg_mqtt_next(brk, NULL); s != NULL; s = mg_mqtt_next(brk, s)) { // Do something }
CoAP
CoAP message format:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- |Ver| T | TKL | Code | Message ID | Token (if any, TKL bytes) ... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- | Options (if any) ... |1 1 1 1 1 1 1 1| Payload (if any) ... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
struct mg_coap_option
struct mg_coap_option {
struct mg_coap_option *next;
uint32_t number;
struct mg_str value;
};
CoAP options. Use mg_coap_add_option and mg_coap_free_options for creation and destruction.
struct mg_coap_message
struct mg_coap_message {
uint32_t flags;
uint8_t msg_type;
uint8_t code_class;
uint8_t code_detail;
uint16_t msg_id;
struct mg_str token;
struct mg_coap_option *options;
struct mg_str payload;
struct mg_coap_option *optiomg_tail;
};
CoAP message. See RFC 7252 for details.
mg_set_protocol_coap()
int mg_set_protocol_coap(struct mg_connection *nc);
Set CoAP protocol handler - trigger CoAP specific events
mg_coap_add_option()
struct mg_coap_option *mg_coap_add_option(struct mg_coap_message *cm,
uint32_t number, char *value,
size_t len);
Add new option to mg_coap_message structure. Returns pointer to the newly created option.
mg_coap_free_options()
void mg_coap_free_options(struct mg_coap_message *cm);
Free the memory allocated for options, if cm paramater doesn’t contain any option does nothing.
mg_coap_send_message()
uint32_t mg_coap_send_message(struct mg_connection *nc,
struct mg_coap_message *cm);
Compose CoAP message from mg_coap_message
and send it into nc
connection.
Return 0 on success. On error, it is a bitmask:
-
#define MG_COAP_ERROR 0x10000
-
#define MG_COAP_FORMAT_ERROR (MG_COAP_ERROR | 0x20000)
-
#define MG_COAP_IGNORE (MG_COAP_ERROR | 0x40000)
-
#define MG_COAP_NOT_ENOUGH_DATA (MG_COAP_ERROR | 0x80000)
-
#define MG_COAP_NETWORK_ERROR (MG_COAP_ERROR | 0x100000)
mg_coap_send_ack()
uint32_t mg_coap_send_ack(struct mg_connection *nc, uint16_t msg_id);
Compose CoAP acknowledgement from mg_coap_message
and send it into nc
connection.
Return value: see mg_coap_send_message()
mg_coap_parse()
uint32_t mg_coap_parse(struct mbuf *io, struct mg_coap_message *cm);
Parse COAP message and fills mg_coap_message and returns cm→flags. This is a helper function.
usually CoAP work over UDP, so lack of data means format error, but in theory it is possible to use CoAP over TCP (according to RFC) |
The caller have to check results and treat COAP_NOT_ENOUGH_DATA according to underlying protocol:
-
in case of UDP COAP_NOT_ENOUGH_DATA means COAP_FORMAT_ERROR,
-
in case of TCP client can try to receive more data
Return value: see mg_coap_send_message()
mg_coap_compose()
uint32_t mg_coap_compose(struct mg_coap_message *cm, struct mbuf *io);
Composes CoAP message from mg_coap_message structure.
This is a helper function.
Return value: see mg_coap_send_message()
Utilities
mg_skip()
const char *mg_skip(const char *s, const char *end_string,
const char *delimiters, struct mg_str *v);
Fetch substring from input string s
, end
into v
.
Skips initial delimiter characters. Records first non-delimiter character
as the beginning of substring v
. Then scans the rest of the string
until a delimiter character or end-of-string is found.
delimiters
is a 0-terminated string containing delimiter characters.
Either one of delimiters
or end_string
terminates the search.
Return an s
pointer, advanced forward where parsing stopped.
mg_ncasecmp()
int mg_ncasecmp(const char *s1, const char *s2, size_t len);
Cross-platform version of strncasecmp()
.
mg_casecmp()
int mg_casecmp(const char *s1, const char *s2);
Cross-platform version of strcasecmp()
.
mg_vcmp()
int mg_vcmp(const struct mg_str *str2, const char *str1);
Cross-platform version of strcmp()
where where first string is
specified by struct mg_str
.
mg_vcasecmp()
int mg_vcasecmp(const struct mg_str *str2, const char *str1);
Cross-platform version of strncasecmp()
where first string is
specified by struct mg_str
.
mg_base64_decode()
int mg_base64_decode(const unsigned char *s, int len, char *dst);
Decode base64-encoded string s
, len
into the destination dst
.
Destination has to have enough space to hold decoded buffer.
Decoding stops either when all string has been decoded, or invalid
character appeared.
Destination is '\0'-terminated.
Return number of decoded characters. On success, that should be equal to
len
. On error (invalid character) the return value is smaller then len
.
mg_base64_encode()
void mg_base64_encode(const unsigned char *src, int src_len, char *dst);
Base64-encode chunk of memory src
, src_len
into the destination dst
.
Destination has to have enough space to hold encoded buffer.
Destination is '\0'-terminated.
mg_stat()
int mg_stat(const char *path, cs_stat_t *st);
Perform a 64-bit stat()
call against given file.
path
should be UTF8 encoded.
Return value is the same as for stat()
syscall.
mg_fopen()
FILE *mg_fopen(const char *path, const char *mode);
Open the given file and return a file stream.
path
and mode
should be UTF8 encoded.
Return value is the same as for the fopen()
call.
mg_open()
int mg_open(const char *path, int flag, int mode);
Open the given file and return a file stream.
path
should be UTF8 encoded.
Return value is the same as for the open()
syscall.
mg_start_thread()
void *mg_start_thread(void *(*thread_func);
Start a new detached thread.
Arguments and semantic is the same as pthead’s pthread_create()
.
thread_func
is a thread function, thread_func_param
is a parameter
that is passed to the thread function.
mg_sock_to_str()
void mg_sock_to_str(sock_t sock, char *buf, size_t len, int flags);
Convert socket’s local or remote address into string.
The flags
parameter is a bit mask that controls the behavior,
see MG_SOCK_STRINGIFY_*
definitions.
-
MG_SOCK_STRINGIFY_IP - print IP address
-
MG_SOCK_STRINGIFY_PORT - print port number
-
MG_SOCK_STRINGIFY_REMOTE - print remote peer’s IP/port, not local address
If both port number and IP address are printed, they are separated by :
.
If compiled with -DMG_ENABLE_IPV6
, IPv6 addresses are supported.
mg_sock_addr_to_str()
void mg_sock_addr_to_str(const union socket_address *sa, char *buf, size_t len,
int flags);
Convert socket’s address into string.
flags
is MG_SOCK_STRINGIFY_IP and/or MG_SOCK_STRINGIFY_PORT.
mg_hexdump()
int mg_hexdump(const void *buf, int len, char *dst, int dst_len);
Generates human-readable hexdump of memory chunk.
Takes a memory buffer buf
of length len
and creates a hex dump of that
buffer in dst
. Generated output is a-la hexdump(1).
Return length of generated string, excluding terminating \0
. If returned
length is bigger than dst_len
, overflow bytes are discarded.
mg_hexdump_connection()
void mg_hexdump_connection(struct mg_connection *nc, const char *path,
int num_bytes, int ev);
Generates human-readable hexdump of the data sent or received by connection.
path
is a file name where hexdump should be written. num_bytes
is
a number of bytes sent/received. ev
is one of the MG_*
events sent to
an event handler. This function is supposed to be called from the
event handler.
mg_avprintf()
int mg_avprintf(char **buf, size_t size, const char *fmt, va_list ap);
Print message to buffer. If buffer is large enough to hold the message, return buffer. If buffer is to small, allocate large enough buffer on heap, and return allocated buffer. This is a supposed use case:
char buf[5], *p = buf; p = mg_avprintf(&p, sizeof(buf), "%s", "hi there"); use_p_somehow(p); if (p != buf) { free(p); }
The purpose of this is to avoid malloc-ing if generated strings are small.
mg_is_big_endian()
int mg_is_big_endian(void);
Return true if target platform is big endian.
mg_next_comma_list_entry()
const char *mg_next_comma_list_entry(const char *list, struct mg_str *val,
struct mg_str *eq_val);
A helper function for traversing a comma separated list of values.
It returns a list pointer shifted to the next value, or NULL if the end
of the list found.
Value is stored in val vector. If value has form "x=y", then eq_val
vector is initialized to point to the "y" part, and val vector length
is adjusted to point only to "x".
If list is just a comma separated list of entries, like "aa,bb,cc" then
eq_val
will contain zero-length string.
The purpose of this function is to parse comma separated string without any copying/memory allocation.
mg_match_prefix()
int mg_match_prefix(const char *pattern, int pattern_len, const char *str);
Match 0-terminated string against a glob pattern. Match is case-insensitive. Return number of bytes matched, or -1 if no match.