diff mbox series

[1/3] mongoose: updated to 6.18

Message ID 20201226112005.14553-1-sbabic@denx.de
State Accepted
Headers show
Series [1/3] mongoose: updated to 6.18 | expand

Commit Message

Stefano Babic Dec. 26, 2020, 11:20 a.m. UTC
Signed-off-by: Stefano Babic <sbabic@denx.de>
---
 mongoose/mongoose.c | 548 +++++++++++++++++++++-----------------------
 mongoose/mongoose.h | 541 +++----------------------------------------
 2 files changed, 298 insertions(+), 791 deletions(-)
diff mbox series

Patch

diff --git a/mongoose/mongoose.c b/mongoose/mongoose.c
index c8c3fd1..fde66de 100644
--- a/mongoose/mongoose.c
+++ b/mongoose/mongoose.c
@@ -1,14 +1,27 @@ 
-#include "mongoose.h"
-#ifdef MG_MODULE_LINES
-#line 1 "mongoose/src/mg_internal.h"
-#endif
 /*
- * Copyright (c) 2014-2018 Cesanta Software Limited
+ * Copyright (c) 2004-2013 Sergey Lyubka
+ * Copyright (c) 2013-2020 Cesanta Software Limited
  * All rights reserved
  *
- * SPDX-License-Identifier: GPL-2.0
+ * This software is dual-licensed: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. For the terms of this
+ * license, see <http://www.gnu.org/licenses/>.
+ *
+ * You are free to use this software under the terms of the GNU General
+ * Public License, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * Alternatively, you can license this software under a commercial
+ * license, as set out in <https://www.cesanta.com/license>.
  */
 
+#include "mongoose.h"
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_internal.h"
+#endif
+
 #ifndef CS_MONGOOSE_SRC_INTERNAL_H_
 #define CS_MONGOOSE_SRC_INTERNAL_H_
 
