text: // Copyright (c) 2004-2013 Sergey Lyubka
  // Copyright (c) 2013-2021 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.mongoose.ws/licensing/
  
  #ifndef MONGOOSE_H
  #define MONGOOSE_H
  
  #define MG_VERSION "7.5"
  
  #ifdef __cplusplus
  extern "C" {
  #endif
  
  
  #define MG_ARCH_CUSTOM 0
  #define MG_ARCH_UNIX 1
  #define MG_ARCH_WIN32 2
  #define MG_ARCH_ESP32 3
  #define MG_ARCH_ESP8266 4
  #define MG_ARCH_FREERTOS_TCP 5
  #define MG_ARCH_FREERTOS_LWIP 6
  #define MG_ARCH_AZURERTOS 7
  
  #if !defined(MG_ARCH)
  #if defined(__unix__) || defined(__APPLE__)
  #define MG_ARCH MG_ARCH_UNIX
  #elif defined(_WIN32)
  #define MG_ARCH MG_ARCH_WIN32
  #elif defined(ICACHE_FLASH) || defined(ICACHE_RAM_ATTR)
  #define MG_ARCH MG_ARCH_ESP8266
  #elif defined(ESP_PLATFORM)
  #define MG_ARCH MG_ARCH_ESP32
  #elif defined(FREERTOS_IP_H)
  #define MG_ARCH MG_ARCH_FREERTOS_TCP
  #elif defined(AZURE_RTOS_THREADX)
  #define MG_ARCH MG_ARCH_AZURERTOS
  #endif
  
  #if !defined(MG_ARCH)
  #error "MG_ARCH is not specified and we couldn't guess it."
  #endif
  #endif  // !defined(MG_ARCH)
  
  #if !defined(PRINTF_LIKE)
  #if defined(__GNUC__) || defined(__clang__) || defined(__TI_COMPILER_VERSION__)
  #define PRINTF_LIKE(f, a) __attribute__((format(printf, f, a)))
  #else
  #define PRINTF_LIKE(f, a)
  #endif
  #endif
  
  #if MG_ARCH == MG_ARCH_CUSTOM
  #include <mongoose_custom.h>
  #endif
  
  
  
  
  
  
  
  
  
  #if MG_ARCH == MG_ARCH_AZURERTOS
  
  #include <stdarg.h>
  #include <stdint.h>
  #include <stdbool.h>
  #include <time.h>
  #include <stdio.h>
   
  #include <tx_api.h>
  #include <fx_api.h>
  
  #include <tx_port.h>
  #include <nx_port.h>
  #include <nx_api.h>
  #include <nx_bsd.h>
  
  #ifdef __REDLIB__
  #define va_copy(d,s)__builtin_va_copy(d,s)
  #endif
  
  #define PATH_MAX FX_MAXIMUM_PATH
  #define MG_DIRSEP '\\'
  
  #define socklen_t int
  #define closesocket(x) soc_close(x)
  #define gmtime_r(a, b) gmtime(a)
  #define MG_INT64_FMT "%lld"
  
  static __inline struct tm *localtime_r(time_t *t, struct tm *tm) {
    (void) tm;
    return localtime(t);
  }
  
  #undef FOPEN_MAX
  
  #endif
  
  
  #if MG_ARCH == MG_ARCH_ESP32
  
  #include <ctype.h>
  #include <dirent.h>
  #include <errno.h>
  #include <fcntl.h>
  #include <limits.h>
  #include <netdb.h>
  #include <stdarg.h>
  #include <stddef.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <sys/stat.h>
  #include <sys/types.h>
  #include <time.h>
  
  #define MG_PATH_MAX 128
  #define MG_ENABLE_DIRLIST 1
  
  #endif
  
  
  #if MG_ARCH == MG_ARCH_ESP8266
  
  #include <ctype.h>
  #include <dirent.h>
  #include <errno.h>
  #include <fcntl.h>
  #include <limits.h>
  #include <netdb.h>
  #include <stdarg.h>
  #include <stdbool.h>
  #include <stddef.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <sys/stat.h>
  #include <sys/time.h>
  #include <sys/types.h>
  #include <time.h>
  
  #include <esp_system.h>
  
  #define MG_PATH_MAX 128
  #define MG_ENABLE_DIRLIST 1
  
  #endif
  
  
  #if MG_ARCH == MG_ARCH_FREERTOS_LWIP
  
  #include <stdarg.h>
  #include <stdbool.h>
  #include <stdint.h>
  #include <string.h>
  #include <stdio.h>
  
  #if defined(__GNUC__)
  #include <sys/stat.h>
  #include <sys/time.h>
  #else
  typedef long suseconds_t;
  struct timeval {
    time_t tv_sec;
    suseconds_t tv_usec;
  };
  #endif
  
  #include <FreeRTOS.h>
  #include <task.h>
  
  #include <lwip/sockets.h>
  
  #if LWIP_SOCKET != 1
  // Sockets support disabled in LWIP by default
  #error Set LWIP_SOCKET variable to 1 (in lwipopts.h)
  #endif
  
  #if LWIP_POSIX_SOCKETS_IO_NAMES != 0
  // LWIP_POSIX_SOCKETS_IO_NAMES must be disabled in posix-compatible OS
  // enviroment (freertos mimics to one) otherwise names like `read` and `write`
  // conflict
  #error LWIP_POSIX_SOCKETS_IO_NAMES must be set to 0 (in lwipopts.h) for FreeRTOS
  #endif
  
  #define MG_INT64_FMT "%lld"
  #define MG_DIRSEP '/'
  
  // Re-route calloc/free to the FreeRTOS's functions, don't use stdlib
  static inline void *mg_calloc(int cnt, size_t size) {
    void *p = pvPortMalloc(cnt * size);
    if (p != NULL) memset(p, 0, size);
    return p;
  }
  #define calloc(a, b) mg_calloc((a), (b))
  #define free(a) vPortFree(a)
  #define malloc(a) pvPortMalloc(a)
  
  #define gmtime_r(a, b) gmtime(a)
  
  #endif  // MG_ARCH == MG_ARCH_FREERTOS_LWIP
  
  
  #if MG_ARCH == MG_ARCH_FREERTOS_TCP
  
  #include <ctype.h>
  #include <errno.h>
  #include <limits.h>
  #include <stdarg.h>
  #include <stdbool.h>
  #include <stddef.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <sys/stat.h>
  #include <time.h>
  
  #include <FreeRTOS.h>
  #include <FreeRTOS_IP.h>
  #include <FreeRTOS_Sockets.h>
  #include <task.h>
  
  // Why FreeRTOS-TCP did not implement a clean BSD API, but its own thing
  // with FreeRTOS_ prefix, is beyond me
  #define IPPROTO_TCP FREERTOS_IPPROTO_TCP
  #define IPPROTO_UDP FREERTOS_IPPROTO_UDP
  #define AF_INET FREERTOS_AF_INET
  #define SOCK_STREAM FREERTOS_SOCK_STREAM
  #define SOCK_DGRAM FREERTOS_SOCK_DGRAM
  #define SO_BROADCAST 0
  #define SO_ERROR 0
  #define SOL_SOCKET 0
  #define SO_REUSEADDR 0
  #define sockaddr_in freertos_sockaddr
  #define sockaddr freertos_sockaddr
  #define accept(a, b, c) FreeRTOS_accept((a), (b), (c))
  #define connect(a, b, c) FreeRTOS_connect((a), (b), (c))
  #define bind(a, b, c) FreeRTOS_bind((a), (b), (c))
  #define listen(a, b) FreeRTOS_listen((a), (b))
  #define socket(a, b, c) FreeRTOS_socket((a), (b), (c))
  #define send(a, b, c, d) FreeRTOS_send((a), (b), (c), (d))
  #define recv(a, b, c, d) FreeRTOS_recv((a), (b), (c), (d))
  #define setsockopt(a, b, c, d, e) FreeRTOS_setsockopt((a), (b), (c), (d), (e))
  #define sendto(a, b, c, d, e, f) FreeRTOS_sendto((a), (b), (c), (d), (e), (f))
  #define recvfrom(a, b, c, d, e, f) \
    FreeRTOS_recvfrom((a), (b), (c), (d), (e), (f))
  #define closesocket(x) FreeRTOS_closesocket(x)
  #define gethostbyname(x) FreeRTOS_gethostbyname(x)
  #define getsockname(a, b, c) (-1)
  
  // Re-route calloc/free to the FreeRTOS's functions, don't use stdlib
  static inline void *mg_calloc(int cnt, size_t size) {
    void *p = pvPortMalloc(cnt * size);
    if (p != NULL) memset(p, 0, size);
    return p;
  }
  #define calloc(a, b) mg_calloc((a), (b))
  #define free(a) vPortFree(a)
  #define malloc(a) pvPortMalloc(a)
  
  #define gmtime_r(a, b) gmtime(a)
  
  #if !defined(__GNUC__)
  // copied from GCC on ARM; for some reason useconds are signed
  typedef long suseconds_t;
  struct timeval {
    time_t tv_sec;
    suseconds_t tv_usec;
  };
  #endif
  
  #ifndef EINPROGRESS
  #define EINPROGRESS pdFREERTOS_ERRNO_EINPROGRESS
  #endif
  #ifndef EWOULDBLOCK
  #define EWOULDBLOCK pdFREERTOS_ERRNO_EWOULDBLOCK
  #endif
  #ifndef EAGAIN
  #define EAGAIN pdFREERTOS_ERRNO_EAGAIN
  #endif
  #ifndef EINTR
  #define EINTR pdFREERTOS_ERRNO_EINTR
  #endif
  
  #endif  // MG_ARCH == MG_ARCH_FREERTOS_TCP
  
  
  #if MG_ARCH == MG_ARCH_UNIX
  
  #define _DARWIN_UNLIMITED_SELECT 1  // No limit on file descriptors
  
  #include <arpa/inet.h>
  #include <ctype.h>
  #include <dirent.h>
  #include <errno.h>
  #include <fcntl.h>
  #include <inttypes.h>
  #include <limits.h>
  #include <netdb.h>
  #include <netinet/tcp.h>
  #include <signal.h>
  #include <stdarg.h>
  #include <stdbool.h>
  #include <stddef.h>
  #include <stdint.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <sys/select.h>
  #include <sys/socket.h>
  #include <sys/stat.h>
  #include <sys/time.h>
  #include <sys/types.h>
  #include <time.h>
  #include <unistd.h>
  
  #define MG_INT64_FMT "%" PRId64
  
  #ifndef MG_ENABLE_DIRLIST
  #define MG_ENABLE_DIRLIST 1
  #endif
  
  #endif
  
  
  #if MG_ARCH == MG_ARCH_WIN32
  
  #ifndef WIN32_LEAN_AND_MEAN
  #define WIN32_LEAN_AND_MEAN
  #endif
  
  #ifndef _CRT_SECURE_NO_WARNINGS
  #define _CRT_SECURE_NO_WARNINGS
  #endif
  
  #ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
  #define _WINSOCK_DEPRECATED_NO_WARNINGS
  #endif
  
  #include <ctype.h>
  #include <errno.h>
  #include <fcntl.h>
  #include <limits.h>
  #include <stdarg.h>
  #include <stddef.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <sys/stat.h>
  #include <sys/types.h>
  #include <time.h>
  
  #if defined(_MSC_VER) && _MSC_VER < 1700
  #define __func__ ""
  typedef __int64 int64_t;
  typedef unsigned __int64 uint64_t;
  typedef unsigned char uint8_t;
  typedef char int8_t;
  typedef unsigned short uint16_t;
  typedef short int16_t;
  typedef unsigned int uint32_t;
  typedef int int32_t;
  typedef enum { false = 0, true = 1 } bool;
  #else
  #include <stdbool.h>
  #include <stdint.h>
  #include <ws2tcpip.h>
  #endif
  
  #include <winsock2.h>
  
  // Protect from calls like std::snprintf in app code
  // See https://github.com/cesanta/mongoose/issues/1047
  #ifndef __cplusplus
  #define sleep(x) Sleep(x)
  #define snprintf _snprintf
  #define vsnprintf _vsnprintf
  #ifndef strdup  // For MSVC with _DEBUG, see #1359
  #define strdup(x) _strdup(x)
  #endif
  #endif
  
  typedef unsigned suseconds_t;
  typedef int socklen_t;
  #define MG_DIRSEP '\\'
  #ifndef PATH_MAX
  #define PATH_MAX MAX_PATH
  #endif
  #ifndef EINPROGRESS
  #define EINPROGRESS WSAEINPROGRESS
  #endif
  #ifndef EWOULDBLOCK
  #define EWOULDBLOCK WSAEWOULDBLOCK
  #endif
  
  #ifndef va_copy
  #ifdef __va_copy
  #define va_copy __va_copy
  #else
  #define va_copy(x, y) (x) = (y)
  #endif
  #endif
  #ifndef S_ISDIR
  #define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR)
  #endif
  
  #define MG_INT64_FMT "%I64d"
  
  #ifndef MG_ENABLE_DIRLIST
  #define MG_ENABLE_DIRLIST 1
  #endif
  
  // https://lgtm.com/rules/2154840805/ -gmtime, localtime, ctime and asctime
  static __inline struct tm *gmtime_r(time_t *t, struct tm *tm) {
    struct tm *x = gmtime(t);
    *tm = *x;
    return tm;
  }
  
  static __inline struct tm *localtime_r(time_t *t, struct tm *tm) {
    struct tm *x = localtime(t);
    *tm = *x;
    return tm;
  }
  
  #endif
  
  
  #ifndef MG_ENABLE_SOCKET
  #define MG_ENABLE_SOCKET 1
  #endif
  
  #ifndef MG_ENABLE_MBEDTLS
  #define MG_ENABLE_MBEDTLS 0
  #endif
  
  #ifndef MG_ENABLE_OPENSSL
  #define MG_ENABLE_OPENSSL 0
  #endif
  
  #ifndef MG_ENABLE_CUSTOM_TLS
  #define MG_ENABLE_CUSTOM_TLS 0
  #endif
  
  #ifndef MG_ENABLE_SSI
  #define MG_ENABLE_SSI 1
  #endif
  
  #ifndef MG_ENABLE_IPV6
  #define MG_ENABLE_IPV6 0
  #endif
  
  #ifndef MG_ENABLE_LOG
  #define MG_ENABLE_LOG 1
  #endif
  
  #ifndef MG_ENABLE_MD5
  #define MG_ENABLE_MD5 0
  #endif
  
  // Set MG_ENABLE_WINSOCK=0 for Win32 builds with external IP stack (like LWIP)
  #ifndef MG_ENABLE_WINSOCK
  #define MG_ENABLE_WINSOCK 1
  #endif
  
  #ifndef MG_ENABLE_DIRLIST
  #define MG_ENABLE_DIRLIST 0
  #endif
  
  #ifndef MG_ENABLE_CUSTOM_RANDOM
  #define MG_ENABLE_CUSTOM_RANDOM 0
  #endif
  
  #ifndef MG_ENABLE_PACKED_FS
  #define MG_ENABLE_PACKED_FS 0
  #endif
  
  // Granularity of the send/recv IO buffer growth
  #ifndef MG_IO_SIZE
  #define MG_IO_SIZE 2048
  #endif
  
  // Maximum size of the recv IO buffer
  #ifndef MG_MAX_RECV_BUF_SIZE
  #define MG_MAX_RECV_BUF_SIZE (3 * 1024 * 1024)
  #endif
  
  #ifndef MG_MAX_HTTP_HEADERS
  #define MG_MAX_HTTP_HEADERS 40
  #endif
  
  #ifndef MG_HTTP_INDEX
  #define MG_HTTP_INDEX "index.html"
  #endif
  
  #ifndef MG_PATH_MAX
  #ifdef PATH_MAX
  #define MG_PATH_MAX PATH_MAX
  #else
  #define MG_PATH_MAX 128
  #endif
  #endif
  
  #ifndef MG_SOCK_LISTEN_BACKLOG_SIZE
  #define MG_SOCK_LISTEN_BACKLOG_SIZE 128
  #endif
  
  #ifndef MG_DIRSEP
  #define MG_DIRSEP '/'
  #endif
  
  #ifndef MG_INT64_FMT
  #define MG_INT64_FMT "%lld"
  #endif
  
  #ifndef MG_ENABLE_FILE
  #if defined(FOPEN_MAX)
  #define MG_ENABLE_FILE 1
  #else
  #define MG_ENABLE_FILE 0
  #endif
  #endif
  
  
  #include <ctype.h>
  #include <string.h>
  
  struct mg_str {
    const char *ptr;  // Pointer to string data
    size_t len;       // String len
  };
  
  #define MG_NULL_STR \
    { NULL, 0 }
  
  // Using macro to avoid shadowing C++ struct constructor, see #1298
  #define mg_str(s) mg_str_s(s)
  
  struct mg_str mg_str(const char *s);
  struct mg_str mg_str_n(const char *s, size_t n);
  int mg_lower(const char *s);
  int mg_ncasecmp(const char *s1, const char *s2, size_t len);
  int mg_casecmp(const char *s1, const char *s2);
  int mg_vcmp(const struct mg_str *s1, const char *s2);
  int mg_vcasecmp(const struct mg_str *str1, const char *str2);
  int mg_strcmp(const struct mg_str str1, const struct mg_str str2);
  struct mg_str mg_strstrip(struct mg_str s);
  struct mg_str mg_strdup(const struct mg_str s);
  const char *mg_strstr(const struct mg_str haystack, const struct mg_str needle);
  
  
  
  
  
  #if MG_ENABLE_LOG
  #define LOG(level, args)                                                   \
    do {                                                                     \
      if (mg_log_prefix((level), __FILE__, __LINE__, __func__)) mg_log args; \
    } while (0)
  enum { LL_NONE, LL_ERROR, LL_INFO, LL_DEBUG, LL_VERBOSE_DEBUG };
  bool mg_log_prefix(int ll, const char *file, int line, const char *fname);
  void mg_log(const char *fmt, ...) PRINTF_LIKE(1, 2);
  void mg_log_set(const char *spec);
  void mg_log_set_callback(void (*fn)(const void *, size_t, void *), void *param);
  #else
  #define LOG(level, args) (void) 0
  #define mg_log_set(x) (void) (x)
  #endif
  
  
  
  
  struct mg_timer {
    int64_t period_ms;        // Timer period in milliseconds
    int64_t expire;           // Expiration timestamp in milliseconds
    unsigned flags;           // Possible flags values below
  #define MG_TIMER_REPEAT 1   // Call function periodically, otherwise run once
  #define MG_TIMER_RUN_NOW 2  // Call immediately when timer is set
    void (*fn)(void *);       // Function to call
    void *arg;                // Function argument
    struct mg_timer *next;    // Linkage in g_timers list
  };
  
  extern struct mg_timer *g_timers;  // Global list of timers
  
  void mg_timer_init(struct mg_timer *, int64_t, unsigned, void (*)(void *),
                     void *);
  void mg_timer_free(struct mg_timer *);
  void mg_timer_poll(int64_t current_time_ms);
  
  
  
  
  
  
  char *mg_file_read(const char *path, size_t *size);
  bool mg_file_write(const char *path, const void *buf, size_t len);
  bool mg_file_printf(const char *path, const char *fmt, ...);
  void mg_random(void *buf, size_t len);
  bool mg_globmatch(const char *pattern, size_t plen, const char *s, size_t n);
  bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v);
  uint16_t mg_ntohs(uint16_t net);
  uint32_t mg_ntohl(uint32_t net);
  uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len);
  char *mg_hexdump(const void *buf, size_t len);
  char *mg_hex(const void *buf, size_t len, char *dst);
  void mg_unhex(const char *buf, size_t len, unsigned char *to);
  unsigned long mg_unhexn(const char *s, size_t len);
  int mg_asprintf(char **buf, size_t size, const char *fmt, ...);
  int mg_vasprintf(char **buf, size_t size, const char *fmt, va_list ap);
  int mg_check_ip_acl(struct mg_str acl, uint32_t remote_ip);
  int64_t mg_to64(struct mg_str str);
  int64_t mg_millis(void);
  
  #define mg_htons(x) mg_ntohs(x)
  #define mg_htonl(x) mg_ntohl(x)
  
  #ifndef EXTERN_C
  #ifdef __cplusplus
  #define EXTERN_C extern "C"
  #else
  #define EXTERN_C
  #endif
  #endif
  
  // Expands to a string representation of its argument: e.g.
  // MG_STRINGIFY_LITERAL(5) expands to "5"
  #if !defined(_MSC_VER) || _MSC_VER >= 1900
  #define MG_STRINGIFY_LITERAL(...) #__VA_ARGS__
  #else
  #define MG_STRINGIFY_LITERAL(x) #x
  #endif
  // Expands to a string representation of its argument, which can be a macro:
  // #define FOO 123
  // MG_STRINGIFY_MACRO(FOO)  // Expands to 123
  #define MG_STRINGIFY_MACRO(x) MG_STRINGIFY_LITERAL(x)
  
  // Linked list management macros
  #define LIST_ADD_HEAD(type_, head_, elem_) \
    do {                                     \
      (elem_)->next = (*head_);              \
      *(head_) = (elem_);                    \
    } while (0)
  
  #define LIST_ADD_TAIL(type_, head_, elem_) \
    do {                                     \
      type_ **h = head_;                     \
      while (*h != NULL) h = &(*h)->next;    \
      *h = (elem_);                          \
    } while (0)
  
  #define LIST_DELETE(type_, head_, elem_)   \
    do {                                     \
      type_ **h = head_;                     \
      while (*h != (elem_)) h = &(*h)->next; \
      *h = (elem_)->next;                    \
    } while (0)
  
  
  
  
  
  enum { MG_FS_READ = 1, MG_FS_WRITE = 2, MG_FS_DIR = 4 };
  
  // Filesystem API functions
  // stat() returns MG_FS_* flags and populates file size and modification time
  // list() calls fn() for every directory entry, allowing to list a directory
  struct mg_fs {
    int (*stat)(const char *path, size_t *size, time_t *mtime);
    void (*list)(const char *path, void (*fn)(const char *, void *), void *);
    struct mg_fd *(*open)(const char *path, int flags);      // Open file
    void (*close)(struct mg_fd *fd);                         // Close file
    size_t (*read)(void *fd, void *buf, size_t len);         // Read file
    size_t (*write)(void *fd, const void *buf, size_t len);  // Write file
    size_t (*seek)(void *fd, size_t offset);                 // Set file position
  };
  
  // File descriptor
  struct mg_fd {
    void *fd;
    struct mg_fs *fs;
  };
  
  extern struct mg_fs mg_fs_posix;   // POSIX open/close/read/write/seek
  extern struct mg_fs mg_fs_packed;  // Packed FS, see examples/complete
  
  
  
  unsigned short mg_url_port(const char *url);
  int mg_url_is_ssl(const char *url);
  struct mg_str mg_url_host(const char *url);
  struct mg_str mg_url_user(const char *url);
  struct mg_str mg_url_pass(const char *url);
  const char *mg_url_uri(const char *url);
  
  
  #include <stddef.h>
  
  struct mg_iobuf {
    unsigned char *buf;  // Pointer to stored data
    size_t size;         // Total size available
    size_t len;          // Current number of bytes
  };
  
  int mg_iobuf_init(struct mg_iobuf *, size_t);
  int mg_iobuf_resize(struct mg_iobuf *, size_t);
  void mg_iobuf_free(struct mg_iobuf *);
  size_t mg_iobuf_add(struct mg_iobuf *, size_t, const void *, size_t, size_t);
  size_t mg_iobuf_del(struct mg_iobuf *, size_t ofs, size_t len);
  
  int mg_base64_update(unsigned char p, char *to, int len);
  int mg_base64_final(char *to, int len);
  int mg_base64_encode(const unsigned char *p, int n, char *to);
  int mg_base64_decode(const char *src, int n, char *dst);
  
  
  
  
  typedef struct {
    uint32_t buf[4];
    uint32_t bits[2];
    unsigned char in[64];
  } mg_md5_ctx;
  
  void mg_md5_init(mg_md5_ctx *c);
  void mg_md5_update(mg_md5_ctx *c, const unsigned char *data, size_t len);
  void mg_md5_final(mg_md5_ctx *c, unsigned char[16]);
  
  
  
  
  typedef struct {
    uint32_t state[5];
    uint32_t count[2];
    unsigned char buffer[64];
  } mg_sha1_ctx;
  
  void mg_sha1_init(mg_sha1_ctx *);
  void mg_sha1_update(mg_sha1_ctx *, const unsigned char *data, size_t len);
  void mg_sha1_final(unsigned char digest[20], mg_sha1_ctx *);
  void mg_hmac_sha1(const unsigned char *key, size_t key_len,
                    const unsigned char *text, size_t text_len,
                    unsigned char out[20]);
  
  
  struct mg_connection;
  typedef void (*mg_event_handler_t)(struct mg_connection *, int ev,
                                     void *ev_data, void *fn_data);
  void mg_call(struct mg_connection *c, int ev, void *ev_data);
  void mg_error(struct mg_connection *c, const char *fmt, ...);
  
  enum {
    MG_EV_ERROR,       // Error                        char *error_message
    MG_EV_OPEN,        // Connection created           NULL
    MG_EV_POLL,        // mg_mgr_poll iteration        int64_t *milliseconds
    MG_EV_RESOLVE,     // Host name is resolved        NULL
    MG_EV_CONNECT,     // Connection established       NULL
    MG_EV_ACCEPT,      // Connection accepted          NULL
    MG_EV_READ,        // Data received from socket    struct mg_str *
    MG_EV_WRITE,       // Data written to socket       long *bytes_written
    MG_EV_CLOSE,       // Connection closed            NULL
    MG_EV_HTTP_MSG,    // HTTP request/response        struct mg_http_message *
    MG_EV_HTTP_CHUNK,  // HTTP chunk (partial msg)     struct mg_http_message *
    MG_EV_WS_OPEN,     // Websocket handshake done     struct mg_http_message *
    MG_EV_WS_MSG,      // Websocket msg, text or bin   struct mg_ws_message *
    MG_EV_WS_CTL,      // Websocket control msg        struct mg_ws_message *
    MG_EV_MQTT_CMD,    // MQTT low-level command       struct mg_mqtt_message *
    MG_EV_MQTT_MSG,    // MQTT PUBLISH received        struct mg_mqtt_message *
    MG_EV_MQTT_OPEN,   // MQTT CONNACK received        int *connack_status_code
    MG_EV_SNTP_TIME,   // SNTP time received           int64_t *milliseconds
    MG_EV_USER,        // Starting ID for user events
  };
  
  
  
  
  
  
  
  struct mg_dns {
    const char *url;          // DNS server URL
    struct mg_connection *c;  // DNS server connection
  };
  
  struct mg_addr {
    uint16_t port;    // TCP or UDP port in network byte order
    uint32_t ip;      // IP address in network byte order
    uint8_t ip6[16];  // IPv6 address
    bool is_ip6;      // True when address is IPv6 address
  };
  
  struct mg_mgr {
    struct mg_connection *conns;  // List of active connections
    struct mg_dns dns4;           // DNS for IPv4
    struct mg_dns dns6;           // DNS for IPv6
    int dnstimeout;               // DNS resolve timeout in milliseconds
    unsigned long nextid;         // Next connection ID
    void *userdata;               // Arbitrary user data pointer
  #if MG_ARCH == MG_ARCH_FREERTOS_TCP
    SocketSet_t ss;  // NOTE(lsm): referenced from socket struct
  #endif
  };
  
  struct mg_connection {
    struct mg_connection *next;  // Linkage in struct mg_mgr :: connections
    struct mg_mgr *mgr;          // Our container
    struct mg_addr peer;         // Remote address. For listeners, local address
    void *fd;                    // Connected socket, or LWIP data
    unsigned long id;            // Auto-incrementing unique connection ID
    struct mg_iobuf recv;        // Incoming data
    struct mg_iobuf send;        // Outgoing data
    mg_event_handler_t fn;       // User-specified event handler function
    void *fn_data;               // User-specified function parameter
    mg_event_handler_t pfn;      // Protocol-specific handler function
    void *pfn_data;              // Protocol-specific function parameter
    char label[50];              // Arbitrary label
    void *tls;                   // TLS specific data
    unsigned is_listening : 1;   // Listening connection
    unsigned is_client : 1;      // Outbound (client) connection
    unsigned is_accepted : 1;    // Accepted (server) connection
    unsigned is_resolving : 1;   // Non-blocking DNS resolution is in progress
    unsigned is_connecting : 1;  // Non-blocking connect is in progress
    unsigned is_tls : 1;         // TLS-enabled connection
    unsigned is_tls_hs : 1;      // TLS handshake is in progress
    unsigned is_udp : 1;         // UDP connection
    unsigned is_websocket : 1;   // WebSocket connection
    unsigned is_hexdumping : 1;  // Hexdump in/out traffic
    unsigned is_draining : 1;    // Send remaining data, then close and free
    unsigned is_closing : 1;     // Close and free the connection immediately
    unsigned is_readable : 1;    // Connection is ready to read
    unsigned is_writable : 1;    // Connection is ready to write
  };
  
  void mg_mgr_poll(struct mg_mgr *, int ms);
  void mg_mgr_init(struct mg_mgr *);
  void mg_mgr_free(struct mg_mgr *);
  
  struct mg_connection *mg_listen(struct mg_mgr *, const char *url,
                                  mg_event_handler_t fn, void *fn_data);
  struct mg_connection *mg_connect(struct mg_mgr *, const char *url,
                                   mg_event_handler_t fn, void *fn_data);
  void mg_connect_resolved(struct mg_connection *);
  bool mg_send(struct mg_connection *, const void *, size_t);
  int mg_printf(struct mg_connection *, const char *fmt, ...);
  int mg_vprintf(struct mg_connection *, const char *fmt, va_list ap);
  char *mg_straddr(struct mg_connection *, char *, size_t);
  bool mg_aton(struct mg_str str, struct mg_addr *addr);
  char *mg_ntoa(const struct mg_addr *addr, char *buf, size_t len);
  
  struct mg_connection *mg_mkpipe(struct mg_mgr *, mg_event_handler_t, void *);
  void mg_mgr_wakeup(struct mg_connection *pipe);
  
  
  
  
  
  
  
  
  struct mg_http_header {
    struct mg_str name;   // Header name
    struct mg_str value;  // Header value
  };
  
  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 head;                                  // Request + headers
    struct mg_str chunk;    // Chunk for chunked encoding,  or partial body
    struct mg_str message;  // Request + headers + body
  };
  
  // Parameter for mg_http_serve_dir()
  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,..
    struct mg_fs *fs;           // Filesystem implementation. Use NULL for POSIX
  };
  
  // 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
  };
  
  int mg_http_parse(const char *s, size_t len, struct mg_http_message *);
  int mg_http_get_request_len(const unsigned char *buf, size_t buf_len);
  void mg_http_printf_chunk(struct mg_connection *cnn, const char *fmt, ...);
  void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len);
  void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm);
  struct mg_connection *mg_http_listen(struct mg_mgr *, const char *url,
                                       mg_event_handler_t fn, void *fn_data);
  struct mg_connection *mg_http_connect(struct mg_mgr *, const char *url,
                                        mg_event_handler_t fn, void *fn_data);
  void mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm,
                         struct mg_http_serve_opts *opts);
  void mg_http_serve_file(struct mg_connection *, struct mg_http_message *hm,
                          const char *path, struct mg_http_serve_opts *opts);
  void mg_http_reply(struct mg_connection *, int status_code, const char *headers,
                     const char *body_fmt, ...);
  struct mg_str *mg_http_get_header(struct mg_http_message *, const char *name);
  int mg_http_get_var(const struct mg_str *, const char *name, char *, size_t);
  int mg_url_decode(const char *s, size_t n, char *to, size_t to_len, int form);
  size_t mg_url_encode(const char *s, size_t n, char *buf, size_t len);
  void mg_http_creds(struct mg_http_message *, char *, size_t, char *, size_t);
  bool mg_http_match_uri(const struct mg_http_message *, const char *glob);
  int mg_http_upload(struct mg_connection *, struct mg_http_message *hm,
                     const char *dir);
  void mg_http_bauth(struct mg_connection *, const char *user, const char *pass);
  struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v);
  size_t mg_http_next_multipart(struct mg_str, size_t, struct mg_http_part *);
  
  
  void mg_http_serve_ssi(struct mg_connection *c, const char *root,
                         const char *fullpath);
  
  
  
  
  
  
  struct mg_tls_opts {
    const char *ca;         // CA certificate file. For both listeners and clients
    const char *crl;        // Certificate Revocation List. For clients
    const char *cert;       // Certificate
    const char *certkey;    // Certificate key
    const char *ciphers;    // Cipher list
    struct mg_str srvname;  // If not empty, enables server name verification
  };
  
  void mg_tls_init(struct mg_connection *, struct mg_tls_opts *);
  void mg_tls_free(struct mg_connection *);
  long mg_tls_send(struct mg_connection *, const void *buf, size_t len);
  long mg_tls_recv(struct mg_connection *, void *buf, size_t len);
  void mg_tls_handshake(struct mg_connection *);
  
  
  #if MG_ENABLE_MBEDTLS
  
  
  
  
  #include <mbedtls/debug.h>
  #include <mbedtls/ssl.h>
  
  #if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000
  #define RNG , rng_get, NULL
  #else
  #define RNG
  #endif
  
  // Different versions have those in different files, so declare here
  EXTERN_C int mbedtls_net_recv(void *, unsigned char *, size_t);
  EXTERN_C int mbedtls_net_send(void *, const unsigned char *, size_t);
  
  struct mg_tls {
    char *cafile;             // CA certificate path
    mbedtls_x509_crt ca;      // Parsed CA certificate
    mbedtls_x509_crl crl;     // Parsed Certificate Revocation List
    mbedtls_x509_crt cert;    // Parsed certificate
    mbedtls_ssl_context ssl;  // SSL/TLS context
    mbedtls_ssl_config conf;  // SSL-TLS config
    mbedtls_pk_context pk;    // Private key context
  };
  #endif
  
  
  #if MG_ENABLE_OPENSSL
  
  #include <openssl/err.h>
  #include <openssl/ssl.h>
  
  struct mg_tls {
    SSL_CTX *ctx;
    SSL *ssl;
  };
  #endif
  
  
  #define WEBSOCKET_OP_CONTINUE 0
  #define WEBSOCKET_OP_TEXT 1
  #define WEBSOCKET_OP_BINARY 2
  #define WEBSOCKET_OP_CLOSE 8
  #define WEBSOCKET_OP_PING 9
  #define WEBSOCKET_OP_PONG 10
  
  
  
  struct mg_ws_message {
    struct mg_str data;  // Websocket message data
    uint8_t flags;       // Websocket message flags
  };
  
  struct mg_connection *mg_ws_connect(struct mg_mgr *, const char *url,
                                      mg_event_handler_t fn, void *fn_data,
                                      const char *fmt, ...);
  void mg_ws_upgrade(struct mg_connection *, struct mg_http_message *,
                     const char *fmt, ...);
  size_t mg_ws_send(struct mg_connection *, const char *buf, size_t len, int op);
  size_t mg_ws_wrap(struct mg_connection *, size_t len, int op);
  
  
  
  
  struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, const char *url,
                                        mg_event_handler_t fn, void *fn_data);
  void mg_sntp_send(struct mg_connection *c, unsigned long utc);
  int64_t mg_sntp_parse(const unsigned char *buf, size_t len);
  
  
  
  
  
  #define MQTT_CMD_CONNECT 1
  #define MQTT_CMD_CONNACK 2
  #define MQTT_CMD_PUBLISH 3
  #define MQTT_CMD_PUBACK 4
  #define MQTT_CMD_PUBREC 5
  #define MQTT_CMD_PUBREL 6
  #define MQTT_CMD_PUBCOMP 7
  #define MQTT_CMD_SUBSCRIBE 8
  #define MQTT_CMD_SUBACK 9
  #define MQTT_CMD_UNSUBSCRIBE 10
  #define MQTT_CMD_UNSUBACK 11
  #define MQTT_CMD_PINGREQ 12
  #define MQTT_CMD_PINGRESP 13
  #define MQTT_CMD_DISCONNECT 14
  
  struct mg_mqtt_opts {
    struct mg_str user;          // Username, can be empty
    struct mg_str pass;          // Password, can be empty
    struct mg_str client_id;     // Client ID
    struct mg_str will_topic;    // Will topic
    struct mg_str will_message;  // Will message
    uint8_t qos;                 // Quality of service
    bool will_retain;            // Retain last will
    bool clean;                  // Use clean session, 0 or 1
    uint16_t keepalive;          // Keep-alive timer in seconds
  };
  
  struct mg_mqtt_message {
    struct mg_str topic;  // Parsed topic
    struct mg_str data;   // Parsed message
    struct mg_str dgram;  // Whole MQTT datagram, including headers
    uint16_t id;  // Set for PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK, PUBLISH
    uint8_t cmd;  // MQTT command, one of MQTT_CMD_*
    uint8_t qos;  // Quality of service
    uint8_t ack;  // Connack return code. 0 - success
  };
  
  struct mg_connection *mg_mqtt_connect(struct mg_mgr *, const char *url,
                                        struct mg_mqtt_opts *opts,
                                        mg_event_handler_t fn, void *fn_data);
  struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url,
                                       mg_event_handler_t fn, void *fn_data);
  void mg_mqtt_login(struct mg_connection *c, struct mg_mqtt_opts *opts);
  void mg_mqtt_pub(struct mg_connection *c, struct mg_str *topic,
                   struct mg_str *data, int qos, bool retain);
  void mg_mqtt_sub(struct mg_connection *, struct mg_str *topic, int qos);
  int mg_mqtt_parse(const uint8_t *buf, size_t len, struct mg_mqtt_message *m);
  void mg_mqtt_send_header(struct mg_connection *, uint8_t cmd, uint8_t flags,
                           uint32_t len);
  size_t mg_mqtt_next_sub(struct mg_mqtt_message *msg, struct mg_str *topic,
                          uint8_t *qos, size_t pos);
  size_t mg_mqtt_next_unsub(struct mg_mqtt_message *msg, struct mg_str *topic,
                            size_t pos);
  void mg_mqtt_ping(struct mg_connection *);
  void mg_mqtt_pong(struct mg_connection *);
  void mg_mqtt_disconnect(struct mg_connection *);
  
  
  
  
  
  // Mongoose sends DNS queries that contain only one question:
  // either A (IPv4) or AAAA (IPv6) address lookup.
  // Therefore, we expect zero or one answer.
  // If `resolved` is true, then `addr` contains resolved IPv4 or IPV6 address.
  struct mg_dns_message {
    uint16_t txnid;       // Transaction ID
    bool resolved;        // Resolve successful, addr is set
    struct mg_addr addr;  // Resolved address
    char name[256];       // Host name
  };
  
  struct mg_dns_header {
    uint16_t txnid;  // Transaction ID
    uint16_t flags;
    uint16_t num_questions;
    uint16_t num_answers;
    uint16_t num_authority_prs;
    uint16_t num_other_prs;
  };
  
  // DNS resource record
  struct mg_dns_rr {
    uint16_t nlen;    // Name or pointer length
    uint16_t atype;   // Address type
    uint16_t aclass;  // Address class
    uint16_t alen;    // Address length
  };
  
  void mg_resolve(struct mg_connection *, struct mg_str *, int);
  void mg_resolve_cancel(struct mg_connection *);
  bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *);
  size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs,
                         bool is_question, struct mg_dns_rr *);
  size_t mg_dns_decode_name(const uint8_t *, size_t, size_t, char *, size_t);
  
  #ifdef __cplusplus
  }
  #endif
  #endif  // MONGOOSE_H
  