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. If this URL is 'https', the is_tls flag will be set
  • 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.

Parameters:

  • mgr - An event manager
  • url - A URL, specifies the remote URL, e.g. http://google.com. If this URL is 'https', the is_tls flag will be set. See mg_connect()
  • 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. Possible errors are: not enough memory, a NULL URL, or, in the case of our built-in TCP/IP stack, the network not being ready.

Note: This function does not connect to the requested peer, it allocates required resources and starts the connection process. Once our peer is really connected, an MG_EV_CONNECT event is sent to the connection event handler.

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.

In addition to overwriting autodetection based on an extension, you can also use * as an extension in mime_types to force a particular MIME type for unknown extensions:

sopts.mime_types = "*=preferred/default,txt=override/text"

mg_http_serve_dir()

void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
                       const struct mg_http_serve_opts *opts);

Serve static files according to the given options. Files can also be gzip compressed, including the directory index. All compressed files must end in .gz and there must not exist a file with the same name without the extension, otherwise it will take precedence; see mg_http_serve_file()

NOTE: In order to enable SSI, you need to set the -DMG_ENABLE_SSI=1 build flag.

NOTE: Avoid double dots .. in 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);
mg_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->buf);
    } else {
      mg_http_reply(c, 200, "", "Oh no, header is not set...");
    }
  }
}

mg_http_get_header_var()

struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v);

Parse HTTP header (e.g. Cookie header) which has form name1=value1; name2=value2; ... and fetch a given variable.

Parameters:

  • s - HTTP 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_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.

struct mg_http_part part;
size_t pos = 0;

while ((pos = mg_http_next_multipart(body, pos, &part)) != 0) {
  MG_INFO(("Chunk name: [%.*s] filename: [%.*s] length: %lu bytes",
           part.name.len, part.name.buf,
           part.filename.len, part.filename.buf, part.body.len));
  // Use this chunk ....
}

A diagram below shows how mg_http_next_multipart() in action:

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_match(hm->uri, mg_str("/upload"), NULL)) {
      mg_http_upload(c, hm, &mg_fs_posix, "/tmp", 99999);
    } else {
      struct mg_http_serve_opts opts = {.root_dir = "."};   // Serve
      mg_http_serve_dir(c, ev_data, &opts);                 // static content
    }
  }
}

mg_http_start_upload()

void mg_http_start_upload(struct mg_connection *c, struct mg_http_message *hm,
                          struct mg_str name, struct mg_str dir,
                          struct mg_fs *fs,
                          void (*fn)(struct mg_connection *, const char *));

Start streaming an HTTP request body into a file. This helper is intended to be called from the MG_EV_HTTP_HDRS event, after HTTP headers have been parsed but before the whole body has been received. It takes over the connection handler, writes incoming body data to dir/name, and calls fn when the upload finishes or fails.

Unlike mg_http_upload(), this function does not require the client to split a file into offset-based chunks. The client sends one POST or PUT request with a valid Content-Length, and Mongoose streams the body as it arrives.

The file name is checked with mg_path_is_sane(). Path traversal names such as ../file.bin are rejected and reported to the callback.

Parameters:

  • c - a connection
  • hm - a parsed HTTP message
  • name - file name to create under dir
  • dir - target directory
  • fs - filesystem where to write the file, e.g. &mg_fs_posix
  • fn - completion callback; receives NULL on success, or an error message on failure

Return value: none

Usage example:

static void upload_done(struct mg_connection *c, const char *errmsg) {
  mg_http_reply(c, errmsg == NULL ? 200 : 400, "", "%s\n",
                errmsg == NULL ? "ok" : errmsg);
  c->is_draining = 1;
}

static void fn(struct mg_connection *c, int ev, void *ev_data) {
  if (ev == MG_EV_HTTP_HDRS) {
    struct mg_http_message *hm = (struct mg_http_message *) ev_data;
    if (mg_match(hm->uri, mg_str("/upload/#"), NULL)) {
      struct mg_str name = mg_str_n(hm->uri.buf + 8, hm->uri.len - 8);
      mg_http_start_upload(c, hm, name, mg_str("/tmp"), &mg_fs_posix,
                           upload_done);
    }
  }
}

mg_http_start_ota()

void mg_http_start_ota(struct mg_connection *c, struct mg_http_message *hm,
                       void (*fn)(struct mg_connection *, const char *));

Start streaming an HTTP request or response body into the OTA writer. This helper is intended to be called from MG_EV_HTTP_HDRS, after the headers are available and the firmware image size is known from Content-Length.

The function calls mg_ota_begin(hm->body.len), streams incoming body data with mg_ota_write(), then calls mg_ota_end() when all expected bytes have been received. The completion callback receives NULL on success, or an error message such as ota begin failed, write error, OTA finalize failed, or connection closed.

Parameters:

  • c - a connection
  • hm - a parsed HTTP message
  • fn - completion callback; receives NULL on success, or an error message on failure

Return value: none

Usage example:

static void ota_done(struct mg_connection *c, const char *errmsg) {
  mg_http_reply(c, errmsg == NULL ? 200 : 500, "", "%s\n",
                errmsg == NULL ? "ok" : errmsg);
  c->is_draining = 1;
}

static void fn(struct mg_connection *c, int ev, void *ev_data) {
  if (ev == MG_EV_HTTP_HDRS) {
    struct mg_http_message *hm = (struct mg_http_message *) ev_data;
    if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
      mg_http_start_ota(c, hm, ota_done);
    }
  }
}