@@ -612,9 +625,9 @@  int cs_log_print_prefix(enum cs_log_level level, const char *file, int ln) {
 
   if (s_file_level != NULL) {
     enum cs_log_level pll = cs_log_level;
-    struct mg_str mgfl = mg_mk_str(s_file_level), ps = MG_MK_STR_N(prefix, pl);
+    struct mg_str fl = mg_mk_str(s_file_level), ps = MG_MK_STR_N(prefix, pl);
     struct mg_str k, v;
-    while ((mgfl = mg_next_comma_list_entry_n(mgfl, &k, &v)).p != NULL) {
+    while ((fl = mg_next_comma_list_entry_n(fl, &k, &v)).p != NULL) {
       bool yes = !(!mg_str_starts_with(ps, k) || v.len == 0);
       if (!yes) continue;
       pll = (enum cs_log_level)(*v.p - '0');
@@ -1188,7 +1201,7 @@  static uint32_t blk0(union char64long16 *block, int i) {
   z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
   w = rol(w, 30);
 
-static void cs_sha1_transform(uint32_t state[5], const unsigned char buffer[64]) {
+void cs_sha1_transform(uint32_t state[5], const unsigned char buffer[64]) {
   uint32_t a, b, c, d, e;
   union char64long16 block[1];
 
@@ -1606,8 +1619,10 @@  int mg_strcmp(const struct mg_str str1, const struct mg_str str2) WEAK;
 int mg_strcmp(const struct mg_str str1, const struct mg_str str2) {
   size_t i = 0;
   while (i < str1.len && i < str2.len) {
-    if (str1.p[i] < str2.p[i]) return -1;
-    if (str1.p[i] > str2.p[i]) return 1;
+    int c1 = str1.p[i];
+    int c2 = str2.p[i];
+    if (c1 < c2) return -1;
+    if (c1 > c2) return 1;
     i++;
   }
   if (i < str1.len) return 1;
@@ -1629,6 +1644,21 @@  int mg_strncmp(const struct mg_str str1, const struct mg_str str2, size_t n) {
   return mg_strcmp(s1, s2);
 }
 
+int mg_strcasecmp(const struct mg_str str1, const struct mg_str str2) WEAK;
+int mg_strcasecmp(const struct mg_str str1, const struct mg_str str2) {
+  size_t i = 0;
+  while (i < str1.len && i < str2.len) {
+    int c1 = tolower((int) str1.p[i]);
+    int c2 = tolower((int) str2.p[i]);
+    if (c1 < c2) return -1;
+    if (c1 > c2) return 1;
+    i++;
+  }
+  if (i < str1.len) return 1;
+  if (i < str2.len) return -1;
+  return 0;
+}
+
 void mg_strfree(struct mg_str *s) WEAK;
 void mg_strfree(struct mg_str *s) {
   char *sp = (char *) s->p;
@@ -2191,23 +2221,6 @@  size_t mg_match_prefix(const char *pattern, int pattern_len, const char *str) {
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_net.c"
 #endif
-/*
- * Copyright (c) 2014 Cesanta Software Limited
- * All rights reserved
- *
- * This software is dual-licensed: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation. For the terms of this
- * license, see <http://www.gnu.org/licenses/>.
- *
- * You are free to use this software under the terms of the GNU General
- * Public License, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * Alternatively, you can license this software under a commercial
- * license, as set out in <https://www.cesanta.com/license>.
- */
 
 /* Amalgamated: #include "common/cs_time.h" */
 /* Amalgamated: #include "mg_dns.h" */
@@ -2354,7 +2367,7 @@  int mg_if_poll(struct mg_connection *nc, double now) {
   return 1;
 }
 
-static void mg_destroy_conn(struct mg_connection *conn, int destroy_if) {
+void mg_destroy_conn(struct mg_connection *conn, int destroy_if) {
   if (conn->sock != INVALID_SOCKET) { /* Don't print timer-only conns */
     LOG(LL_DEBUG, ("%p 0x%lx %d", conn, conn->flags, destroy_if));
   }
@@ -2733,7 +2746,7 @@  struct mg_connection *mg_if_accept_new_conn(struct mg_connection *lc) {
   nc->iface = lc->iface;
   if (lc->flags & MG_F_SSL) nc->flags |= MG_F_SSL;
   mg_add_conn(nc->mgr, nc);
-  LOG(LL_DEBUG, ("%p %p %d %d", lc, nc, nc->sock, (int) nc->flags));
+  LOG(LL_DEBUG, ("%p %p %d %#x", lc, nc, (int) nc->sock, (int) nc->flags));
   return nc;
 }
 
@@ -3070,7 +3083,7 @@  struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *address,
   return mg_connect_opt(mgr, address, MG_CB(callback, user_data), opts);
 }
 
-static void mg_ev_handler_empty(struct mg_connection *c, int ev,
+void mg_ev_handler_empty(struct mg_connection *c, int ev,
                          void *ev_data MG_UD_ARG(void *user_data)) {
   (void) c;
   (void) ev;
@@ -3363,13 +3376,13 @@  double mg_set_timer(struct mg_connection *c, double timestamp) {
   return result;
 }
 
-static void mg_sock_set(struct mg_connection *nc, sock_t sock) {
+void mg_sock_set(struct mg_connection *nc, sock_t sock) {
   if (sock != INVALID_SOCKET) {
     nc->iface->vtable->sock_set(nc, sock);
   }
 }
 
-static void mg_if_get_conn_addr(struct mg_connection *nc, int remote,
+void mg_if_get_conn_addr(struct mg_connection *nc, int remote,
                          union socket_address *sa) {
   nc->iface->vtable->get_conn_addr(nc, remote, sa);
 }
@@ -3404,10 +3417,6 @@  double mg_time(void) {
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_net_if_socket.h"
 #endif
-/*
- * Copyright (c) 2014-2016 Cesanta Software Limited
- * All rights reserved
- */
 
 #ifndef CS_MONGOOSE_SRC_NET_IF_SOCKET_H_
 #define CS_MONGOOSE_SRC_NET_IF_SOCKET_H_
@@ -3432,10 +3441,6 @@  extern const struct mg_iface_vtable mg_socket_iface_vtable;
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_net_if_socks.h"
 #endif
-/*
-* Copyright (c) 2014-2017 Cesanta Software Limited
-* All rights reserved
-*/
 
 #ifndef CS_MONGOOSE_SRC_NET_IF_SOCKS_H_
 #define CS_MONGOOSE_SRC_NET_IF_SOCKS_H_
@@ -3513,23 +3518,6 @@  double mg_mgr_min_timer(const struct mg_mgr *mgr) {
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_net_if_null.c"
 #endif
-/*
- * Copyright (c) 2018 Cesanta Software Limited
- * All rights reserved
- *
- * This software is dual-licensed: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation. For the terms of this
- * license, see <http://www.gnu.org/licenses/>.
- *
- * You are free to use this software under the terms of the GNU General
- * Public License, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * Alternatively, you can license this software under a commercial
- * license, as set out in <https://www.cesanta.com/license>.
- */
 
 static void mg_null_if_connect_tcp(struct mg_connection *c,
                                    const union socket_address *sa) {
@@ -3571,14 +3559,14 @@  static int mg_null_if_udp_send(struct mg_connection *c, const void *buf,
   return -1;
 }
 
-static int mg_null_if_tcp_recv(struct mg_connection *c, void *buf, size_t len) {
+int mg_null_if_tcp_recv(struct mg_connection *c, void *buf, size_t len) {
   (void) c;
   (void) buf;
   (void) len;
   return -1;
 }
 
-static int mg_null_if_udp_recv(struct mg_connection *c, void *buf, size_t len,
+int mg_null_if_udp_recv(struct mg_connection *c, void *buf, size_t len,
                         union socket_address *sa, size_t *sa_len) {
   (void) c;
   (void) buf;
@@ -3657,10 +3645,6 @@  const struct mg_iface_vtable mg_default_iface_vtable = MG_NULL_IFACE_VTABLE;
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_net_if_socket.c"
 #endif
-/*
- * Copyright (c) 2014-2016 Cesanta Software Limited
- * All rights reserved
- */
 
 #if MG_ENABLE_NET_IF_SOCKET
 
@@ -3671,7 +3655,7 @@  const struct mg_iface_vtable mg_default_iface_vtable = MG_NULL_IFACE_VTABLE;
 static sock_t mg_open_listening_socket(union socket_address *sa, int type,
                                        int proto);
 
-static void mg_set_non_blocking_mode(sock_t sock) {
+void mg_set_non_blocking_mode(sock_t sock) {
 #ifdef _WIN32
   unsigned long on = 1;
   ioctlsocket(sock, FIONBIO, &on);
@@ -3693,7 +3677,7 @@  static int mg_is_error(void) {
       ;
 }
 
-static void mg_socket_if_connect_tcp(struct mg_connection *nc,
+void mg_socket_if_connect_tcp(struct mg_connection *nc,
                               const union socket_address *sa) {
   int rc, proto = 0;
   nc->sock = socket(AF_INET, SOCK_STREAM, proto);
@@ -3706,11 +3690,11 @@  static void mg_socket_if_connect_tcp(struct mg_connection *nc,
 #endif
   rc = connect(nc->sock, &sa->sa, sizeof(sa->sin));
   nc->err = rc < 0 && mg_is_error() ? mg_get_errno() : 0;
-  DBG(("%p sock %d rc %d errno %d err %d", nc, nc->sock, rc, mg_get_errno(),
-       nc->err));
+  DBG(("%p sock %d rc %d errno %d err %d", nc, (int) nc->sock, rc,
+      mg_get_errno(), nc->err));
 }
 
-static void mg_socket_if_connect_udp(struct mg_connection *nc) {
+void mg_socket_if_connect_udp(struct mg_connection *nc) {
   nc->sock = socket(AF_INET, SOCK_DGRAM, 0);
   if (nc->sock == INVALID_SOCKET) {
     nc->err = mg_get_errno() ? mg_get_errno() : 1;
@@ -3727,7 +3711,7 @@  static void mg_socket_if_connect_udp(struct mg_connection *nc) {
   nc->err = 0;
 }
 
-static int mg_socket_if_listen_tcp(struct mg_connection *nc,
+int mg_socket_if_listen_tcp(struct mg_connection *nc,
                             union socket_address *sa) {
   int proto = 0;
   sock_t sock = mg_open_listening_socket(sa, SOCK_STREAM, proto);
@@ -3782,12 +3766,12 @@  static int mg_socket_if_udp_recv(struct mg_connection *nc, void *buf,
   return n;
 }
 
-static int mg_socket_if_create_conn(struct mg_connection *nc) {
+int mg_socket_if_create_conn(struct mg_connection *nc) {
   (void) nc;
   return 1;
 }
 
-static void mg_socket_if_destroy_conn(struct mg_connection *nc) {
+void mg_socket_if_destroy_conn(struct mg_connection *nc) {
   if (nc->sock == INVALID_SOCKET) return;
   if (!(nc->flags & MG_F_UDP)) {
     closesocket(nc->sock);
@@ -3873,12 +3857,12 @@  static sock_t mg_open_listening_socket(union socket_address *sa, int type,
 #define _MG_F_FD_CAN_WRITE 1 << 1
 #define _MG_F_FD_ERROR 1 << 2
 
-static void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) {
+void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) {
   int worth_logging =
       fd_flags != 0 || (nc->flags & (MG_F_WANT_READ | MG_F_WANT_WRITE));
   if (worth_logging) {
-    DBG(("%p fd=%d fd_flags=%d nc_flags=0x%lx rmbl=%d smbl=%d", nc, nc->sock,
-         fd_flags, nc->flags, (int) nc->recv_mbuf.len,
+    DBG(("%p fd=%d fd_flags=%d nc_flags=0x%lx rmbl=%d smbl=%d", nc,
+         (int) nc->sock, fd_flags, nc->flags, (int) nc->recv_mbuf.len,
          (int) nc->send_mbuf.len));
   }
 
@@ -3930,7 +3914,7 @@  static void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double no
   if (fd_flags & _MG_F_FD_CAN_WRITE) mg_if_can_send_cb(nc);
 
   if (worth_logging) {
-    DBG(("%p after fd=%d nc_flags=0x%lx rmbl=%d smbl=%d", nc, nc->sock,
+    DBG(("%p after fd=%d nc_flags=0x%lx rmbl=%d smbl=%d", nc, (int) nc->sock,
          nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len));
   }
 }
@@ -3938,8 +3922,7 @@  static void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double no
 #if MG_ENABLE_BROADCAST
 static void mg_mgr_handle_ctl_sock(struct mg_mgr *mgr) {
   struct ctl_msg ctl_msg;
-  int len =
-      (int) MG_RECV_FUNC(mgr->ctl[1], (char *) &ctl_msg, sizeof(ctl_msg), 0);
+  int len = (int) MG_RECV_FUNC(mgr->ctl[1], (char *) &ctl_msg, sizeof(ctl_msg), 0);
   size_t dummy = MG_SEND_FUNC(mgr->ctl[1], ctl_msg.message, 1, 0);
   DBG(("read %d from ctl socket", len));
   (void) dummy; /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25509 */
@@ -3954,14 +3937,14 @@  static void mg_mgr_handle_ctl_sock(struct mg_mgr *mgr) {
 #endif
 
 /* Associate a socket to a connection. */
-static void mg_socket_if_sock_set(struct mg_connection *nc, sock_t sock) {
+void mg_socket_if_sock_set(struct mg_connection *nc, sock_t sock) {
   mg_set_non_blocking_mode(sock);
   mg_set_close_on_exec(sock);
   nc->sock = sock;
-  DBG(("%p %d", nc, sock));
+  DBG(("%p %d", nc, (int) sock));
 }
 
-static void mg_socket_if_init(struct mg_iface *iface) {
+void mg_socket_if_init(struct mg_iface *iface) {
   (void) iface;
   DBG(("%p using select()", iface->mgr));
 #if MG_ENABLE_BROADCAST
@@ -3969,19 +3952,19 @@  static void mg_socket_if_init(struct mg_iface *iface) {
 #endif
 }
 
-static void mg_socket_if_free(struct mg_iface *iface) {
+void mg_socket_if_free(struct mg_iface *iface) {
   (void) iface;
 }
 
-static void mg_socket_if_add_conn(struct mg_connection *nc) {
+void mg_socket_if_add_conn(struct mg_connection *nc) {
   (void) nc;
 }
 
-static void mg_socket_if_remove_conn(struct mg_connection *nc) {
+void mg_socket_if_remove_conn(struct mg_connection *nc) {
   (void) nc;
 }
 
-static void mg_add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) {
+void mg_add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) {
   if (sock != INVALID_SOCKET
 #ifdef __unix__
       && sock < (sock_t) FD_SETSIZE
@@ -3994,7 +3977,7 @@  static void mg_add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) {
   }
 }
 
-static time_t mg_socket_if_poll(struct mg_iface *iface, int timeout_ms) {
+time_t mg_socket_if_poll(struct mg_iface *iface, int timeout_ms) {
   struct mg_mgr *mgr = iface->mgr;
   double now = mg_time();
   double min_timer;
@@ -4201,7 +4184,7 @@  void mg_sock_to_str(sock_t sock, char *buf, size_t len, int flags) {
   mg_sock_addr_to_str(&sa, buf, len, flags);
 }
 
-static void mg_socket_if_get_conn_addr(struct mg_connection *nc, int remote,
+void mg_socket_if_get_conn_addr(struct mg_connection *nc, int remote,
                                 union socket_address *sa) {
   if ((nc->flags & MG_F_UDP) && remote) {
     memcpy(sa, &nc->sa, sizeof(*sa));
@@ -4242,10 +4225,6 @@  const struct mg_iface_vtable mg_default_iface_vtable = MG_SOCKET_IFACE_VTABLE;
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_net_if_socks.c"
 #endif
-/*
- * Copyright (c) 2014-2016 Cesanta Software Limited
- * All rights reserved
- */
 
 #if MG_ENABLE_SOCKS
 
@@ -4482,10 +4461,6 @@  struct mg_iface *mg_socks_mk_iface(struct mg_mgr *mgr, const char *proxy_addr) {
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_ssl_if_openssl.c"
 #endif
-/*
- * Copyright (c) 2014-2016 Cesanta Software Limited
- * All rights reserved
- */
 
 #if MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_OPENSSL
 
@@ -4493,15 +4468,13 @@  struct mg_iface *mg_socks_mk_iface(struct mg_mgr *mgr, const char *proxy_addr) {
 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 #endif
 
-#ifdef CONFIG_SSL_IMPL_WOLFSSL
-#include <wolfssl/openssl/ssl.h>
-#include <wolfssl/openssl/tls1.h>
-#else
 #include <openssl/ssl.h>
+#include <openssl/err.h>
 #ifndef KR_VERSION
 #include <openssl/tls1.h>
 #endif
-#endif /*CONFIG_SSL_IMPL_WOLFSSL */
+
+static const char *mg_default_session_id_context = "mongoose";
 
 struct mg_ssl_if_ctx {
   SSL *ssl;
@@ -4564,6 +4537,9 @@  enum mg_ssl_if_result mg_ssl_if_conn_init(
   SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2);
   SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv3);
   SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1);
+  SSL_CTX_set_session_id_context(ctx->ssl_ctx,
+                                 (void *) mg_default_session_id_context,
+                                 strlen(mg_default_session_id_context));
 #ifdef MG_SSL_OPENSSL_NO_COMPRESSION
   SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_COMPRESSION);
 #endif
@@ -4621,6 +4597,17 @@  static enum mg_ssl_if_result mg_ssl_if_ssl_err(struct mg_connection *nc,
                                                int res) {
   struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data;
   int err = SSL_get_error(ctx->ssl, res);
+  /*
+   * We've just fetched the last error from the queue.
+   * Now we need to clear the error queue. If we do not, then the following
+   * can happen (actually reported):
+   *  - A new connection is accept()-ed with cert error (e.g. self-signed cert)
+   *  - Since all accept()-ed connections share listener's context,
+   *  - *ALL* SSL accepted connection report read error on the next poll cycle.
+   *    Thus a single errored connection can close all the rest, unrelated ones.
+   * Clearing the error keeps the shared SSL_CTX in an OK state.
+   */
+  ERR_clear_error();
   if (err == SSL_ERROR_WANT_READ) return MG_SSL_WANT_READ;
   if (err == SSL_ERROR_WANT_WRITE) return MG_SSL_WANT_WRITE;
   DBG(("%p %p SSL error: %d %d", nc, ctx->ssl_ctx, res, err));
@@ -4785,7 +4772,7 @@  static enum mg_ssl_if_result mg_use_cert(SSL_CTX *ctx, const char *cert,
       SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE);
       DH_free(dh);
     }
-#if OPENSSL_VERSION_NUMBER > 0x10002000L && !defined(LIBWOLFSSL_VERSION_STRING)
+#if OPENSSL_VERSION_NUMBER > 0x10002000L
     SSL_CTX_set_ecdh_auto(ctx, 1);
 #endif
 #endif
@@ -4887,10 +4874,6 @@  const char *mg_set_ssl(struct mg_connection *nc, const char *cert,
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_ssl_if_mbedtls.c"
 #endif
-/*
- * Copyright (c) 2014-2016 Cesanta Software Limited
- * All rights reserved
- */
 
 #if MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_MBEDTLS
 
@@ -4933,7 +4916,11 @@  struct mg_ssl_if_ctx {
   mbedtls_ssl_context *ssl;
   mbedtls_x509_crt *cert;
   mbedtls_pk_context *key;
+#ifdef MBEDTLS_X509_CA_CHAIN_ON_DISK
+  char *ca_chain_file;
+#else
   mbedtls_x509_crt *ca_cert;
+#endif
   struct mbuf cipher_suites;
   size_t saved_len;
 };
@@ -4941,7 +4928,7 @@  struct mg_ssl_if_ctx {
 /* Must be provided by the platform. ctx is struct mg_connection. */
 extern int mg_ssl_if_mbed_random(void *ctx, unsigned char *buf, size_t len);
 
-void mg_ssl_if_init() {
+void mg_ssl_if_init(void) {
   LOG(LL_INFO, ("%s", MBEDTLS_VERSION_STRING_FULL));
 }
 
@@ -5109,18 +5096,17 @@  static void mg_ssl_if_mbed_free_certs_and_keys(struct mg_ssl_if_ctx *ctx) {
     MG_FREE(ctx->key);
     ctx->key = NULL;
   }
+#ifdef MBEDTLS_X509_CA_CHAIN_ON_DISK
+  MG_FREE(ctx->ca_chain_file);
+  ctx->ca_chain_file = NULL;
+#else
   if (ctx->ca_cert != NULL) {
     mbedtls_ssl_conf_ca_chain(ctx->conf, NULL, NULL);
-#ifdef MBEDTLS_X509_CA_CHAIN_ON_DISK
-    if (ctx->conf->ca_chain_file != NULL) {
-      MG_FREE((void *) ctx->conf->ca_chain_file);
-      ctx->conf->ca_chain_file = NULL;
-    }
-#endif
     mbedtls_x509_crt_free(ctx->ca_cert);
     MG_FREE(ctx->ca_cert);
     ctx->ca_cert = NULL;
   }
+#endif
 }
 
 enum mg_ssl_if_result mg_ssl_if_handshake(struct mg_connection *nc) {
@@ -5201,11 +5187,11 @@  void mg_ssl_if_conn_free(struct mg_connection *nc) {
     mbedtls_ssl_free(ctx->ssl);
     MG_FREE(ctx->ssl);
   }
-  mg_ssl_if_mbed_free_certs_and_keys(ctx);
   if (ctx->conf != NULL) {
     mbedtls_ssl_config_free(ctx->conf);
     MG_FREE(ctx->conf);
   }
+  mg_ssl_if_mbed_free_certs_and_keys(ctx);
   mbuf_free(&ctx->cipher_suites);
   memset(ctx, 0, sizeof(*ctx));
   MG_FREE(ctx);
@@ -5217,12 +5203,15 @@  static enum mg_ssl_if_result mg_use_ca_cert(struct mg_ssl_if_ctx *ctx,
     mbedtls_ssl_conf_authmode(ctx->conf, MBEDTLS_SSL_VERIFY_NONE);
     return MG_SSL_OK;
   }
-  ctx->ca_cert = (mbedtls_x509_crt *) MG_CALLOC(1, sizeof(*ctx->ca_cert));
-  mbedtls_x509_crt_init(ctx->ca_cert);
 #ifdef MBEDTLS_X509_CA_CHAIN_ON_DISK
-  ca_cert = strdup(ca_cert);
-  mbedtls_ssl_conf_ca_chain_file(ctx->conf, ca_cert, NULL);
+  ctx->ca_chain_file = strdup(ca_cert);
+  if (ctx->ca_chain_file == NULL) return MG_SSL_ERROR;
+  if (mbedtls_ssl_conf_ca_chain_file(ctx->conf, ctx->ca_chain_file, NULL) != 0) {
+    return MG_SSL_ERROR;
+  }
 #else
+  ctx->ca_cert = (mbedtls_x509_crt *) MG_CALLOC(1, sizeof(*ctx->ca_cert));
+  mbedtls_x509_crt_init(ctx->ca_cert);
   if (mbedtls_x509_crt_parse_file(ctx->ca_cert, ca_cert) != 0) {
     return MG_SSL_ERROR;
   }
@@ -5399,10 +5388,6 @@  int mg_ssl_if_mbed_random(void *ctx, unsigned char *buf, size_t len) {
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_uri.c"
 #endif
-/*
- * Copyright (c) 2014 Cesanta Software Limited
- * All rights reserved
- */
 
 /* Amalgamated: #include "mg_internal.h" */
 /* Amalgamated: #include "mg_uri.h" */
@@ -5663,10 +5648,6 @@  out:
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_http.c"
 #endif
-/*
- * Copyright (c) 2014 Cesanta Software Limited
- * All rights reserved
- */
 
 #if MG_ENABLE_HTTP
 
@@ -5901,7 +5882,7 @@  static void mg_http_free_proto_data_endpoints(struct mg_http_endpoint **ep) {
     current = tmp;
   }
 
-  ep = NULL;
+  *ep = NULL;
 }
 
 static void mg_http_free_reverse_proxy_data(struct mg_reverse_proxy_data *rpd) {
@@ -5993,37 +5974,53 @@  static const struct {
     MIME_ENTRY("asf", "video/x-ms-asf"),
     MIME_ENTRY("avi", "video/x-msvideo"),
     MIME_ENTRY("bmp", "image/bmp"),
-    {NULL, 0, NULL}};
+    {NULL, 0, NULL},
+};
 
-static struct mg_str mg_get_mime_type(const char *path, const char *dflt,
-                                      const struct mg_serve_http_opts *opts) {
-  const char *ext, *overrides;
-  size_t i, path_len;
-  struct mg_str r, k, v;
+static struct mg_str mg_get_mime_types_entry(struct mg_str path) {
+  size_t i;
+  for (i = 0; mg_static_builtin_mime_types[i].extension != NULL; i++) {
+    if (path.len < mg_static_builtin_mime_types[i].ext_len + 1) continue;
+    struct mg_str ext = MG_MK_STR_N(mg_static_builtin_mime_types[i].extension,
+                                    mg_static_builtin_mime_types[i].ext_len);
+    struct mg_str pext = MG_MK_STR_N(path.p + (path.len - ext.len), ext.len);
+    if (pext.p[-1] == '.' && mg_strcasecmp(ext, pext) == 0) {
+      return mg_mk_str(mg_static_builtin_mime_types[i].mime_type);
+    }
+  }
+  return mg_mk_str(NULL);
+}
 
-  path_len = strlen(path);
+MG_INTERNAL int mg_get_mime_type_encoding(
+    struct mg_str path, struct mg_str *type, struct mg_str *encoding,
+    const struct mg_serve_http_opts *opts) {
+  const char *ext, *overrides;
+  struct mg_str k, v;
 
   overrides = opts->custom_mime_types;
   while ((overrides = mg_next_comma_list_entry(overrides, &k, &v)) != NULL) {
-    ext = path + (path_len - k.len);
-    if (path_len > k.len && mg_vcasecmp(&k, ext) == 0) {
-      return v;
+    ext = path.p + (path.len - k.len);
+    if (path.len > k.len && mg_vcasecmp(&k, ext) == 0) {
+      *type = v;
+      return 1;
     }
   }
 
-  for (i = 0; mg_static_builtin_mime_types[i].extension != NULL; i++) {
-    ext = path + (path_len - mg_static_builtin_mime_types[i].ext_len);
-    if (path_len > mg_static_builtin_mime_types[i].ext_len && ext[-1] == '.' &&
-        mg_casecmp(ext, mg_static_builtin_mime_types[i].extension) == 0) {
-      r.p = mg_static_builtin_mime_types[i].mime_type;
-      r.len = strlen(r.p);
-      return r;
+  *type = mg_get_mime_types_entry(path);
+
+  /* Check for .html.gz, .js.gz, etc. */
+  if (mg_vcmp(type, "application/x-gunzip") == 0) {
+    struct mg_str path2 = mg_mk_str_n(path.p, path.len - 3);
+    struct mg_str type2 = mg_get_mime_types_entry(path2);
+    LOG(LL_ERROR, ("'%.*s' '%.*s' '%.*s'", (int) path.len, path.p,
+                   (int) path2.len, path2.p, (int) type2.len, type2.p));
+    if (type2.len > 0) {
+      *type = type2;
+      *encoding = mg_mk_str("gzip");
     }
   }
 
-  r.p = dflt;
-  r.len = strlen(r.p);
-  return r;
+  return (type->len > 0);
 }
 #endif
 
@@ -6201,12 +6198,12 @@  static void mg_http_transfer_file_data(struct mg_connection *nc) {
   } else if (pd->file.type == DATA_PUT) {
     struct mbuf *io = &nc->recv_mbuf;
     size_t to_write = left <= 0 ? 0 : left < io->len ? (size_t) left : io->len;
-    size_t n1 = mg_fwrite(io->buf, 1, to_write, pd->file.fp);
-    if (n1 > 0) {
-      mbuf_remove(io, n1);
-      pd->file.sent += n1;
+    size_t n = mg_fwrite(io->buf, 1, to_write, pd->file.fp);
+    if (n > 0) {
+      mbuf_remove(io, n);
+      pd->file.sent += n;
     }
-    if (n1 == 0 || pd->file.sent >= pd->file.cl) {
+    if (n == 0 || pd->file.sent >= pd->file.cl) {
       if (!pd->file.keepalive) nc->flags |= MG_F_SEND_AND_CLOSE;
       mg_http_free_proto_data_file(&pd->file);
     }
@@ -6323,7 +6320,7 @@  MG_INTERNAL size_t mg_handle_chunked(struct mg_connection *nc,
   return body_len;
 }
 
-static struct mg_http_endpoint *mg_http_get_endpoint_handler(struct mg_connection *nc,
+struct mg_http_endpoint *mg_http_get_endpoint_handler(struct mg_connection *nc,
                                                       struct mg_str *uri_path) {
   struct mg_http_proto_data *pd;
   struct mg_http_endpoint *ret = NULL;
@@ -6393,7 +6390,7 @@  static void mg_http_handler2(struct mg_connection *nc, int ev,
                              void *ev_data MG_UD_ARG(void *user_data),
                              struct http_message *hm) {
 #else  /* !__XTENSA__ */
-static void mg_http_handler(struct mg_connection *nc, int ev,
+void mg_http_handler(struct mg_connection *nc, int ev,
                      void *ev_data MG_UD_ARG(void *user_data)) {
   struct http_message shm, *hm = &shm;
 #endif /* __XTENSA__ */
@@ -6832,7 +6829,7 @@  static int mg_http_multipart_continue_wait_for_chunk(struct mg_connection *c) {
       mbuf_remove(io, consumed);
     }
     return 0;
-  } else {
+  } else if (boundary != NULL) {
     size_t data_len = ((size_t)(boundary - io->buf) - 4);
     size_t consumed = mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_DATA,
                                                      io->buf, data_len);
@@ -6844,6 +6841,8 @@  static int mg_http_multipart_continue_wait_for_chunk(struct mg_connection *c) {
     } else {
       return 0;
     }
+  } else {
+    return 0;
   }
 }
 
@@ -6898,7 +6897,7 @@  void mg_set_protocol_http_websocket(struct mg_connection *nc) {
   nc->proto_handler = mg_http_handler;
 }
 
-static const char *mg_status_message(int status_code) {
+const char *mg_status_message(int status_code) {
   switch (status_code) {
     case 206:
       return "Partial Content";
@@ -7029,7 +7028,7 @@  static const char *mg_status_message(int status_code) {
   }
 }
 
-static void mg_send_response_line_s(struct mg_connection *nc, int status_code,
+void mg_send_response_line_s(struct mg_connection *nc, int status_code,
                              const struct mg_str extra_headers) {
   mg_printf(nc, "HTTP/1.1 %d %s\r\n", status_code,
             mg_status_message(status_code));
@@ -7121,12 +7120,15 @@  static int mg_http_parse_range_header(const struct mg_str *header, int64_t *a,
   return result;
 }
 
-void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm,
-                        const char *path, const struct mg_str mime_type,
-                        const struct mg_str extra_headers) {
+void mg_http_serve_file_internal(struct mg_connection *nc,
+                                 struct http_message *hm, const char *path,
+                                 struct mg_str mime_type,
+                                 struct mg_str encoding,
+                                 struct mg_str extra_headers) {
   struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
   cs_stat_t st;
-  LOG(LL_DEBUG, ("%p [%s] %.*s", nc, path, (int) mime_type.len, mime_type.p));
+  LOG(LL_DEBUG, ("%p [%s] %.*s %.*s", nc, path, (int) mime_type.len,
+                 mime_type.p, (int) encoding.len, encoding.p));
   if (mg_stat(path, &st) != 0 || (pd->file.fp = mg_fopen(path, "rb")) == NULL) {
     int code, err = mg_get_errno();
     switch (err) {
@@ -7165,8 +7167,9 @@  void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm,
       } else {
         status_code = 206;
         cl = r2 - r1 + 1;
-        snprintf(range, sizeof(range), "Content-Range: bytes %" INT64_FMT
-                                       "-%" INT64_FMT "/%" INT64_FMT "\r\n",
+        snprintf(range, sizeof(range),
+                 "Content-Range: bytes %" INT64_FMT "-%" INT64_FMT
+                 "/%" INT64_FMT "\r\n",
                  r1, r1 + cl - 1, (int64_t) st.st_size);
 #if _FILE_OFFSET_BITS == 64 || _POSIX_C_SOURCE >= 200112L || \
     _XOPEN_SOURCE >= 600
@@ -7191,13 +7194,6 @@  void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm,
     mg_http_construct_etag(etag, sizeof(etag), &st);
     mg_gmt_time_string(current_time, sizeof(current_time), &t);
     mg_gmt_time_string(last_modified, sizeof(last_modified), &st.st_mtime);
-    /*
-     * Content length casted to size_t because:
-     * 1) that's the maximum buffer size anyway
-     * 2) ESP8266 RTOS SDK newlib vprintf cannot contain a 64bit arg at non-last
-     *    position
-     * TODO(mkm): fix ESP8266 RTOS SDK
-     */
     mg_send_response_line_s(nc, status_code, extra_headers);
     mg_printf(nc,
               "Date: %s\r\n"
@@ -7207,17 +7203,29 @@  void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm,
               "Connection: %s\r\n"
               "Content-Length: %" SIZE_T_FMT
               "\r\n"
-              "%sEtag: %s\r\n\r\n",
+              "%s"
+              "Etag: %s\r\n",
               current_time, last_modified, (int) mime_type.len, mime_type.p,
               (pd->file.keepalive ? "keep-alive" : "close"), (size_t) cl, range,
               etag);
-
+    if (encoding.len > 0) {
+      mg_printf(nc, "Content-Encoding: %.*s\r\n", (int) encoding.len,
+                encoding.p);
+    }
+    mg_send(nc, "\r\n", 2);
     pd->file.cl = cl;
     pd->file.type = DATA_FILE;
     mg_http_transfer_file_data(nc);
   }
 }
 
+void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm,
+                        const char *path, const struct mg_str mime_type,
+                        const struct mg_str extra_headers) {
+  mg_http_serve_file_internal(nc, hm, path, mime_type, mg_mk_str(NULL),
+                              extra_headers);
+}
+
 static void mg_http_serve_file2(struct mg_connection *nc, const char *path,
                                 struct http_message *hm,
                                 struct mg_serve_http_opts *opts) {
@@ -7227,8 +7235,12 @@  static void mg_http_serve_file2(struct mg_connection *nc, const char *path,
     return;
   }
 #endif
-  mg_http_serve_file(nc, hm, path, mg_get_mime_type(path, "text/plain", opts),
-                     mg_mk_str(opts->extra_headers));
+  struct mg_str type = MG_NULL_STR, encoding = MG_NULL_STR;
+  if (!mg_get_mime_type_encoding(mg_mk_str(path), &type, &encoding, opts)) {
+    type = mg_mk_str("text/plain");
+  }
+  mg_http_serve_file_internal(nc, hm, path, type, encoding,
+                              mg_mk_str(opts->extra_headers));
 }
 
 #endif
@@ -7496,7 +7508,7 @@  extern void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[],
                           const size_t *msg_lens, uint8_t *digest);
 #endif
 
-static void cs_md5(char buf[33], ...) {
+void cs_md5(char buf[33], ...) {
   unsigned char hash[16];
   const uint8_t *msgs[20], *p;
   size_t msg_lens[20];
@@ -7504,7 +7516,7 @@  static void cs_md5(char buf[33], ...) {
   va_list ap;
 
   va_start(ap, buf);
-  while ((p = va_arg(ap, const unsigned char *) ) != NULL) {
+  while ((p = va_arg(ap, const unsigned char *)) != NULL) {
     msgs[num_msgs] = p;
     msg_lens[num_msgs] = va_arg(ap, size_t);
     num_msgs++;
@@ -7736,7 +7748,7 @@  static void mg_print_dir_entry(struct mg_connection *nc, const char *file_name,
   href = mg_url_encode(mg_mk_str(file_name));
   mg_printf_http_chunk(nc,
                        "<tr><td><a href=\"%s%s\">%s%s</a></td>"
-                       "<td>%s</td><td name=%" INT64_FMT ">%s</td></tr>\n",
+                       "<td>%s</td><td name=\"%" INT64_FMT "\">%s</td></tr>",
                        href.p, slash, path, slash, mod, is_dir ? -1 : fsize,
                        size);
   free((void *) href.p);
@@ -7802,23 +7814,24 @@  static void mg_send_directory_listing(struct mg_connection *nc, const char *dir,
 
   mg_printf_http_chunk(
       nc,
-      "<html><head><title>Index of %.*s</title>%s%s"
+      "<!DOCTYPE html><html><head><title>Index of %.*s</title>%s%s"
       "<style>th,td {text-align: left; padding-right: 1em; "
-      "font-family: monospace; }</style></head>\n"
-      "<body><h1>Index of %.*s</h1>\n<table cellpadding=0><thead>"
-      "<tr><th><a href=# rel=0>Name</a></th><th>"
-      "<a href=# rel=1>Modified</a</th>"
-      "<th><a href=# rel=2>Size</a></th></tr>"
-      "<tr><td colspan=3><hr></td></tr>\n"
-      "</thead>\n"
-      "<tbody id=tb>",
+      "font-family: monospace; }</style></head>"
+      "<body><h1>Index of %.*s</h1><table cellpadding=\"0\"><thead>"
+      "<tr><th><a href=\"#\" rel=\"0\">Name</a></th><th>"
+      "<a href=\"#\" rel=\"1\">Modified</a></th>"
+      "<th><a href=\"#\" rel=\"2\">Size</a></th></tr>"
+      "<tr><td colspan=\"3\"><hr></td></tr>"
+      "</thead>"
+      "<tbody id=\"tb\">",
       (int) hm->uri.len, hm->uri.p, sort_js_code, sort_js_code2,
       (int) hm->uri.len, hm->uri.p);
   mg_scan_directory(nc, dir, opts, mg_print_dir_entry);
   mg_printf_http_chunk(nc,
-                       "</tbody><tr><td colspan=3><hr></td></tr>\n"
-                       "</table>\n"
-                       "<address>%s</address>\n"
+                       "</tbody>"
+                       "<tfoot><tr><td colspan=\"3\"><hr></td></tr></tfoot>"
+                       "</table>"
+                       "<address>%s</address>"
                        "</body></html>",
                        mg_version_header);
   mg_send_http_chunk(nc, "", 0);
@@ -8135,13 +8148,13 @@  MG_INTERNAL int mg_uri_to_local_path(struct http_message *hm,
           *p++ = DIRSEP;
           /* No NULs and DIRSEPs in the component (percent-encoded). */
           for (i = 0; i < component.len; i++, p++) {
-            if (*p == '\0' || *p == DIRSEP
+            if (*p == '\0' ||
+                *p == DIRSEP
 #ifdef _WIN32
                 /* On Windows, "/" is also accepted, so check for that too. */
-                ||
-                *p == '/'
+                || *p == '/'
 #endif
-                ) {
+            ) {
               ok = 0;
               break;
             }
@@ -8354,7 +8367,9 @@  MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
     mg_http_send_error(nc, 501, NULL);
 #endif
   } else if (mg_is_not_modified(hm, &st)) {
-    mg_http_send_error(nc, 304, "Not Modified");
+    /* Note: not using mg_http_send_error in order to keep connection alive */
+    /* Note: passing extra headers allow users to control session cookies */
+    mg_send_head(nc, 304, 0, opts->extra_headers);
   } else {
     mg_http_serve_file2(nc, index_file ? index_file : path, hm, opts);
   }
@@ -8487,7 +8502,7 @@  void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data,
 #ifdef SPIFFS_ERR_FULL
             || mg_get_errno() == SPIFFS_ERR_FULL
 #endif
-            ) {
+        ) {
           mg_printf(nc,
                     "HTTP/1.1 413 Payload Too Large\r\n"
                     "Content-Type: text/plain\r\n"
@@ -8646,8 +8661,9 @@  struct mg_connection *mg_connect_http_opt(
   if (path.len == 0) path = mg_mk_str("/");
   if (host.len == 0) host = mg_mk_str("");
 
-  mg_printf(nc, "%s %.*s HTTP/1.1\r\nHost: %.*s\r\nContent-Length: %" SIZE_T_FMT
-                "\r\n%.*s%s\r\n%s",
+  mg_printf(nc,
+            "%s %.*s HTTP/1.1\r\nHost: %.*s\r\nContent-Length: %" SIZE_T_FMT
+            "\r\n%.*s%s\r\n%s",
             (post_data[0] == '\0' ? "GET" : "POST"), (int) path.len, path.p,
             (int) (path.p - host.p), host.p, strlen(post_data), (int) auth.len,
             (auth.buf == NULL ? "" : auth.buf), extra_headers, post_data);
@@ -8756,7 +8772,7 @@  static void mg_http_call_endpoint_handler(struct mg_connection *nc, int ev,
 #if MG_ENABLE_HTTP_STREAMING_MULTIPART
       || ev == MG_EV_HTTP_MULTIPART_REQUEST
 #endif
-      ) {
+  ) {
     struct mg_http_endpoint *ep =
         mg_http_get_endpoint_handler(nc->listener, &hm->uri);
     if (ep != NULL) {
@@ -8792,10 +8808,6 @@  void mg_register_http_endpoint(struct mg_connection *nc, const char *uri_path,
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_http_cgi.c"
 #endif
-/*
- * Copyright (c) 2014-2016 Cesanta Software Limited
- * All rights reserved
- */
 
 #ifndef _WIN32
 #include <signal.h>
@@ -9308,10 +9320,6 @@  MG_INTERNAL void mg_http_free_proto_data_cgi(struct mg_http_proto_data_cgi *d) {
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_http_ssi.c"
 #endif
-/*
- * Copyright (c) 2014-2016 Cesanta Software Limited
- * All rights reserved
- */
 
 #if MG_ENABLE_HTTP && MG_ENABLE_HTTP_SSI && MG_ENABLE_FILESYSTEM
 
@@ -9480,7 +9488,7 @@  MG_INTERNAL void mg_handle_ssi_request(struct mg_connection *nc,
                                        const char *path,
                                        const struct mg_serve_http_opts *opts) {
   FILE *fp;
-  struct mg_str mime_type;
+  struct mg_str mime_type = MG_NULL_STR, encoding = MG_NULL_STR;
   DBG(("%p %s", nc, path));
 
   if ((fp = mg_fopen(path, "rb")) == NULL) {
@@ -9488,12 +9496,20 @@  MG_INTERNAL void mg_handle_ssi_request(struct mg_connection *nc,
   } else {
     mg_set_close_on_exec((sock_t) fileno(fp));
 
-    mime_type = mg_get_mime_type(path, "text/plain", opts);
+    if (!mg_get_mime_type_encoding(mg_mk_str(path), &mime_type, &encoding,
+                                   opts)) {
+      mime_type = mg_mk_str("text/plain");
+    }
     mg_send_response_line(nc, 200, opts->extra_headers);
     mg_printf(nc,
               "Content-Type: %.*s\r\n"
-              "Connection: close\r\n\r\n",
+              "Connection: close\r\n",
               (int) mime_type.len, mime_type.p);
+    if (encoding.len > 0) {
+      mg_printf(nc, "Content-Encoding: %.*s\r\n", (int) encoding.len,
+                encoding.p);
+    }
+    mg_send(nc, "\r\n", 2);
     mg_send_ssi_file(nc, hm, path, fp, 0, opts);
     fclose(fp);
     nc->flags |= MG_F_SEND_AND_CLOSE;
@@ -9504,10 +9520,6 @@  MG_INTERNAL void mg_handle_ssi_request(struct mg_connection *nc,
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_http_webdav.c"
 #endif
-/*
- * Copyright (c) 2014-2016 Cesanta Software Limited
- * All rights reserved
- */
 
 #if MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBDAV
 
@@ -9776,10 +9788,6 @@  MG_INTERNAL void mg_handle_put(struct mg_connection *nc, const char *path,
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_http_websocket.c"
 #endif
-/*
- * Copyright (c) 2014 Cesanta Software Limited
- * All rights reserved
- */
 
 #if MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBSOCKET
 
@@ -10298,10 +10306,6 @@  struct mg_connection *mg_connect_ws(
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_util.c"
 #endif
-/*
- * Copyright (c) 2014 Cesanta Software Limited
- * All rights reserved
- */
 
 /* Amalgamated: #include "common/cs_base64.h" */
 /* Amalgamated: #include "mg_internal.h" */
@@ -10645,10 +10649,6 @@  struct mg_str mg_url_encode(const struct mg_str src) {
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_mqtt.c"
 #endif
-/*
- * Copyright (c) 2014 Cesanta Software Limited
- * All rights reserved
- */
 
 #if MG_ENABLE_MQTT
 
@@ -10657,6 +10657,8 @@  struct mg_str mg_url_encode(const struct mg_str src) {
 /* Amalgamated: #include "mg_internal.h" */
 /* Amalgamated: #include "mg_mqtt.h" */
 
+#define MG_F_MQTT_PING_PENDING MG_F_PROTO_1
+
 static uint16_t getu16(const char *p) {
   const uint8_t *up = (const uint8_t *) p;
   return (up[0] << 8) + up[1];
@@ -10670,7 +10672,7 @@  static const char *scanto(const char *p, struct mg_str *s) {
 
 MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) {
   uint8_t header;
-  size_t len = 0, len_len = 0;
+  uint32_t len, len_len; /* must be 32-bit, see #1055 */
   const char *p, *end, *eop = &io->buf[io->len];
   unsigned char lc = 0;
   int cmd;
@@ -10687,7 +10689,7 @@  MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) {
     len += (lc & 0x7f) << 7 * len_len;
     len_len++;
     if (!(lc & 0x80)) break;
-    if (len_len > 4) return MG_MQTT_ERROR_MALFORMED_MSG;
+    if (len_len > sizeof(len)) return MG_MQTT_ERROR_MALFORMED_MSG;
   }
 
   end = p + len;
@@ -10819,6 +10821,10 @@  static void mqtt_handler(struct mg_connection *nc, int ev,
           }
           break;
         }
+        if (mm.cmd == MG_MQTT_CMD_PINGRESP) {
+          LOG(LL_DEBUG, ("Recv PINGRESP"));
+          nc->flags &= ~MG_F_MQTT_PING_PENDING;
+        }
 
         nc->handler(nc, MG_MQTT_EVENT_BASE + mm.cmd, &mm MG_UD_ARG(user_data));
         mbuf_remove(io, len);
@@ -10829,10 +10835,26 @@  static void mqtt_handler(struct mg_connection *nc, int ev,
       struct mg_mqtt_proto_data *pd =
           (struct mg_mqtt_proto_data *) nc->proto_data;
       double now = mg_time();
-      if (pd->keep_alive > 0 && pd->last_control_time > 0 &&
-          (now - pd->last_control_time) > pd->keep_alive) {
-        LOG(LL_DEBUG, ("Send PINGREQ"));
-        mg_mqtt_ping(nc);
+      if (pd->keep_alive > 0 && pd->last_control_time > 0) {
+        double diff = (now - pd->last_control_time);
+        if (diff > pd->keep_alive) {
+          if (diff < 1500000000) {
+            if (!(nc->flags & MG_F_MQTT_PING_PENDING)) {
+              LOG(LL_DEBUG, ("Send PINGREQ"));
+              nc->flags |= MG_F_MQTT_PING_PENDING;
+              mg_mqtt_ping(nc);
+            } else {
+              LOG(LL_DEBUG, ("Ping timeout"));
+              nc->flags |= MG_F_CLOSE_IMMEDIATELY;
+            }
+          } else {
+            /* Wall time has just been set. Avoid immediate ping,
+             * more likely than not it is not needed. The standard allows for
+             * 1.5X interval for ping requests, so even if were just about to
+             * send one, we should be ok waiting 0.4X more. */
+            pd->last_control_time = now - pd->keep_alive * 0.6;
+          }
+        }
       }
       break;
     }
@@ -11159,10 +11181,6 @@  void mg_mqtt_disconnect(struct mg_connection *nc) {
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_mqtt_server.c"
 #endif
-/*
- * Copyright (c) 2014 Cesanta Software Limited
- * All rights reserved
- */
 
 /* Amalgamated: #include "mg_internal.h" */
 /* Amalgamated: #include "mg_mqtt_server.h" */
@@ -11356,10 +11374,6 @@  struct mg_mqtt_session *mg_mqtt_next(struct mg_mqtt_broker *brk,
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_dns.c"
 #endif
-/*
- * Copyright (c) 2014 Cesanta Software Limited
- * All rights reserved
- */
 
 #if MG_ENABLE_DNS
 
@@ -11443,35 +11457,39 @@  int mg_dns_copy_questions(struct mbuf *io, struct mg_dns_message *msg) {
   return mbuf_append(io, begin, end - begin);
 }
 
-int mg_dns_encode_name(struct mbuf *io, const char *name, size_t len) {
+int mg_dns_encode_name_s(struct mbuf *io, struct mg_str name) {
   const char *s;
   unsigned char n;
   size_t pos = io->len;
 
   do {
-    if ((s = strchr(name, '.')) == NULL) {
-      s = name + len;
+    if ((s = mg_strchr(name, '.')) == NULL) {
+      s = name.p + name.len;
     }
 
-    if (s - name > 127) {
+    if (s - name.p > 127) {
       return -1; /* TODO(mkm) cover */
     }
-    n = s - name;           /* chunk length */
+    n = s - name.p;         /* chunk length */
     mbuf_append(io, &n, 1); /* send length */
-    mbuf_append(io, name, n);
+    mbuf_append(io, name.p, n);
 
-    if (*s == '.') {
+    if (n < name.len && *s == '.') {
       n++;
     }
 
-    name += n;
-    len -= n;
-  } while (*s != '\0');
+    name.p += n;
+    name.len -= n;
+  } while (name.len > 0);
   mbuf_append(io, "\0", 1); /* Mark end of host name */
 
   return io->len - pos;
 }
 
+int mg_dns_encode_name(struct mbuf *io, const char *name, size_t len) {
+  return mg_dns_encode_name_s(io, mg_mk_str_n(name, len));
+}
+
 int mg_dns_encode_record(struct mbuf *io, struct mg_dns_resource_record *rr,
                          const char *name, size_t nlen, const void *rdata,
                          size_t rlen) {
@@ -11736,10 +11754,6 @@  void mg_set_protocol_dns(struct mg_connection *nc) {
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_dns_server.c"
 #endif
-/*
- * Copyright (c) 2014 Cesanta Software Limited
- * All rights reserved
- */
 
 #if MG_ENABLE_DNS_SERVER
 
@@ -11810,10 +11824,6 @@  int mg_dns_reply_record(struct mg_dns_reply *reply,
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_resolv.c"
 #endif
-/*
- * Copyright (c) 2014 Cesanta Software Limited
- * All rights reserved
- */
 
 #if MG_ENABLE_ASYNC_RESOLVER
 
@@ -12105,22 +12115,6 @@  void mg_set_nameserver(struct mg_mgr *mgr, const char *nameserver) {
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_coap.c"
 #endif
-/*
- * Copyright (c) 2015 Cesanta Software Limited
- * All rights reserved
- * This software is dual-licensed: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation. For the terms of this
- * license, see <http://www.gnu.org/licenses/>.
- *
- * You are free to use this software under the terms of the GNU General
- * Public License, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * Alternatively, you can license this software under a commercial
- * license, as set out in <https://www.cesanta.com/license>.
- */
 
 /* Amalgamated: #include "mg_internal.h" */
 /* Amalgamated: #include "mg_coap.h" */
@@ -12705,10 +12699,6 @@  int mg_set_protocol_coap(struct mg_connection *nc) {
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_sntp.c"
 #endif
-/*
- * Copyright (c) 2016 Cesanta Software Limited
- * All rights reserved
- */
 
 /* Amalgamated: #include "mg_internal.h" */
 /* Amalgamated: #include "mg_sntp.h" */
@@ -12996,10 +12986,6 @@  struct mg_connection *mg_sntp_get_time(struct mg_mgr *mgr,
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_socks.c"
 #endif
-/*
- * Copyright (c) 2017 Cesanta Software Limited
- * All rights reserved
- */
 
 #if MG_ENABLE_SOCKS
 
@@ -14588,7 +14574,7 @@  struct mg_ssl_if_ctx {
   char *ssl_server_name;
 };
 
-void mg_ssl_if_init(void) {
+void mg_ssl_if_init() {
 }
 
 enum mg_ssl_if_result mg_ssl_if_conn_init(
diff --git a/mongoose/mongoose.h b/mongoose/mongoose.h
index e691172..3d5d2cb 100644
--- a/mongoose/mongoose.h
+++ b/mongoose/mongoose.h
@@ -1,9 +1,6 @@ 
-#ifdef MG_MODULE_LINES
-#line 1 "mongoose/src/mg_common.h"
-#endif
 /*
  * Copyright (c) 2004-2013 Sergey Lyubka
- * Copyright (c) 2013-2015 Cesanta Software Limited
+ * Copyright (c) 2013-2020 Cesanta Software Limited
  * All rights reserved
  *
  * This software is dual-licensed: you can redistribute it and/or modify
@@ -18,14 +15,15 @@ 
  *
  * Alternatively, you can license this software under a commercial
  * license, as set out in <https://www.cesanta.com/license>.
- *
- * SPDX-License-Identifier: GPL-2.0
  */
 
+#ifdef MG_MODULE_LINES
+#line 1 "mongoose/src/mg_common.h"
+#endif
 #ifndef CS_MONGOOSE_SRC_COMMON_H_
 #define CS_MONGOOSE_SRC_COMMON_H_
 
-#define MG_VERSION "6.16"
+#define MG_VERSION "6.18"
 
 /* Local tweaks, applied before any of Mongoose's own headers. */
 #ifdef MG_LOCALS
@@ -230,7 +228,7 @@ 
 #include <windows.h>
 #include <process.h>
 
-#if _MSC_VER < 1700
+#if defined(_MSC_VER) && (_MSC_VER < 1700)
 typedef int bool;
 #else
 #include <stdbool.h>
@@ -512,22 +510,6 @@  typedef struct stat cs_stat_t;
 #ifdef MG_MODULE_LINES
 #line 1 "common/platforms/platform_esp32.h"
 #endif
-/*
- * Copyright (c) 2014-2018 Cesanta Software Limited
- * All rights reserved
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
 #ifndef CS_COMMON_PLATFORMS_PLATFORM_ESP32_H_
 #define CS_COMMON_PLATFORMS_PLATFORM_ESP32_H_
@@ -569,22 +551,6 @@  typedef struct stat cs_stat_t;
 #ifdef MG_MODULE_LINES
 #line 1 "common/platforms/platform_esp8266.h"
 #endif
-/*
- * Copyright (c) 2014-2018 Cesanta Software Limited
- * All rights reserved
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
 #ifndef CS_COMMON_PLATFORMS_PLATFORM_ESP8266_H_
 #define CS_COMMON_PLATFORMS_PLATFORM_ESP8266_H_
@@ -642,22 +608,6 @@  typedef struct stat cs_stat_t;
 #ifdef MG_MODULE_LINES
 #line 1 "common/platforms/platform_cc3100.h"
 #endif
-/*
- * Copyright (c) 2014-2018 Cesanta Software Limited
- * All rights reserved
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
 #ifndef CS_COMMON_PLATFORMS_PLATFORM_CC3100_H_
 #define CS_COMMON_PLATFORMS_PLATFORM_CC3100_H_
@@ -703,22 +653,6 @@  int inet_pton(int af, const char *src, void *dst);
 #ifdef MG_MODULE_LINES
 #line 1 "common/platforms/platform_cc3200.h"
 #endif
-/*
- * Copyright (c) 2014-2018 Cesanta Software Limited
- * All rights reserved
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
 #ifndef CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_
 #define CS_COMMON_PLATFORMS_PLATFORM_CC3200_H_
@@ -840,22 +774,6 @@  int stat(const char *pathname, struct stat *st);
 #ifdef MG_MODULE_LINES
 #line 1 "common/platforms/platform_cc3220.h"
 #endif
-/*
- * Copyright (c) 2014-2018 Cesanta Software Limited
- * All rights reserved
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
 #ifndef CS_COMMON_PLATFORMS_PLATFORM_CC3220_H_
 #define CS_COMMON_PLATFORMS_PLATFORM_CC3220_H_
@@ -964,22 +882,6 @@  int stat(const char *pathname, struct stat *st);
 #ifdef MG_MODULE_LINES
 #line 1 "common/platforms/platform_msp432.h"
 #endif
-/*
- * Copyright (c) 2014-2018 Cesanta Software Limited
- * All rights reserved
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
 #ifndef CS_COMMON_PLATFORMS_PLATFORM_MSP432_H_
 #define CS_COMMON_PLATFORMS_PLATFORM_MSP432_H_
@@ -1084,22 +986,6 @@  int _stat(const char *pathname, struct stat *st);
 #ifdef MG_MODULE_LINES
 #line 1 "common/platforms/platform_tm4c129.h"
 #endif
-/*
- * Copyright (c) 2014-2018 Cesanta Software Limited
- * All rights reserved
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
 #ifndef CS_COMMON_PLATFORMS_PLATFORM_TM4C129_H_
 #define CS_COMMON_PLATFORMS_PLATFORM_TM4C129_H_
@@ -1156,22 +1042,6 @@  typedef struct stat cs_stat_t;
 #ifdef MG_MODULE_LINES
 #line 1 "common/platforms/platform_mbed.h"
 #endif
-/*
- * Copyright (c) 2014-2018 Cesanta Software Limited
- * All rights reserved
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
 #ifndef CS_COMMON_PLATFORMS_PLATFORM_MBED_H_
 #define CS_COMMON_PLATFORMS_PLATFORM_MBED_H_
@@ -1252,22 +1122,6 @@  in_addr_t inet_addr(const char *cp);
 #ifdef MG_MODULE_LINES
 #line 1 "common/platforms/platform_nrf51.h"
 #endif
-/*
- * Copyright (c) 2014-2018 Cesanta Software Limited
- * All rights reserved
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 #ifndef CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_
 #define CS_COMMON_PLATFORMS_PLATFORM_NRF51_H_
 #if CS_PLATFORM == CS_P_NRF51
@@ -1309,22 +1163,6 @@  int gettimeofday(struct timeval *tp, void *tzp);
 #ifdef MG_MODULE_LINES
 #line 1 "common/platforms/platform_nrf52.h"
 #endif
-/*
- * Copyright (c) 2014-2018 Cesanta Software Limited
- * All rights reserved
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 #ifndef CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_
 #define CS_COMMON_PLATFORMS_PLATFORM_NRF52_H_
 #if CS_PLATFORM == CS_P_NRF52
@@ -1369,22 +1207,6 @@  int gettimeofday(struct timeval *tp, void *tzp);
 #ifdef MG_MODULE_LINES
 #line 1 "common/platforms/simplelink/cs_simplelink.h"
 #endif
-/*
- * Copyright (c) 2014-2018 Cesanta Software Limited
- * All rights reserved
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
 #ifndef CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_
 #define CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_
@@ -1752,22 +1574,6 @@  const char *strerror();
 #ifdef MG_MODULE_LINES
 #line 1 "common/platforms/platform_nxp_lpc.h"
 #endif
-/*
- * Copyright (c) 2014-2018 Cesanta Software Limited
- * All rights reserved
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
 #ifndef CS_COMMON_PLATFORMS_PLATFORM_NXP_LPC_H_
 #define CS_COMMON_PLATFORMS_PLATFORM_NXP_LPC_H_
@@ -1820,22 +1626,6 @@  typedef struct stat cs_stat_t;
 #ifdef MG_MODULE_LINES
 #line 1 "common/platforms/platform_nxp_kinetis.h"
 #endif
-/*
- * Copyright (c) 2014-2018 Cesanta Software Limited
- * All rights reserved
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
 #ifndef CS_COMMON_PLATFORMS_PLATFORM_NXP_KINETIS_H_
 #define CS_COMMON_PLATFORMS_PLATFORM_NXP_KINETIS_H_
@@ -1866,22 +1656,6 @@  typedef struct stat cs_stat_t;
 #ifdef MG_MODULE_LINES
 #line 1 "common/platforms/platform_pic32.h"
 #endif
-/*
- * Copyright (c) 2014-2018 Cesanta Software Limited
- * All rights reserved
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
 #ifndef CS_COMMON_PLATFORMS_PLATFORM_PIC32_H_
 #define CS_COMMON_PLATFORMS_PLATFORM_PIC32_H_
@@ -1918,22 +1692,6 @@  char *inet_ntoa(struct in_addr in);
 #ifdef MG_MODULE_LINES
 #line 1 "common/platforms/platform_rs14100.h"
 #endif
-/*
- * Copyright (c) 2014-2019 Cesanta Software Limited
- * All rights reserved
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
 #ifndef CS_COMMON_PLATFORMS_PLATFORM_RS14100_H_
 #define CS_COMMON_PLATFORMS_PLATFORM_RS14100_H_
@@ -1982,22 +1740,6 @@  typedef struct stat cs_stat_t;
 #ifdef MG_MODULE_LINES
 #line 1 "common/platforms/platform_stm32.h"
 #endif
-/*
- * Copyright (c) 2014-2018 Cesanta Software Limited
- * All rights reserved
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
 #ifndef CS_COMMON_PLATFORMS_PLATFORM_STM32_H_
 #define CS_COMMON_PLATFORMS_PLATFORM_STM32_H_
@@ -2036,22 +1778,6 @@  typedef struct stat cs_stat_t;
 #ifdef MG_MODULE_LINES
 #line 1 "common/platforms/lwip/mg_lwip.h"
 #endif
-/*
- * Copyright (c) 2014-2018 Cesanta Software Limited
- * All rights reserved
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
 #ifndef CS_COMMON_PLATFORMS_LWIP_MG_LWIP_H_
 #define CS_COMMON_PLATFORMS_LWIP_MG_LWIP_H_
@@ -2119,22 +1845,6 @@  void mg_lwip_set_keepalive_params(struct mg_connection *nc, int idle,
 #ifdef MG_MODULE_LINES
 #line 1 "common/cs_md5.h"
 #endif
-/*
- * Copyright (c) 2014-2018 Cesanta Software Limited
- * All rights reserved
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
 #ifndef CS_COMMON_MD5_H_
 #define CS_COMMON_MD5_H_
@@ -2167,22 +1877,6 @@  void cs_md5_final(unsigned char *md, cs_md5_ctx *c);
 #ifdef MG_MODULE_LINES
 #line 1 "common/cs_sha1.h"
 #endif
-/*
- * Copyright (c) 2014-2018 Cesanta Software Limited
- * All rights reserved
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
 #ifndef CS_COMMON_SHA1_H_
 #define CS_COMMON_SHA1_H_
@@ -2221,22 +1915,6 @@  void cs_hmac_sha1(const unsigned char *key, size_t key_len,
 #ifdef MG_MODULE_LINES
 #line 1 "common/cs_time.h"
 #endif
-/*
- * Copyright (c) 2014-2018 Cesanta Software Limited
- * All rights reserved
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
 #ifndef CS_COMMON_CS_TIME_H_
 #define CS_COMMON_CS_TIME_H_
@@ -2266,22 +1944,6 @@  double cs_timegm(const struct tm *tm);
 #ifdef MG_MODULE_LINES
 #line 1 "common/mg_str.h"
 #endif
-/*
- * Copyright (c) 2014-2018 Cesanta Software Limited
- * All rights reserved
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
 #ifndef CS_COMMON_MG_STR_H_
 #define CS_COMMON_MG_STR_H_
@@ -2353,6 +2015,11 @@  int mg_strcmp(const struct mg_str str1, const struct mg_str str2);
  */
 int mg_strncmp(const struct mg_str str1, const struct mg_str str2, size_t n);
 
+/*
+ * Compare two `mg_str`s ignoreing case; return value is the same as `strcmp`.
+ */
+int mg_strcasecmp(const struct mg_str str1, const struct mg_str str2);
+
 /*
  * Free the string (assuming it was heap allocated).
  */
@@ -2377,22 +2044,6 @@  int mg_str_starts_with(struct mg_str s, struct mg_str prefix);
 #ifdef MG_MODULE_LINES
 #line 1 "common/mbuf.h"
 #endif
-/*
- * Copyright (c) 2014-2018 Cesanta Software Limited
- * All rights reserved
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
 /*
  * Mbufs are mutable/growing memory buffers, like C++ strings.
@@ -2491,22 +2142,6 @@  void mbuf_trim(struct mbuf *);
 #ifdef MG_MODULE_LINES
 #line 1 "common/cs_base64.h"
 #endif
-/*
- * Copyright (c) 2014-2018 Cesanta Software Limited
- * All rights reserved
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
 #ifndef CS_COMMON_CS_BASE64_H_
 #define CS_COMMON_CS_BASE64_H_
@@ -2560,22 +2195,6 @@  int cs_base64_decode(const unsigned char *s, int len, char *dst, int *dec_len);
 #ifdef MG_MODULE_LINES
 #line 1 "common/str_util.h"
 #endif
-/*
- * Copyright (c) 2014-2018 Cesanta Software Limited
- * All rights reserved
- *
- * Licensed under the Apache License, Version 2.0 (the ""License"");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an ""AS IS"" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
 #ifndef CS_COMMON_STR_UTIL_H_
 #define CS_COMMON_STR_UTIL_H_
@@ -3196,7 +2815,7 @@  struct {								\
 	if (LIST_NEXT((elm), field) != NULL &&				\
 	    LIST_NEXT((elm), field)->field.le_prev !=			\
 	     &((elm)->field.le_next))					\
-		panic("Bad link elm %p next->prev != elm", (elm));	\
+	     	panic("Bad link elm %p next->prev != elm", (elm));	\
 } while (0)
 
 #define	QMD_LIST_CHECK_PREV(elm, field) do {				\
@@ -3339,7 +2958,7 @@  struct {								\
 
 #define	QMD_TAILQ_CHECK_TAIL(head, field) do {				\
 	if (*(head)->tqh_last != NULL)					\
-		panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); 	\
+	    	panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); 	\
 } while (0)
 
 #define	QMD_TAILQ_CHECK_NEXT(elm, field) do {				\
@@ -3516,10 +3135,6 @@  struct {								\
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_features.h"
 #endif
-/*
- * Copyright (c) 2014-2016 Cesanta Software Limited
- * All rights reserved
- */
 
 #ifndef CS_MONGOOSE_SRC_FEATURES_H_
 #define CS_MONGOOSE_SRC_FEATURES_H_
@@ -3695,10 +3310,6 @@  struct {								\
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_net_if.h"
 #endif
-/*
- * Copyright (c) 2014-2016 Cesanta Software Limited
- * All rights reserved
- */
 
 #ifndef CS_MONGOOSE_SRC_NET_IF_H_
 #define CS_MONGOOSE_SRC_NET_IF_H_
@@ -3829,10 +3440,6 @@  double mg_mgr_min_timer(const struct mg_mgr *mgr);
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_ssl_if.h"
 #endif
-/*
- * Copyright (c) 2014-2016 Cesanta Software Limited
- * All rights reserved
- */
 
 #ifndef CS_MONGOOSE_SRC_SSL_IF_H_
 #define CS_MONGOOSE_SRC_SSL_IF_H_
@@ -3887,22 +3494,6 @@  int mg_ssl_if_write(struct mg_connection *nc, const void *data, size_t len);
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_net.h"
 #endif
-/*
- * Copyright (c) 2014 Cesanta Software Limited
- * All rights reserved
- * This software is dual-licensed: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation. For the terms of this
- * license, see <http://www.gnu.org/licenses/>.
- *
- * You are free to use this software under the terms of the GNU General
- * Public License, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * Alternatively, you can license this software under a commercial
- * license, as set out in <https://www.cesanta.com/license>.
- */
 
 /*
  * === Core API: TCP/UDP/SSL
@@ -3999,9 +3590,6 @@  struct mg_connection {
   struct mbuf send_mbuf;   /* Data scheduled for sending */
   time_t last_io_time;     /* Timestamp of the last socket IO */
   double ev_timer_time;    /* Timestamp of the future MG_EV_TIMER */
-#if MG_ENABLE_SSL
-  void *ssl_if_data; /* SSL library data. */
-#endif
   mg_event_handler_t proto_handler; /* Protocol-specific event handler */
   void *proto_data;                 /* Protocol-specific data */
   void (*proto_data_destructor)(void *proto_data);
@@ -4034,16 +3622,25 @@  struct mg_connection {
 /* Flags that are settable by user */
 #define MG_F_SEND_AND_CLOSE (1 << 10)      /* Push remaining data and close  */
 #define MG_F_CLOSE_IMMEDIATELY (1 << 11)   /* Disconnect */
-#define MG_F_WEBSOCKET_NO_DEFRAG (1 << 12) /* Websocket specific */
-#define MG_F_DELETE_CHUNK (1 << 13)        /* HTTP specific */
+
+/* Flags for protocol handlers */
+#define MG_F_PROTO_1 (1 << 12)
+#define MG_F_PROTO_2 (1 << 13)
 #define MG_F_ENABLE_BROADCAST (1 << 14)    /* Allow broadcast address usage */
 
-#define MG_F_USER_1 (1 << 20) /* Flags left for application */
+/* Flags left for application */
+#define MG_F_USER_1 (1 << 20)
 #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)
+
+#if MG_ENABLE_SSL
+  void *ssl_if_data; /* SSL library data. */
+#else
+  void *unused_ssl_if_data; /* To keep the size of the structure the same. */
+#endif
 };
 
 /*
@@ -4483,10 +4080,6 @@  double mg_time(void);
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_uri.h"
 #endif
-/*
- * Copyright (c) 2014 Cesanta Software Limited
- * All rights reserved
- */
 
 /*
  * === URI
@@ -4553,10 +4146,6 @@  int mg_normalize_uri_path(const struct mg_str *in, struct mg_str *out);
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_util.h"
 #endif
-/*
- * Copyright (c) 2014 Cesanta Software Limited
- * All rights reserved
- */
 
 /*
  * === Utility API
@@ -4771,10 +4360,6 @@  struct mg_str mg_url_encode(const struct mg_str src);
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_http.h"
 #endif
-/*
- * Copyright (c) 2014 Cesanta Software Limited
- * All rights reserved
- */
 
 /*
  * === Common API reference
@@ -4785,8 +4370,8 @@  struct mg_str mg_url_encode(const struct mg_str src);
 
 #if MG_ENABLE_HTTP
 
-/* Amalgamated: #include "mg_net.h" */
 /* Amalgamated: #include "common/mg_str.h" */
+/* Amalgamated: #include "mg_net.h" */
 
 #ifdef __cplusplus
 extern "C" {
@@ -4893,6 +4478,9 @@  struct mg_ssi_call_ctx {
 #define MG_EV_HTTP_MULTIPART_REQUEST_END 125
 #endif
 
+#define MG_F_WEBSOCKET_NO_DEFRAG MG_F_PROTO_1
+#define MG_F_DELETE_CHUNK MG_F_PROTO_2
+
 /*
  * Attaches a built-in HTTP event handler to the given connection.
  * The user-defined event handler will receive following extra events:
@@ -5777,22 +5365,6 @@  int mg_http_create_digest_auth_header(char *buf, size_t buf_len,
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_mqtt.h"
 #endif
-/*
- * Copyright (c) 2014 Cesanta Software Limited
- * All rights reserved
- * This software is dual-licensed: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation. For the terms of this
- * license, see <http://www.gnu.org/licenses/>.
- *
- * You are free to use this software under the terms of the GNU General
- * Public License, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * Alternatively, you can license this software under a commercial
- * license, as set out in <https://www.cesanta.com/license>.
- */
 
 /*
  * === MQTT API reference
@@ -5880,10 +5452,10 @@  struct mg_mqtt_proto_data {
 
 /* Message flags */
 #define MG_MQTT_RETAIN 0x1
-#define MG_MQTT_DUP 0x4
 #define MG_MQTT_QOS(qos) ((qos) << 1)
 #define MG_MQTT_GET_QOS(flags) (((flags) &0x6) >> 1)
 #define MG_MQTT_SET_QOS(flags, qos) (flags) = ((flags) & ~0x6) | ((qos) << 1)
+#define MG_MQTT_DUP 0x8
 
 /* Connection flags */
 #define MG_MQTT_CLEAN_SESSION 0x02
@@ -6008,22 +5580,6 @@  int mg_mqtt_vmatch_topic_expression(const char *exp, struct mg_str topic);
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_mqtt_server.h"
 #endif
-/*
- * Copyright (c) 2014 Cesanta Software Limited
- * All rights reserved
- * This software is dual-licensed: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation. For the terms of this
- * license, see <http://www.gnu.org/licenses/>.
- *
- * You are free to use this software under the terms of the GNU General
- * Public License, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * Alternatively, you can license this software under a commercial
- * license, as set out in <https://www.cesanta.com/license>.
- */
 
 /*
  * === MQTT Server API reference
@@ -6115,10 +5671,6 @@  struct mg_mqtt_session *mg_mqtt_next(struct mg_mqtt_broker *brk,
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_dns.h"
 #endif
-/*
- * Copyright (c) 2014 Cesanta Software Limited
- * All rights reserved
- */
 
 /*
  * === DNS API reference
@@ -6241,6 +5793,7 @@  int mg_dns_encode_record(struct mbuf *io, struct mg_dns_resource_record *rr,
  * Encodes a DNS name.
  */
 int mg_dns_encode_name(struct mbuf *io, const char *name, size_t len);
+int mg_dns_encode_name_s(struct mbuf *io, struct mg_str name);
 
 /* Low-level: parses a DNS response. */
 int mg_parse_dns(const char *buf, int len, struct mg_dns_message *msg);
@@ -6282,10 +5835,6 @@  void mg_set_protocol_dns(struct mg_connection *nc);
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_dns_server.h"
 #endif
-/*
- * Copyright (c) 2014 Cesanta Software Limited
- * All rights reserved
- */
 
 /*
  * === DNS server API reference
@@ -6379,10 +5928,6 @@  void mg_dns_send_reply(struct mg_connection *nc, struct mg_dns_reply *r);
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_resolv.h"
 #endif
-/*
- * Copyright (c) 2014 Cesanta Software Limited
- * All rights reserved
- */
 
 /*
  * === API reference
@@ -6463,22 +6008,6 @@  int mg_resolve_from_hosts_file(const char *host, union socket_address *usa);
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_coap.h"
 #endif
-/*
- * Copyright (c) 2015 Cesanta Software Limited
- * All rights reserved
- * This software is dual-licensed: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation. For the terms of this
- * license, see <http://www.gnu.org/licenses/>.
- *
- * You are free to use this software under the terms of the GNU General
- * Public License, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * Alternatively, you can license this software under a commercial
- * license, as set out in <https://www.cesanta.com/license>.
- */
 
 /*
  * === CoAP API reference
@@ -6631,10 +6160,6 @@  uint32_t mg_coap_compose(struct mg_coap_message *cm, struct mbuf *io);
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_sntp.h"
 #endif
-/*
- * Copyright (c) 2016 Cesanta Software Limited
- * All rights reserved
- */
 
 #ifndef CS_MONGOOSE_SRC_SNTP_H_
 #define CS_MONGOOSE_SRC_SNTP_H_
@@ -6688,10 +6213,6 @@  struct mg_connection *mg_sntp_get_time(struct mg_mgr *mgr,
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_socks.h"
 #endif
-/*
- * Copyright (c) 2017 Cesanta Software Limited
- * All rights reserved
- */
 
 #ifndef CS_MONGOOSE_SRC_SOCKS_H_
 #define CS_MONGOOSE_SRC_SOCKS_H_