]> git.mxchange.org Git - flightgear.git/commitdiff
httpd: upgrade to latest mongoose 5.5
authorTorsten Dreyer <torsten@ŧ3r.de>
Thu, 6 Nov 2014 21:21:22 +0000 (22:21 +0100)
committerTorsten Dreyer <torsten@ŧ3r.de>
Thu, 6 Nov 2014 21:21:22 +0000 (22:21 +0100)
3rdparty/mongoose/LICENSE
3rdparty/mongoose/mongoose.c
3rdparty/mongoose/mongoose.h
src/Network/http/httpd.cxx

index 2473d6dd717a1f7d353abd53c465904df35cadf5..08bf9ddc1364b5ebd84016a0af3fa14160e69341 100644 (file)
@@ -5,7 +5,7 @@ All rights reserved
 This code 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.
+license, see <http://www.gnu.org/licenses>.
 
 You are free to use this code under the terms of the GNU General
 Public License, but WITHOUT ANY WARRANTY; without even the implied
@@ -13,4 +13,4 @@ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 See the GNU General Public License for more details.
 
 Alternatively, you can license this code under a commercial
-license, as set out in http://cesanta.com/products.html.
+license, as set out in <http://cesanta.com/>.
index 9fdfa305d0bf5b9fcdd962facbe2d0985baef414..84c2d609456696a2c43480aa35ff264de625c7e4 100644 (file)
 //
 // Alternatively, you can license this library under a commercial
 // license, as set out in <http://cesanta.com/>.
+//
+// $Date: 2014-09-16 06:47:40 UTC $
 
 #ifdef NOEMBED_NET_SKELETON
 #include "net_skeleton.h"
 #else
 // net_skeleton start
-
 // Copyright (c) 2014 Cesanta Software Limited
 // All rights reserved
 //
-// This library is dual-licensed: you can redistribute it and/or modify
+// 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 library under the terms of the GNU General
+// 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 library under a commercial
+// Alternatively, you can license this software under a commercial
 // license, as set out in <http://cesanta.com/>.
+//
+// $Date: 2014-09-28 05:04:41 UTC $
 
 #ifndef NS_SKELETON_HEADER_INCLUDED
 #define NS_SKELETON_HEADER_INCLUDED
 
-#define NS_SKELETON_VERSION "1.0"
+#define NS_SKELETON_VERSION "2.1.0"
 
 #undef UNICODE                  // Use ANSI WinAPI functions
 #undef _UNICODE                 // Use multibyte encoding on Windows
@@ -50,7 +53,9 @@
 #define _XOPEN_SOURCE 600       // For flockfile() on Linux
 #define __STDC_FORMAT_MACROS    // <inttypes.h> wants this for C++
 #define __STDC_LIMIT_MACROS     // C++ wants that for INT64_MAX
+#ifndef _LARGEFILE_SOURCE
 #define _LARGEFILE_SOURCE       // Enable fseeko() and ftello() functions
+#endif
 #define _FILE_OFFSET_BITS 64    // Enable 64-bit file offsets
 
 #ifdef _MSC_VER
@@ -61,6 +66,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <assert.h>
+#include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <stdarg.h>
@@ -72,7 +78,9 @@
 #include <signal.h>
 
 #ifdef _WIN32
+#ifdef _MSC_VER
 #pragma comment(lib, "ws2_32.lib")    // Linking with winsock library
+#endif
 #include <windows.h>
 #include <process.h>
 #ifndef EINPROGRESS
@@ -91,6 +99,7 @@
 #endif // MINGW #defines va_copy
 #define snprintf _snprintf
 #define vsnprintf _vsnprintf
+#define sleep(x) Sleep((x) * 1000)
 #define to64(x) _atoi64(x)
 typedef int socklen_t;
 typedef unsigned char uint8_t;
@@ -99,6 +108,10 @@ typedef unsigned short uint16_t;
 typedef unsigned __int64 uint64_t;
 typedef __int64   int64_t;
 typedef SOCKET sock_t;
+typedef struct _stati64 ns_stat_t;
+#ifndef S_ISDIR
+#define S_ISDIR(x) ((x) & _S_IFDIR)
+#endif
 #else
 #include <errno.h>
 #include <fcntl.h>
@@ -115,6 +128,7 @@ typedef SOCKET sock_t;
 #define INVALID_SOCKET (-1)
 #define to64(x) strtoll(x, NULL, 10)
 typedef int sock_t;
+typedef struct stat ns_stat_t;
 #endif
 
 #ifdef NS_ENABLE_DEBUG
@@ -124,7 +138,9 @@ typedef int sock_t;
 #define DBG(x)
 #endif
 
+#ifndef ARRAY_SIZE
 #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
+#endif
 
 #ifdef NS_ENABLE_SSL
 #ifdef __APPLE__
@@ -145,68 +161,99 @@ union socket_address {
   struct sockaddr_in sin;
 #ifdef NS_ENABLE_IPV6
   struct sockaddr_in6 sin6;
+#else
+  struct sockaddr sin6;
 #endif
 };
 
+// Describes chunk of memory
+struct ns_str {
+  const char *p;
+  size_t len;
+};
+
+// IO buffers interface
 struct iobuf {
   char *buf;
-  int len;
-  int size;
+  size_t len;
+  size_t size;
 };
 
-void iobuf_init(struct iobuf *, int initial_size);
+void iobuf_init(struct iobuf *, size_t initial_size);
 void iobuf_free(struct iobuf *);
-int iobuf_append(struct iobuf *, const void *data, int data_size);
-void iobuf_remove(struct iobuf *, int data_size);
+size_t iobuf_append(struct iobuf *, const void *data, size_t data_size);
+void iobuf_remove(struct iobuf *, size_t data_size);
+void iobuf_resize(struct iobuf *, size_t new_size);
 
+// Callback function (event handler) prototype, must be defined by user.
+// Net skeleton will call event handler, passing events defined above.
 struct ns_connection;
-enum ns_event { NS_POLL, NS_ACCEPT, NS_CONNECT, NS_RECV, NS_SEND, NS_CLOSE };
-typedef void (*ns_callback_t)(struct ns_connection *, enum ns_event, void *);
+typedef void (*ns_callback_t)(struct ns_connection *, int event_num, void *evp);
+
+// Events. Meaning of event parameter (evp) is given in the comment.
+#define NS_POLL    0  // Sent to each connection on each call to ns_mgr_poll()
+#define NS_ACCEPT  1  // New connection accept()-ed. union socket_address *addr
+#define NS_CONNECT 2  // connect() succeeded or failed. int *success_status
+#define NS_RECV    3  // Data has benn received. int *num_bytes
+#define NS_SEND    4  // Data has been written to a socket. int *num_bytes
+#define NS_CLOSE   5  // Connection is closed. NULL
 
-struct ns_server {
-  void *server_data;
-  union socket_address listening_sa;
-  sock_t listening_sock;
+
+struct ns_mgr {
   struct ns_connection *active_connections;
-  ns_callback_t callback;
-  SSL_CTX *ssl_ctx;
-  SSL_CTX *client_ssl_ctx;
-  sock_t ctl[2];
+  const char *hexdump_file;         // Debug hexdump file path
+  sock_t ctl[2];                    // Socketpair for mg_wakeup()
+  void *user_data;                  // User data
 };
 
+
 struct ns_connection {
-  struct ns_connection *prev, *next;
-  struct ns_server *server;
-  void *connection_data;
-  time_t last_io_time;
-  sock_t sock;
-  struct iobuf recv_iobuf;
-  struct iobuf send_iobuf;
+  struct ns_connection *next, *prev;  // ns_mgr::active_connections linkage
+  struct ns_connection *listener;     // Set only for accept()-ed connections
+  struct ns_mgr *mgr;
+
+  sock_t sock;                // Socket
+  union socket_address sa;    // Peer address
+  struct iobuf recv_iobuf;    // Received data
+  struct iobuf send_iobuf;    // Data scheduled for sending
   SSL *ssl;
+  SSL_CTX *ssl_ctx;
+  void *user_data;            // User-specific data
+  void *proto_data;           // Application protocol-specific data
+  time_t last_io_time;        // Timestamp of the last socket IO
+  ns_callback_t callback;     // Event handler function
+
   unsigned int flags;
 #define NSF_FINISHED_SENDING_DATA   (1 << 0)
 #define NSF_BUFFER_BUT_DONT_SEND    (1 << 1)
 #define NSF_SSL_HANDSHAKE_DONE      (1 << 2)
 #define NSF_CONNECTING              (1 << 3)
 #define NSF_CLOSE_IMMEDIATELY       (1 << 4)
-#define NSF_ACCEPTED                (1 << 5)
-#define NSF_USER_1                  (1 << 6)
-#define NSF_USER_2                  (1 << 7)
-#define NSF_USER_3                  (1 << 8)
-#define NSF_USER_4                  (1 << 9)
+#define NSF_WANT_READ               (1 << 5)
+#define NSF_WANT_WRITE              (1 << 6)
+#define NSF_LISTENING               (1 << 7)
+#define NSF_UDP                     (1 << 8)
+
+#define NSF_USER_1                  (1 << 20)
+#define NSF_USER_2                  (1 << 21)
+#define NSF_USER_3                  (1 << 22)
+#define NSF_USER_4                  (1 << 23)
+#define NSF_USER_5                  (1 << 24)
+#define NSF_USER_6                  (1 << 25)
 };
 
-void ns_server_init(struct ns_server *, void *server_data, ns_callback_t);
-void ns_server_free(struct ns_server *);
-int ns_server_poll(struct ns_server *, int milli);
-void ns_server_wakeup(struct ns_server *);
-void ns_iterate(struct ns_server *, ns_callback_t cb, void *param);
-struct ns_connection *ns_add_sock(struct ns_server *, sock_t sock, void *p);
+void ns_mgr_init(struct ns_mgr *, void *user_data);
+void ns_mgr_free(struct ns_mgr *);
+time_t ns_mgr_poll(struct ns_mgr *, int milli);
+void ns_broadcast(struct ns_mgr *, ns_callback_t, void *, size_t);
 
-int ns_bind(struct ns_server *, const char *addr);
-int ns_set_ssl_cert(struct ns_server *, const char *ssl_cert);
-struct ns_connection *ns_connect(struct ns_server *, const char *host,
-                                 int port, int ssl, void *connection_param);
+struct ns_connection *ns_next(struct ns_mgr *, struct ns_connection *);
+struct ns_connection *ns_add_sock(struct ns_mgr *, sock_t,
+                                  ns_callback_t, void *);
+struct ns_connection *ns_bind(struct ns_mgr *, const char *,
+                              ns_callback_t, void *);
+struct ns_connection *ns_connect(struct ns_mgr *, const char *,
+                                 ns_callback_t, void *);
 
 int ns_send(struct ns_connection *, const void *buf, int len);
 int ns_printf(struct ns_connection *, const char *fmt, ...);
@@ -215,7 +262,12 @@ int ns_vprintf(struct ns_connection *, const char *fmt, va_list ap);
 // Utility functions
 void *ns_start_thread(void *(*f)(void *), void *p);
 int ns_socketpair(sock_t [2]);
+int ns_socketpair2(sock_t [2], int sock_type);  // SOCK_STREAM or SOCK_DGRAM
 void ns_set_close_on_exec(sock_t);
+void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags);
+int ns_hexdump(const void *buf, int len, char *dst, int dst_len);
+int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap);
+int ns_resolve(const char *domain_name, char *ip_addr_buf, size_t buf_len);
 
 #ifdef __cplusplus
 }
@@ -225,18 +277,20 @@ void ns_set_close_on_exec(sock_t);
 // Copyright (c) 2014 Cesanta Software Limited
 // All rights reserved
 //
-// This library is dual-licensed: you can redistribute it and/or modify
+// 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 library under the terms of the GNU General
+// 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 library under a commercial
+// Alternatively, you can license this software under a commercial
 // license, as set out in <http://cesanta.com/>.
+//
+// $Date: 2014-09-28 05:04:41 UTC $
 
 
 #ifndef NS_MALLOC
@@ -251,19 +305,29 @@ void ns_set_close_on_exec(sock_t);
 #define NS_FREE free
 #endif
 
-#ifndef IOBUF_RESIZE_MULTIPLIER
-#define IOBUF_RESIZE_MULTIPLIER 2.0
-#endif
+#define NS_UDP_RECEIVE_BUFFER_SIZE  2000
+#define NS_VPRINTF_BUFFER_SIZE      500
 
-void iobuf_init(struct iobuf *iobuf, int size) {
-  iobuf->len = iobuf->size = 0;
-  iobuf->buf = NULL;
+struct ctl_msg {
+  ns_callback_t callback;
+  char message[1024 * 8];
+};
 
-  if (size > 0 && (iobuf->buf = (char *) NS_MALLOC(size)) != NULL) {
-    iobuf->size = size;
+void iobuf_resize(struct iobuf *io, size_t new_size) {
+  char *p;
+  if ((new_size > io->size || (new_size < io->size && new_size >= io->len)) &&
+      (p = (char *) NS_REALLOC(io->buf, new_size)) != NULL) {
+    io->size = new_size;
+    io->buf = p;
   }
 }
 
+void iobuf_init(struct iobuf *iobuf, size_t initial_size) {
+  iobuf->len = iobuf->size = 0;
+  iobuf->buf = NULL;
+  iobuf_resize(iobuf, initial_size);
+}
+
 void iobuf_free(struct iobuf *iobuf) {
   if (iobuf != NULL) {
     if (iobuf->buf != NULL) NS_FREE(iobuf->buf);
@@ -271,24 +335,21 @@ void iobuf_free(struct iobuf *iobuf) {
   }
 }
 
-int iobuf_append(struct iobuf *io, const void *buf, int len) {
-  static const double mult = IOBUF_RESIZE_MULTIPLIER;
+size_t iobuf_append(struct iobuf *io, const void *buf, size_t len) {
   char *p = NULL;
-  int new_len = 0;
 
-  assert(io->len >= 0);
+  assert(io != NULL);
   assert(io->len <= io->size);
 
   if (len <= 0) {
-  } else if ((new_len = io->len + len) < io->size) {
+  } else if (io->len + len <= io->size) {
     memcpy(io->buf + io->len, buf, len);
-    io->len = new_len;
-  } else if ((p = (char *)
-              NS_REALLOC(io->buf, (int) (new_len * mult))) != NULL) {
+    io->len += len;
+  } else if ((p = (char *) NS_REALLOC(io->buf, io->len + len)) != NULL) {
     io->buf = p;
     memcpy(io->buf + io->len, buf, len);
-    io->len = new_len;
-    io->size = (int) (new_len * mult);
+    io->len += len;
+    io->size = io->len;
   } else {
     len = 0;
   }
@@ -296,13 +357,23 @@ int iobuf_append(struct iobuf *io, const void *buf, int len) {
   return len;
 }
 
-void iobuf_remove(struct iobuf *io, int n) {
-  if (n >= 0 && n <= io->len) {
+void iobuf_remove(struct iobuf *io, size_t n) {
+  if (n > 0 && n <= io->len) {
     memmove(io->buf, io->buf + n, io->len - n);
     io->len -= n;
   }
 }
 
+static size_t ns_out(struct ns_connection *nc, const void *buf, size_t len) {
+  if (nc->flags & NSF_UDP) {
+    long n = sendto(nc->sock, buf, len, 0, &nc->sa.sa, sizeof(nc->sa.sin));
+    DBG(("%p %d send %ld (%d %s)", nc, nc->sock, n, errno, strerror(errno)));
+    return n < 0 ? 0 : n;
+  } else {
+    return iobuf_append(&nc->send_iobuf, buf, len);
+  }
+}
+
 #ifndef NS_DISABLE_THREADS
 void *ns_start_thread(void *(*f)(void *), void *p) {
 #ifdef _WIN32
@@ -314,7 +385,7 @@ void *ns_start_thread(void *(*f)(void *), void *p) {
   (void) pthread_attr_init(&attr);
   (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
 
-#if NS_STACK_SIZE > 1
+#if defined(NS_STACK_SIZE) && NS_STACK_SIZE > 1
   (void) pthread_attr_setstacksize(&attr, NS_STACK_SIZE);
 #endif
 
@@ -326,15 +397,15 @@ void *ns_start_thread(void *(*f)(void *), void *p) {
 }
 #endif  // NS_DISABLE_THREADS
 
-static void ns_add_conn(struct ns_server *server, struct ns_connection *c) {
-  c->next = server->active_connections;
-  server->active_connections = c;
+static void ns_add_conn(struct ns_mgr *mgr, struct ns_connection *c) {
+  c->next = mgr->active_connections;
+  mgr->active_connections = c;
   c->prev = NULL;
   if (c->next != NULL) c->next->prev = c;
 }
 
 static void ns_remove_conn(struct ns_connection *conn) {
-  if (conn->prev == NULL) conn->server->active_connections = conn->next;
+  if (conn->prev == NULL) conn->mgr->active_connections = conn->next;
   if (conn->prev) conn->prev->next = conn->next;
   if (conn->next) conn->next->prev = conn->prev;
 }
@@ -342,7 +413,7 @@ static void ns_remove_conn(struct ns_connection *conn) {
 // Print message to buffer. If buffer is large enough to hold the message,
 // return buffer. If buffer is to small, allocate large enough buffer on heap,
 // and return allocated buffer.
-static int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap) {
+int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap) {
   va_list ap_copy;
   int len;
 
@@ -377,12 +448,12 @@ static int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap) {
   return len;
 }
 
-int ns_vprintf(struct ns_connection *conn, const char *fmt, va_list ap) {
-  char mem[2000], *buf = mem;
+int ns_vprintf(struct ns_connection *nc, const char *fmt, va_list ap) {
+  char mem[NS_VPRINTF_BUFFER_SIZE], *buf = mem;
   int len;
 
   if ((len = ns_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) {
-    iobuf_append(&conn->send_iobuf, buf, len);
+    ns_out(nc, buf, len);
   }
   if (buf != mem && buf != NULL) {
     free(buf);
@@ -400,20 +471,62 @@ int ns_printf(struct ns_connection *conn, const char *fmt, ...) {
   return len;
 }
 
-static void ns_call(struct ns_connection *conn, enum ns_event ev, void *p) {
-  if (conn->server->callback) conn->server->callback(conn, ev, p);
+static void hexdump(struct ns_connection *nc, const char *path,
+                    int num_bytes, int ev) {
+  const struct iobuf *io = ev == NS_SEND ? &nc->send_iobuf : &nc->recv_iobuf;
+  FILE *fp;
+  char *buf, src[60], dst[60];
+  int buf_size = num_bytes * 5 + 100;
+
+  if ((fp = fopen(path, "a")) != NULL) {
+    ns_sock_to_str(nc->sock, src, sizeof(src), 3);
+    ns_sock_to_str(nc->sock, dst, sizeof(dst), 7);
+    fprintf(fp, "%lu %p %s %s %s %d\n", (unsigned long) time(NULL),
+            nc->user_data, src,
+            ev == NS_RECV ? "<-" : ev == NS_SEND ? "->" :
+            ev == NS_ACCEPT ? "<A" : ev == NS_CONNECT ? "C>" : "XX",
+            dst, num_bytes);
+    if (num_bytes > 0 && (buf = (char *) NS_MALLOC(buf_size)) != NULL) {
+      ns_hexdump(io->buf + (ev == NS_SEND ? 0 : io->len) -
+        (ev == NS_SEND ? 0 : num_bytes), num_bytes, buf, buf_size);
+      fprintf(fp, "%s", buf);
+      free(buf);
+    }
+    fclose(fp);
+  }
 }
 
-static void ns_close_conn(struct ns_connection *conn) {
-  DBG(("%p %d", conn, conn->flags));
-  ns_call(conn, NS_CLOSE, NULL);
-  ns_remove_conn(conn);
+static void ns_call(struct ns_connection *nc, int ev, void *p) {
+  if (nc->mgr->hexdump_file != NULL && ev != NS_POLL) {
+    int len = (ev == NS_RECV || ev == NS_SEND) ? * (int *) p : 0;
+    hexdump(nc, nc->mgr->hexdump_file, len, ev);
+  }
+
+  nc->callback(nc, ev, p);
+}
+
+static void ns_destroy_conn(struct ns_connection *conn) {
   closesocket(conn->sock);
   iobuf_free(&conn->recv_iobuf);
   iobuf_free(&conn->send_iobuf);
+#ifdef NS_ENABLE_SSL
+  if (conn->ssl != NULL) {
+    SSL_free(conn->ssl);
+  }
+  if (conn->ssl_ctx != NULL) {
+    SSL_CTX_free(conn->ssl_ctx);
+  }
+#endif
   NS_FREE(conn);
 }
 
+static void ns_close_conn(struct ns_connection *conn) {
+  DBG(("%p %d", conn, conn->flags));
+  ns_call(conn, NS_CLOSE, NULL);
+  ns_remove_conn(conn);
+  ns_destroy_conn(conn);
+}
+
 void ns_set_close_on_exec(sock_t sock) {
 #ifdef _WIN32
   (void) SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0);
@@ -433,26 +546,29 @@ static void ns_set_non_blocking_mode(sock_t sock) {
 }
 
 #ifndef NS_DISABLE_SOCKETPAIR
-int ns_socketpair(sock_t sp[2]) {
-  struct sockaddr_in sa;
+int ns_socketpair2(sock_t sp[2], int sock_type) {
+  union socket_address sa;
   sock_t sock;
-  socklen_t len = sizeof(sa);
+  socklen_t len = sizeof(sa.sin);
   int ret = 0;
 
   sp[0] = sp[1] = INVALID_SOCKET;
 
   (void) memset(&sa, 0, sizeof(sa));
-  sa.sin_family = AF_INET;
-  sa.sin_port = htons(0);
-  sa.sin_addr.s_addr = htonl(0x7f000001);
-
-  if ((sock = socket(AF_INET, SOCK_STREAM, 0)) != INVALID_SOCKET &&
-      !bind(sock, (struct sockaddr *) &sa, len) &&
-      !listen(sock, 1) &&
-      !getsockname(sock, (struct sockaddr *) &sa, &len) &&
-      (sp[0] = socket(AF_INET, SOCK_STREAM, 6)) != -1 &&
-      !connect(sp[0], (struct sockaddr *) &sa, len) &&
-      (sp[1] = accept(sock,(struct sockaddr *) &sa, &len)) != INVALID_SOCKET) {
+  sa.sin.sin_family = AF_INET;
+  sa.sin.sin_port = htons(0);
+  sa.sin.sin_addr.s_addr = htonl(0x7f000001);
+
+  if ((sock = socket(AF_INET, sock_type, 0)) != INVALID_SOCKET &&
+      !bind(sock, &sa.sa, len) &&
+      (sock_type == SOCK_DGRAM || !listen(sock, 1)) &&
+      !getsockname(sock, &sa.sa, &len) &&
+      (sp[0] = socket(AF_INET, sock_type, 0)) != INVALID_SOCKET &&
+      !connect(sp[0], &sa.sa, len) &&
+      (sock_type == SOCK_STREAM ||
+       (!getsockname(sp[0], &sa.sa, &len) && !connect(sock, &sa.sa, len))) &&
+      (sp[1] = (sock_type == SOCK_DGRAM ? sock :
+                accept(sock, &sa.sa, &len))) != INVALID_SOCKET) {
     ns_set_close_on_exec(sp[0]);
     ns_set_close_on_exec(sp[1]);
     ret = 1;
@@ -461,16 +577,41 @@ int ns_socketpair(sock_t sp[2]) {
     if (sp[1] != INVALID_SOCKET) closesocket(sp[1]);
     sp[0] = sp[1] = INVALID_SOCKET;
   }
-  closesocket(sock);
+  if (sock_type != SOCK_DGRAM) closesocket(sock);
 
   return ret;
 }
+
+int ns_socketpair(sock_t sp[2]) {
+  return ns_socketpair2(sp, SOCK_STREAM);
+}
 #endif  // NS_DISABLE_SOCKETPAIR
 
-// Valid listening port spec is: [ip_address:]port, e.g. "80", "127.0.0.1:3128"
-static int ns_parse_port_string(const char *str, union socket_address *sa) {
+// TODO(lsm): use non-blocking resolver
+static int ns_resolve2(const char *host, struct in_addr *ina) {
+  struct hostent *he;
+  if ((he = gethostbyname(host)) == NULL) {
+    DBG(("gethostbyname(%s) failed: %s", host, strerror(errno)));
+  } else {
+    memcpy(ina, he->h_addr_list[0], sizeof(*ina));
+    return 1;
+  }
+  return 0;
+}
+
+// Resolve FDQN "host", store IP address in the "ip".
+// Return > 0 (IP address length) on success.
+int ns_resolve(const char *host, char *buf, size_t n) {
+  struct in_addr ad;
+  return ns_resolve2(host, &ad) ? snprintf(buf, n, "%s", inet_ntoa(ad)) : 0;
+}
+
+// Address format: [PROTO://][IP_ADDRESS:]PORT[:CERT][:CA_CERT]
+static int ns_parse_address(const char *str, union socket_address *sa,
+                            int *proto, int *use_ssl, char *cert, char *ca) {
   unsigned int a, b, c, d, port;
-  int len = 0;
+  int n = 0, len = 0;
+  char host[200];
 #ifdef NS_ENABLE_IPV6
   char buf[100];
 #endif
@@ -481,40 +622,71 @@ static int ns_parse_port_string(const char *str, union socket_address *sa) {
   memset(sa, 0, sizeof(*sa));
   sa->sin.sin_family = AF_INET;
 
+  *proto = SOCK_STREAM;
+  *use_ssl = 0;
+  cert[0] = ca[0] = '\0';
+
+  if (memcmp(str, "ssl://", 6) == 0) {
+    str += 6;
+    *use_ssl = 1;
+  } else if (memcmp(str, "udp://", 6) == 0) {
+    str += 6;
+    *proto = SOCK_DGRAM;
+  } else if (memcmp(str, "tcp://", 6) == 0) {
+    str += 6;
+  }
+
   if (sscanf(str, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len) == 5) {
     // Bind to a specific IPv4 address, e.g. 192.168.1.5:8080
     sa->sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d);
     sa->sin.sin_port = htons((uint16_t) port);
 #ifdef NS_ENABLE_IPV6
-  } else if (sscanf(str, "[%49[^]]]:%u%n", buf, &port, &len) == 2 &&
+  } else if (sscanf(str, "[%99[^]]]:%u%n", buf, &port, &len) == 2 &&
              inet_pton(AF_INET6, buf, &sa->sin6.sin6_addr)) {
     // IPv6 address, e.g. [3ffe:2a00:100:7031::1]:8080
     sa->sin6.sin6_family = AF_INET6;
     sa->sin6.sin6_port = htons((uint16_t) port);
 #endif
+  } else if (sscanf(str, "%199[^ :]:%u%n", host, &port, &len) == 2) {
+    sa->sin.sin_port = htons((uint16_t) port);
+    ns_resolve2(host, &sa->sin.sin_addr);
   } else if (sscanf(str, "%u%n", &port, &len) == 1) {
     // If only port is specified, bind to IPv4, INADDR_ANY
     sa->sin.sin_port = htons((uint16_t) port);
-  } else {
-    port = 0;   // Parsing failure. Make port invalid.
   }
 
-  return port <= 0xffff && str[len] == '\0';
+  if (*use_ssl && (sscanf(str + len, ":%99[^:]:%99[^:]%n", cert, ca, &n) == 2 ||
+                   sscanf(str + len, ":%99[^:]%n", cert, &n) == 1)) {
+    len += n;
+  }
+
+  return port < 0xffff && str[len] == '\0' ? len : 0;
 }
 
 // 'sa' must be an initialized address to bind to
-static sock_t ns_open_listening_socket(union socket_address *sa) {
-  socklen_t len = sizeof(*sa);
-  sock_t on = 1, sock = INVALID_SOCKET;
+static sock_t ns_open_listening_socket(union socket_address *sa, int proto) {
+  socklen_t sa_len = (sa->sa.sa_family == AF_INET) ?
+    sizeof(sa->sin) : sizeof(sa->sin6);
+  sock_t sock = INVALID_SOCKET;
+#ifndef _WIN32
+  int on = 1;
+#endif
 
-  if ((sock = socket(sa->sa.sa_family, SOCK_STREAM, 6)) != INVALID_SOCKET &&
+  if ((sock = socket(sa->sa.sa_family, proto, 0)) != INVALID_SOCKET &&
+#ifndef _WIN32
+      // SO_RESUSEADDR is not enabled on Windows because the semantics of
+      // SO_REUSEADDR on UNIX and Windows is different. On Windows,
+      // SO_REUSEADDR allows to bind a socket to a port without error even if
+      // the port is already open by another program. This is not the behavior
+      // SO_REUSEADDR was designed for, and leads to hard-to-track failure
+      // scenarios. Therefore, SO_REUSEADDR was disabled on Windows.
       !setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)) &&
-      !bind(sock, &sa->sa, sa->sa.sa_family == AF_INET ?
-            sizeof(sa->sin) : sizeof(sa->sa)) &&
-      !listen(sock, SOMAXCONN)) {
+#endif
+      !bind(sock, &sa->sa, sa_len) &&
+      (proto == SOCK_DGRAM || listen(sock, SOMAXCONN) == 0)) {
     ns_set_non_blocking_mode(sock);
     // In case port was set to 0, get the real port number
-    (void) getsockname(sock, &sa->sa, &len);
+    (void) getsockname(sock, &sa->sa, &sa_len);
   } else if (sock != INVALID_SOCKET) {
     closesocket(sock);
     sock = INVALID_SOCKET;
@@ -523,66 +695,101 @@ static sock_t ns_open_listening_socket(union socket_address *sa) {
   return sock;
 }
 
-
-int ns_set_ssl_cert(struct ns_server *server, const char *cert) {
 #ifdef NS_ENABLE_SSL
-  if (cert != NULL &&
-      (server->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
+// Certificate generation script is at
+// https://github.com/cesanta/net_skeleton/blob/master/scripts/gen_certs.sh
+
+static int ns_use_ca_cert(SSL_CTX *ctx, const char *cert) {
+  if (ctx == NULL) {
+    return -1;
+  } else if (cert == NULL || cert[0] == '\0') {
+    return 0;
+  }
+  SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0);
+  return SSL_CTX_load_verify_locations(ctx, cert, NULL) == 1 ? 0 : -2;
+}
+
+static int ns_use_cert(SSL_CTX *ctx, const char *pem_file) {
+  if (ctx == NULL) {
     return -1;
-  } else if (SSL_CTX_use_certificate_file(server->ssl_ctx, cert, 1) == 0 ||
-             SSL_CTX_use_PrivateKey_file(server->ssl_ctx, cert, 1) == 0) {
+  } else if (pem_file == NULL || pem_file[0] == '\0') {
+    return 0;
+  } else if (SSL_CTX_use_certificate_file(ctx, pem_file, 1) == 0 ||
+             SSL_CTX_use_PrivateKey_file(ctx, pem_file, 1) == 0) {
     return -2;
   } else {
-    SSL_CTX_use_certificate_chain_file(server->ssl_ctx, cert);
+    SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+    SSL_CTX_use_certificate_chain_file(ctx, pem_file);
+    return 0;
   }
-  return 0;
-#else
-  return server != NULL && cert == NULL ? 0 : -3;
-#endif
 }
+#endif  // NS_ENABLE_SSL
 
-int ns_bind(struct ns_server *server, const char *str) {
-  ns_parse_port_string(str, &server->listening_sa);
-  if (server->listening_sock != INVALID_SOCKET) {
-    closesocket(server->listening_sock);
+struct ns_connection *ns_bind(struct ns_mgr *srv, const char *str,
+                              ns_callback_t callback, void *user_data) {
+  union socket_address sa;
+  struct ns_connection *nc = NULL;
+  int use_ssl, proto;
+  char cert[100], ca_cert[100];
+  sock_t sock;
+
+  ns_parse_address(str, &sa, &proto, &use_ssl, cert, ca_cert);
+  if (use_ssl && cert[0] == '\0') return NULL;
+
+  if ((sock = ns_open_listening_socket(&sa, proto)) == INVALID_SOCKET) {
+  } else if ((nc = ns_add_sock(srv, sock, callback, NULL)) == NULL) {
+    closesocket(sock);
+  } else {
+    nc->sa = sa;
+    nc->flags |= NSF_LISTENING;
+    nc->user_data = user_data;
+    nc->callback = callback;
+
+    if (proto == SOCK_DGRAM) {
+      nc->flags |= NSF_UDP;
+    }
+
+#ifdef NS_ENABLE_SSL
+    if (use_ssl) {
+      nc->ssl_ctx = SSL_CTX_new(SSLv23_server_method());
+      if (ns_use_cert(nc->ssl_ctx, cert) != 0 ||
+          ns_use_ca_cert(nc->ssl_ctx, ca_cert) != 0) {
+        ns_close_conn(nc);
+        nc = NULL;
+      }
+    }
+#endif
+
+    DBG(("%p sock %d/%d ssl %p %p", nc, sock, proto, nc->ssl_ctx, nc->ssl));
   }
-  server->listening_sock = ns_open_listening_socket(&server->listening_sa);
-  return server->listening_sock == INVALID_SOCKET ? -1 :
-    (int) ntohs(server->listening_sa.sin.sin_port);
-}
 
+  return nc;
+}
 
-static struct ns_connection *accept_conn(struct ns_server *server) {
+static struct ns_connection *accept_conn(struct ns_connection *ls) {
   struct ns_connection *c = NULL;
   union socket_address sa;
   socklen_t len = sizeof(sa);
   sock_t sock = INVALID_SOCKET;
 
   // NOTE(lsm): on Windows, sock is always > FD_SETSIZE
-  if ((sock = accept(server->listening_sock, &sa.sa, &len)) == INVALID_SOCKET) {
-    closesocket(sock);
-  } else if ((c = (struct ns_connection *) NS_MALLOC(sizeof(*c))) == NULL ||
-             memset(c, 0, sizeof(*c)) == NULL) {
+  if ((sock = accept(ls->sock, &sa.sa, &len)) == INVALID_SOCKET) {
+  } else if ((c = ns_add_sock(ls->mgr, sock, ls->callback,
+              ls->user_data)) == NULL) {
     closesocket(sock);
 #ifdef NS_ENABLE_SSL
-  } else if (server->ssl_ctx != NULL &&
-             ((c->ssl = SSL_new(server->ssl_ctx)) == NULL ||
+  } else if (ls->ssl_ctx != NULL &&
+             ((c->ssl = SSL_new(ls->ssl_ctx)) == NULL ||
               SSL_set_fd(c->ssl, sock) != 1)) {
     DBG(("SSL error"));
-    closesocket(sock);
-    free(c);
+    ns_close_conn(c);
     c = NULL;
 #endif
   } else {
-    ns_set_close_on_exec(sock);
-    ns_set_non_blocking_mode(sock);
-    c->server = server;
-    c->sock = sock;
-    c->flags |= NSF_ACCEPTED;
-
-    ns_add_conn(server, c);
+    c->listener = ls;
+    c->proto_data = ls->proto_data;
     ns_call(c, NS_ACCEPT, &sa);
-    DBG(("%p %d %p %p", c, c->sock, c->ssl, server->ssl_ctx));
+    DBG(("%p %d %p %p", c, c->sock, c->ssl_ctx, c->ssl));
   }
 
   return c;
@@ -598,47 +805,65 @@ static int ns_is_error(int n) {
     );
 }
 
-#ifdef NS_ENABLE_HEXDUMP
-static void ns_hexdump(const struct ns_connection *conn, const void *buf,
-                       int len, const char *marker) {
-  const unsigned char *p = (const unsigned char *) buf;
-  char path[500], date[100], ascii[17];
-  FILE *fp;
-
-#if 0
-  if (!match_prefix(NS_ENABLE_HEXDUMP, strlen(NS_ENABLE_HEXDUMP),
-                    conn->remote_ip)) {
-    return;
-  }
+void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags) {
+  union socket_address sa;
+  socklen_t slen = sizeof(sa);
 
-  snprintf(path, sizeof(path), "%s.%hu.txt",
-           conn->mg_conn.remote_ip, conn->mg_conn.remote_port);
+  if (buf != NULL && len > 0) {
+    buf[0] = '\0';
+    memset(&sa, 0, sizeof(sa));
+    if (flags & 4) {
+      getpeername(sock, &sa.sa, &slen);
+    } else {
+      getsockname(sock, &sa.sa, &slen);
+    }
+    if (flags & 1) {
+#if defined(NS_ENABLE_IPV6)
+      inet_ntop(sa.sa.sa_family, sa.sa.sa_family == AF_INET ?
+                (void *) &sa.sin.sin_addr :
+                (void *) &sa.sin6.sin6_addr, buf, len);
+#elif defined(_WIN32)
+      // Only Windoze Vista (and newer) have inet_ntop()
+      strncpy(buf, inet_ntoa(sa.sin.sin_addr), len);
+#else
+      inet_ntop(sa.sa.sa_family, (void *) &sa.sin.sin_addr, buf,(socklen_t)len);
 #endif
-  snprintf(path, sizeof(path), "%p.txt", conn);
-
-  if ((fp = fopen(path, "a")) != NULL) {
-    time_t cur_time = time(NULL);
-    int i, idx;
+    }
+    if (flags & 2) {
+      snprintf(buf + strlen(buf), len - (strlen(buf) + 1), "%s%d",
+               flags & 1 ? ":" : "", (int) ntohs(sa.sin.sin_port));
+    }
+  }
+}
 
-    strftime(date, sizeof(date), "%d/%b/%Y %H:%M:%S", localtime(&cur_time));
-    fprintf(fp, "%s %s %d bytes\n", marker, date, len);
+int ns_hexdump(const void *buf, int len, char *dst, int dst_len) {
+  const unsigned char *p = (const unsigned char *) buf;
+  char ascii[17] = "";
+  int i, idx, n = 0;
 
-    for (i = 0; i < len; i++) {
-      idx = i % 16;
-      if (idx == 0) {
-        if (i > 0) fprintf(fp, "  %s\n", ascii);
-        fprintf(fp, "%04x ", i);
-      }
-      fprintf(fp, " %02x", p[i]);
-      ascii[idx] = p[i] < 0x20 || p[i] > 0x7e ? '.' : p[i];
-      ascii[idx + 1] = '\0';
+  for (i = 0; i < len; i++) {
+    idx = i % 16;
+    if (idx == 0) {
+      if (i > 0) n += snprintf(dst + n, dst_len - n, "  %s\n", ascii);
+      n += snprintf(dst + n, dst_len - n, "%04x ", i);
     }
+    n += snprintf(dst + n, dst_len - n, " %02x", p[i]);
+    ascii[idx] = p[i] < 0x20 || p[i] > 0x7e ? '.' : p[i];
+    ascii[idx + 1] = '\0';
+  }
 
-    while (i++ % 16) fprintf(fp, "%s", "   ");
-    fprintf(fp, "  %s\n\n", ascii);
+  while (i++ % 16) n += snprintf(dst + n, dst_len - n, "%s", "   ");
+  n += snprintf(dst + n, dst_len - n, "  %s\n\n", ascii);
 
-    fclose(fp);
-  }
+  return n;
+}
+
+#ifdef NS_ENABLE_SSL
+static int ns_ssl_err(struct ns_connection *conn, int res) {
+  int ssl_err = SSL_get_error(conn->ssl, res);
+  if (ssl_err == SSL_ERROR_WANT_READ) conn->flags |= NSF_WANT_READ;
+  if (ssl_err == SSL_ERROR_WANT_WRITE) conn->flags |= NSF_WANT_WRITE;
+  return ssl_err;
 }
 #endif
 
@@ -655,11 +880,11 @@ static void ns_read_from_socket(struct ns_connection *conn) {
 #ifdef NS_ENABLE_SSL
     if (ret == 0 && ok == 0 && conn->ssl != NULL) {
       int res = SSL_connect(conn->ssl);
-      int ssl_err = SSL_get_error(conn->ssl, res);
-      DBG(("%p SSL_connect %d %d", conn, res, ssl_err));
+      int ssl_err = ns_ssl_err(conn, res);
       if (res == 1) {
-        conn->flags = NSF_SSL_HANDSHAKE_DONE;
-      } else if (ssl_err == 2 || ssl_err == 3) {
+        conn->flags |= NSF_SSL_HANDSHAKE_DONE;
+      } else if (ssl_err == SSL_ERROR_WANT_READ ||
+                 ssl_err == SSL_ERROR_WANT_WRITE) {
         return; // Call us again
       } else {
         ok = 1;
@@ -678,14 +903,22 @@ static void ns_read_from_socket(struct ns_connection *conn) {
 #ifdef NS_ENABLE_SSL
   if (conn->ssl != NULL) {
     if (conn->flags & NSF_SSL_HANDSHAKE_DONE) {
-      n = SSL_read(conn->ssl, buf, sizeof(buf));
+      // SSL library may have more bytes ready to read then we ask to read.
+      // Therefore, read in a loop until we read everything. Without the loop,
+      // we skip to the next select() cycle which can just timeout.
+      while ((n = SSL_read(conn->ssl, buf, sizeof(buf))) > 0) {
+        DBG(("%p %d <- %d bytes (SSL)", conn, conn->flags, n));
+        iobuf_append(&conn->recv_iobuf, buf, n);
+        ns_call(conn, NS_RECV, &n);
+      }
+      ns_ssl_err(conn, n);
     } else {
       int res = SSL_accept(conn->ssl);
-      int ssl_err = SSL_get_error(conn->ssl, res);
-      DBG(("%p SSL_accept %d %d", conn, res, ssl_err));
+      int ssl_err = ns_ssl_err(conn, res);
       if (res == 1) {
         conn->flags |= NSF_SSL_HANDSHAKE_DONE;
-      } else if (ssl_err == 2 || ssl_err == 3) {
+      } else if (ssl_err == SSL_ERROR_WANT_READ ||
+                 ssl_err == SSL_ERROR_WANT_WRITE) {
         return; // Call us again
       } else {
         conn->flags |= NSF_CLOSE_IMMEDIATELY;
@@ -695,21 +928,15 @@ static void ns_read_from_socket(struct ns_connection *conn) {
   } else
 #endif
   {
-    n = recv(conn->sock, buf, sizeof(buf), 0);
+    while ((n = (int) recv(conn->sock, buf, sizeof(buf), 0)) > 0) {
+      DBG(("%p %d <- %d bytes (PLAIN)", conn, conn->flags, n));
+      iobuf_append(&conn->recv_iobuf, buf, n);
+      ns_call(conn, NS_RECV, &n);
+    }
   }
 
-#ifdef NS_ENABLE_HEXDUMP
-  ns_hexdump(conn, buf, n, "<-");
-#endif
-
-  DBG(("%p <- %d bytes [%.*s%s]",
-       conn, n, n < 40 ? n : 40, buf, n < 40 ? "" : "..."));
-
   if (ns_is_error(n)) {
     conn->flags |= NSF_CLOSE_IMMEDIATELY;
-  } else if (n > 0) {
-    iobuf_append(&conn->recv_iobuf, buf, n);
-    ns_call(conn, NS_RECV, &n);
   }
 }
 
@@ -720,35 +947,56 @@ static void ns_write_to_socket(struct ns_connection *conn) {
 #ifdef NS_ENABLE_SSL
   if (conn->ssl != NULL) {
     n = SSL_write(conn->ssl, io->buf, io->len);
+    if (n <= 0) {
+      int ssl_err = ns_ssl_err(conn, n);
+      if (ssl_err == SSL_ERROR_WANT_READ || ssl_err == SSL_ERROR_WANT_WRITE) {
+        return; // Call us again
+      } else {
+        conn->flags |= NSF_CLOSE_IMMEDIATELY;
+      }
+    }
   } else
 #endif
-  { n = send(conn->sock, io->buf, io->len, 0); }
-
-
-#ifdef NS_ENABLE_HEXDUMP
-  ns_hexdump(conn, io->buf, n, "->");
-#endif
+  { n = (int) send(conn->sock, io->buf, io->len, 0); }
 
-  DBG(("%p -> %d bytes %d [%.*s%s]", conn, n, conn->flags,
-       io->len < 40 ? io->len : 40,
-       io->buf, io->len < 40 ? "" : "..."));
+  DBG(("%p %d -> %d bytes", conn, conn->flags, n));
 
+  ns_call(conn, NS_SEND, &n);
   if (ns_is_error(n)) {
     conn->flags |= NSF_CLOSE_IMMEDIATELY;
   } else if (n > 0) {
     iobuf_remove(io, n);
-    //conn->num_bytes_sent += n;
-  }
-
-  if (io->len == 0 && conn->flags & NSF_FINISHED_SENDING_DATA) {
-    conn->flags |= NSF_CLOSE_IMMEDIATELY;
   }
-
-  ns_call(conn, NS_SEND, NULL);
 }
 
 int ns_send(struct ns_connection *conn, const void *buf, int len) {
-  return iobuf_append(&conn->send_iobuf, buf, len);
+  return (int) ns_out(conn, buf, len);
+}
+
+static void ns_handle_udp(struct ns_connection *ls) {
+  struct ns_connection nc;
+  char buf[NS_UDP_RECEIVE_BUFFER_SIZE];
+  int n;
+  socklen_t s_len = sizeof(nc.sa);
+
+  memset(&nc, 0, sizeof(nc));
+  n = recvfrom(ls->sock, buf, sizeof(buf), 0, &nc.sa.sa, &s_len);
+  if (n <= 0) {
+    DBG(("%p recvfrom: %s", ls, strerror(errno)));
+  } else {
+    nc.mgr = ls->mgr;
+    nc.recv_iobuf.buf = buf;
+    nc.recv_iobuf.len = nc.recv_iobuf.size = n;
+    nc.sock = ls->sock;
+    nc.callback = ls->callback;
+    nc.user_data = ls->user_data;
+    nc.proto_data = ls->proto_data;
+    nc.mgr = ls->mgr;
+    nc.listener = ls;
+    nc.flags = NSF_UDP;
+    DBG(("%p %d bytes received", ls, n));
+    ns_call(&nc, NS_RECV, &n);
+  }
 }
 
 static void ns_add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) {
@@ -760,32 +1008,33 @@ static void ns_add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) {
   }
 }
 
-int ns_server_poll(struct ns_server *server, int milli) {
+time_t ns_mgr_poll(struct ns_mgr *mgr, int milli) {
   struct ns_connection *conn, *tmp_conn;
   struct timeval tv;
   fd_set read_set, write_set;
-  int num_active_connections = 0;
   sock_t max_fd = INVALID_SOCKET;
   time_t current_time = time(NULL);
 
-  if (server->listening_sock == INVALID_SOCKET &&
-      server->active_connections == NULL) return 0;
-
   FD_ZERO(&read_set);
   FD_ZERO(&write_set);
-  ns_add_to_set(server->listening_sock, &read_set, &max_fd);
-  ns_add_to_set(server->ctl[1], &read_set, &max_fd);
+  ns_add_to_set(mgr->ctl[1], &read_set, &max_fd);
 
-  for (conn = server->active_connections; conn != NULL; conn = tmp_conn) {
+  for (conn = mgr->active_connections; conn != NULL; conn = tmp_conn) {
     tmp_conn = conn->next;
-    ns_call(conn, NS_POLL, &current_time);
-    ns_add_to_set(conn->sock, &read_set, &max_fd);
-    if (conn->flags & NSF_CONNECTING) {
-      ns_add_to_set(conn->sock, &write_set, &max_fd);
+    if (!(conn->flags & (NSF_LISTENING | NSF_CONNECTING))) {
+      ns_call(conn, NS_POLL, &current_time);
     }
-    if (conn->send_iobuf.len > 0 && !(conn->flags & NSF_BUFFER_BUT_DONT_SEND)) {
+    if (!(conn->flags & NSF_WANT_WRITE)) {
+      //DBG(("%p read_set", conn));
+      ns_add_to_set(conn->sock, &read_set, &max_fd);
+    }
+    if (((conn->flags & NSF_CONNECTING) && !(conn->flags & NSF_WANT_READ)) ||
+        (conn->send_iobuf.len > 0 && !(conn->flags & NSF_CONNECTING) &&
+         !(conn->flags & NSF_BUFFER_BUT_DONT_SEND))) {
+      //DBG(("%p write_set", conn));
       ns_add_to_set(conn->sock, &write_set, &max_fd);
-    } else if (conn->flags & NSF_CLOSE_IMMEDIATELY) {
+    }
+    if (conn->flags & NSF_CLOSE_IMMEDIATELY) {
       ns_close_conn(conn);
     }
   }
@@ -794,31 +1043,42 @@ int ns_server_poll(struct ns_server *server, int milli) {
   tv.tv_usec = (milli % 1000) * 1000;
 
   if (select((int) max_fd + 1, &read_set, &write_set, NULL, &tv) > 0) {
-    // Accept new connections
-    if (server->listening_sock != INVALID_SOCKET &&
-        FD_ISSET(server->listening_sock, &read_set)) {
-      // We're not looping here, and accepting just one connection at
-      // a time. The reason is that eCos does not respect non-blocking
-      // flag on a listening socket and hangs in a loop.
-      if ((conn = accept_conn(server)) != NULL) {
-        conn->last_io_time = current_time;
+    // select() might have been waiting for a long time, reset current_time
+    // now to prevent last_io_time being set to the past.
+    current_time = time(NULL);
+
+    // Read wakeup messages
+    if (mgr->ctl[1] != INVALID_SOCKET &&
+        FD_ISSET(mgr->ctl[1], &read_set)) {
+      struct ctl_msg ctl_msg;
+      int len = (int) recv(mgr->ctl[1], (char *) &ctl_msg, sizeof(ctl_msg), 0);
+      send(mgr->ctl[1], ctl_msg.message, 1, 0);
+      if (len >= (int) sizeof(ctl_msg.callback) && ctl_msg.callback != NULL) {
+        struct ns_connection *c;
+        for (c = ns_next(mgr, NULL); c != NULL; c = ns_next(mgr, c)) {
+          ctl_msg.callback(c, NS_POLL, ctl_msg.message);
+        }
       }
     }
 
-    // Read possible wakeup calls
-    if (server->ctl[1] != INVALID_SOCKET &&
-        FD_ISSET(server->ctl[1], &read_set)) {
-      unsigned char ch;
-      recv(server->ctl[1], &ch, 1, 0);
-      send(server->ctl[1], &ch, 1, 0);
-    }
-
-    for (conn = server->active_connections; conn != NULL; conn = tmp_conn) {
+    for (conn = mgr->active_connections; conn != NULL; conn = tmp_conn) {
       tmp_conn = conn->next;
       if (FD_ISSET(conn->sock, &read_set)) {
-        conn->last_io_time = current_time;
-        ns_read_from_socket(conn);
+        if (conn->flags & NSF_LISTENING) {
+          if (conn->flags & NSF_UDP) {
+            ns_handle_udp(conn);
+          } else {
+            // We're not looping here, and accepting just one connection at
+            // a time. The reason is that eCos does not respect non-blocking
+            // flag on a listening socket and hangs in a loop.
+            accept_conn(conn);
+          }
+        } else {
+          conn->last_io_time = current_time;
+          ns_read_from_socket(conn);
+        }
       }
+
       if (FD_ISSET(conn->sock, &write_set)) {
         if (conn->flags & NSF_CONNECTING) {
           ns_read_from_socket(conn);
@@ -830,76 +1090,72 @@ int ns_server_poll(struct ns_server *server, int milli) {
     }
   }
 
-  for (conn = server->active_connections; conn != NULL; conn = tmp_conn) {
+  for (conn = mgr->active_connections; conn != NULL; conn = tmp_conn) {
     tmp_conn = conn->next;
-    num_active_connections++;
-    if (conn->flags & NSF_CLOSE_IMMEDIATELY) {
+    if ((conn->flags & NSF_CLOSE_IMMEDIATELY) ||
+        (conn->send_iobuf.len == 0 &&
+          (conn->flags & NSF_FINISHED_SENDING_DATA))) {
       ns_close_conn(conn);
     }
   }
-  //DBG(("%d active connections", num_active_connections));
 
-  return num_active_connections;
+  return current_time;
 }
 
-struct ns_connection *ns_connect(struct ns_server *server, const char *host,
-                                 int port, int use_ssl, void *param) {
+struct ns_connection *ns_connect(struct ns_mgr *mgr, const char *address,
+                                 ns_callback_t callback, void *user_data) {
   sock_t sock = INVALID_SOCKET;
-  struct sockaddr_in sin;
-  struct hostent *he = NULL;
-  struct ns_connection *conn = NULL;
-  int connect_ret_val;
+  struct ns_connection *nc = NULL;
+  union socket_address sa;
+  char cert[100], ca_cert[100];
+  int rc, use_ssl, proto;
 
-  (void) use_ssl;
-  if (host == NULL || (he = gethostbyname(host)) == NULL ||
-      (sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
-    DBG(("gethostbyname(%s) failed: %s", host, strerror(errno)));
+  ns_parse_address(address, &sa, &proto, &use_ssl, cert, ca_cert);
+  if ((sock = socket(AF_INET, proto, 0)) == INVALID_SOCKET) {
     return NULL;
   }
-
-  sin.sin_family = AF_INET;
-  sin.sin_port = htons((uint16_t) port);
-  sin.sin_addr = * (struct in_addr *) he->h_addr_list[0];
   ns_set_non_blocking_mode(sock);
+  rc = (proto == SOCK_DGRAM) ? 0 : connect(sock, &sa.sa, sizeof(sa.sin));
 
-  connect_ret_val = connect(sock, (struct sockaddr *) &sin, sizeof(sin));
-  if (ns_is_error(connect_ret_val)) {
+  if (rc != 0 && ns_is_error(rc)) {
     closesocket(sock);
     return NULL;
-  } else if ((conn = (struct ns_connection *)
-              NS_MALLOC(sizeof(*conn))) == NULL) {
+  } else if ((nc = ns_add_sock(mgr, sock, callback, user_data)) == NULL) {
     closesocket(sock);
     return NULL;
   }
 
-  memset(conn, 0, sizeof(*conn));
-  conn->server = server;
-  conn->sock = sock;
-  conn->connection_data = param;
-  conn->flags = NSF_CONNECTING;
-  conn->last_io_time = time(NULL);
+  nc->sa = sa;   // Important, cause UDP conns will use sendto()
+  nc->flags = (proto == SOCK_DGRAM) ? NSF_UDP : NSF_CONNECTING;
 
 #ifdef NS_ENABLE_SSL
-  if (use_ssl &&
-      (conn->ssl = SSL_new(server->client_ssl_ctx)) != NULL) {
-    SSL_set_fd(conn->ssl, sock);
+  if (use_ssl) {
+    if ((nc->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL ||
+        ns_use_cert(nc->ssl_ctx, cert) != 0 ||
+        ns_use_ca_cert(nc->ssl_ctx, ca_cert) != 0 ||
+        (nc->ssl = SSL_new(nc->ssl_ctx)) == NULL) {
+      ns_close_conn(nc);
+      return NULL;
+    } else {
+      SSL_set_fd(nc->ssl, sock);
+    }
   }
 #endif
 
-  ns_add_conn(server, conn);
-  DBG(("%p %s:%d %d %p", conn, host, port, conn->sock, conn->ssl));
-
-  return conn;
+  return nc;
 }
 
-struct ns_connection *ns_add_sock(struct ns_server *s, sock_t sock, void *p) {
+struct ns_connection *ns_add_sock(struct ns_mgr *s, sock_t sock,
+                                  ns_callback_t callback, void *user_data) {
   struct ns_connection *conn;
   if ((conn = (struct ns_connection *) NS_MALLOC(sizeof(*conn))) != NULL) {
     memset(conn, 0, sizeof(*conn));
     ns_set_non_blocking_mode(sock);
+    ns_set_close_on_exec(sock);
     conn->sock = sock;
-    conn->connection_data = p;
-    conn->server = s;
+    conn->user_data = user_data;
+    conn->callback = callback;
+    conn->mgr = s;
     conn->last_io_time = time(NULL);
     ns_add_conn(s, conn);
     DBG(("%p %d", conn, sock));
@@ -907,28 +1163,26 @@ struct ns_connection *ns_add_sock(struct ns_server *s, sock_t sock, void *p) {
   return conn;
 }
 
-void ns_iterate(struct ns_server *server, ns_callback_t cb, void *param) {
-  struct ns_connection *conn, *tmp_conn;
-
-  for (conn = server->active_connections; conn != NULL; conn = tmp_conn) {
-    tmp_conn = conn->next;
-    cb(conn, NS_POLL, param);
-  }
+struct ns_connection *ns_next(struct ns_mgr *s, struct ns_connection *conn) {
+  return conn == NULL ? s->active_connections : conn->next;
 }
 
-void ns_server_wakeup(struct ns_server *server) {
-  unsigned char ch = 0;
-  if (server->ctl[0] != INVALID_SOCKET) {
-    send(server->ctl[0], &ch, 1, 0);
-    recv(server->ctl[0], &ch, 1, 0);
+void ns_broadcast(struct ns_mgr *mgr, ns_callback_t cb,void *data, size_t len) {
+  struct ctl_msg ctl_msg;
+  if (mgr->ctl[0] != INVALID_SOCKET && data != NULL &&
+      len < sizeof(ctl_msg.message)) {
+    ctl_msg.callback = cb;
+    memcpy(ctl_msg.message, data, len);
+    send(mgr->ctl[0], (char *) &ctl_msg,
+         offsetof(struct ctl_msg, message) + len, 0);
+    recv(mgr->ctl[0], (char *) &len, 1, 0);
   }
 }
 
-void ns_server_init(struct ns_server *s, void *server_data, ns_callback_t cb) {
+void ns_mgr_init(struct ns_mgr *s, void *user_data) {
   memset(s, 0, sizeof(*s));
-  s->listening_sock = s->ctl[0] = s->ctl[1] = INVALID_SOCKET;
-  s->server_data = server_data;
-  s->callback = cb;
+  s->ctl[0] = s->ctl[1] = INVALID_SOCKET;
+  s->user_data = user_data;
 
 #ifdef _WIN32
   { WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); }
@@ -940,40 +1194,32 @@ void ns_server_init(struct ns_server *s, void *server_data, ns_callback_t cb) {
 
 #ifndef NS_DISABLE_SOCKETPAIR
   do {
-    ns_socketpair(s->ctl);
+    ns_socketpair2(s->ctl, SOCK_DGRAM);
   } while (s->ctl[0] == INVALID_SOCKET);
 #endif
 
 #ifdef NS_ENABLE_SSL
-  SSL_library_init();
-  s->client_ssl_ctx = SSL_CTX_new(SSLv23_client_method());
+  {static int init_done; if (!init_done) { SSL_library_init(); init_done++; }}
 #endif
 }
 
-void ns_server_free(struct ns_server *s) {
+void ns_mgr_free(struct ns_mgr *s) {
   struct ns_connection *conn, *tmp_conn;
 
   DBG(("%p", s));
   if (s == NULL) return;
   // Do one last poll, see https://github.com/cesanta/mongoose/issues/286
-  ns_server_poll(s, 0);
+  ns_mgr_poll(s, 0);
 
-  if (s->listening_sock != INVALID_SOCKET) closesocket(s->listening_sock);
   if (s->ctl[0] != INVALID_SOCKET) closesocket(s->ctl[0]);
   if (s->ctl[1] != INVALID_SOCKET) closesocket(s->ctl[1]);
-  s->listening_sock = s->ctl[0] = s->ctl[1] = INVALID_SOCKET;
+  s->ctl[0] = s->ctl[1] = INVALID_SOCKET;
 
   for (conn = s->active_connections; conn != NULL; conn = tmp_conn) {
     tmp_conn = conn->next;
     ns_close_conn(conn);
   }
-
-#ifdef NS_ENABLE_SSL
-  if (s->ssl_ctx != NULL) SSL_CTX_free(s->ssl_ctx);
-  if (s->client_ssl_ctx != NULL) SSL_CTX_free(s->client_ssl_ctx);
-#endif
 }
-
 // net_skeleton end
 #endif  // NOEMBED_NET_SKELETON
 
@@ -985,35 +1231,52 @@ void ns_server_free(struct ns_server *s) {
 #ifndef S_ISDIR
 #define S_ISDIR(x) ((x) & _S_IFDIR)
 #endif
-#define sleep(x) Sleep((x) * 1000)
+#ifdef stat
+#undef stat
+#endif
+#ifdef lseek
+#undef lseek
+#endif
+#ifdef popen
+#undef popen
+#endif
+#ifdef pclose
+#undef pclose
+#endif
 #define stat(x, y) mg_stat((x), (y))
 #define fopen(x, y) mg_fopen((x), (y))
-#define open(x, y) mg_open((x), (y))
+#define open(x, y, z) mg_open((x), (y), (z))
+#define close(x) _close(x)
 #define lseek(x, y, z) _lseeki64((x), (y), (z))
 #define popen(x, y) _popen((x), (y))
 #define pclose(x) _pclose(x)
 #define mkdir(x, y) _mkdir(x)
-#define to64(x) _atoi64(x)
 #ifndef __func__
 #define STRX(x) #x
 #define STR(x) STRX(x)
 #define __func__ __FILE__ ":" STR(__LINE__)
 #endif
-#define INT64_FMT  "I64d"
-#define stat(x, y) mg_stat((x), (y))
-#define fopen(x, y) mg_fopen((x), (y))
-#define open(x, y) mg_open((x), (y))
+/* MINGW has adopted the MSVC formatting for 64-bit ints as of gcc 4.4 till 4.8*/
+#if (defined(__MINGW32__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4 && __GNUC_MINOR__ < 8))) || defined(_MSC_VER)
+#define INT64_FMT   "I64d"
+#else
+#define INT64_FMT   "lld"
+#endif
 #define flockfile(x)      ((void) (x))
 #define funlockfile(x)    ((void) (x))
 typedef struct _stati64 file_stat_t;
-typedef HANDLE pid_t;
+typedef HANDLE process_id_t;
+
 #else                    ////////////// UNIX specific defines and includes
+
 #include <dirent.h>
+#include <dlfcn.h>
 #include <inttypes.h>
 #include <pwd.h>
 #define O_BINARY 0
 #define INT64_FMT PRId64
 typedef struct stat file_stat_t;
+typedef pid_t process_id_t;
 #endif                  //////// End of platform-specific defines and includes
 
 #include "mongoose.h"
@@ -1021,7 +1284,6 @@ typedef struct stat file_stat_t;
 #define MAX_REQUEST_SIZE 16384
 #define IOBUF_SIZE 8192
 #define MAX_PATH_SIZE 8192
-#define LUA_SCRIPT_PATTERN "**.lp$"
 #define DEFAULT_CGI_PATTERN "**.cgi$|**.pl$|**.php$"
 #define CGI_ENVIRONMENT_SIZE 8192
 #define MAX_CGI_ENVIR_VARS 64
@@ -1042,10 +1304,10 @@ typedef struct stat file_stat_t;
 #endif
 
 #ifndef MONGOOSE_IDLE_TIMEOUT_SECONDS
-#define MONGOOSE_IDLE_TIMEOUT_SECONDS 30
+#define MONGOOSE_IDLE_TIMEOUT_SECONDS 300
 #endif
 
-#ifdef MONGOOSE_NO_SOCKETPAIR
+#ifdef NS_DISABLE_SOCKETPAIR
 #define MONGOOSE_NO_CGI
 #endif
 
@@ -1056,6 +1318,7 @@ typedef struct stat file_stat_t;
 #define MONGOOSE_NO_DIRECTORY_LISTING
 #define MONGOOSE_NO_LOGGING
 #define MONGOOSE_NO_SSI
+#define MONGOOSE_NO_DL
 #endif
 
 struct vec {
@@ -1082,20 +1345,20 @@ enum {
   CGI_INTERPRETER,
   CGI_PATTERN,
 #endif
-#ifndef MONGOOSE_NO_DAV
   DAV_AUTH_FILE,
-#endif
   DOCUMENT_ROOT,
 #ifndef MONGOOSE_NO_DIRECTORY_LISTING
   ENABLE_DIRECTORY_LISTING,
 #endif
 #endif
+  ENABLE_PROXY,
   EXTRA_MIME_TYPES,
 #if !defined(MONGOOSE_NO_FILESYSTEM) && !defined(MONGOOSE_NO_AUTH)
   GLOBAL_AUTH_FILE,
 #endif
-  HIDE_FILES_PATTERN,
 #ifndef MONGOOSE_NO_FILESYSTEM
+  HIDE_FILES_PATTERN,
+  HEXDUMP_FILE,
   INDEX_FILES,
 #endif
   LISTENING_PORT,
@@ -1104,9 +1367,6 @@ enum {
 #endif
 #ifndef MONGOOSE_NO_SSI
   SSI_PATTERN,
-#endif
-#ifdef NS_ENABLE_SSL
-  SSL_CERTIFICATE,
 #endif
   URL_REWRITES,
   NUM_OPTIONS
@@ -1123,21 +1383,21 @@ static const char *static_config_options[] = {
   "cgi_interpreter", NULL,
   "cgi_pattern", DEFAULT_CGI_PATTERN,
 #endif
-#ifndef MONGOOSE_NO_DAV
   "dav_auth_file", NULL,
-#endif
   "document_root",  NULL,
 #ifndef MONGOOSE_NO_DIRECTORY_LISTING
   "enable_directory_listing", "yes",
 #endif
 #endif
+  "enable_proxy", NULL,
   "extra_mime_types", NULL,
 #if !defined(MONGOOSE_NO_FILESYSTEM) && !defined(MONGOOSE_NO_AUTH)
   "global_auth_file", NULL,
 #endif
-  "hide_files_patterns", NULL,
 #ifndef MONGOOSE_NO_FILESYSTEM
-  "index_files","index.html,index.htm,index.shtml,index.cgi,index.php,index.lp",
+  "hide_files_patterns", NULL,
+  "hexdump_file", NULL,
+  "index_files","index.html,index.htm,index.shtml,index.cgi,index.php",
 #endif
   "listening_port", NULL,
 #ifndef _WIN32
@@ -1145,47 +1405,45 @@ static const char *static_config_options[] = {
 #endif
 #ifndef MONGOOSE_NO_SSI
   "ssi_pattern", "**.shtml$|**.shtm$",
-#endif
-#ifdef NS_ENABLE_SSL
-  "ssl_certificate", NULL,
 #endif
   "url_rewrites", NULL,
   NULL
 };
 
 struct mg_server {
-  struct ns_server ns_server;
+  struct ns_mgr ns_mgr;
   union socket_address lsa;   // Listening socket address
   mg_handler_t event_handler;
   char *config_options[NUM_OPTIONS];
-  char local_ip[48];
 };
 
 // Local endpoint representation
 union endpoint {
-  int fd;                           // Opened regular local file
-  struct ns_connection *cgi_conn;   // CGI socket
+  int fd;                     // Opened regular local file
+  struct ns_connection *nc;   // CGI or proxy->target connection
 };
 
-enum endpoint_type { EP_NONE, EP_FILE, EP_CGI, EP_USER, EP_PUT, EP_CLIENT };
+enum endpoint_type {
+ EP_NONE, EP_FILE, EP_CGI, EP_USER, EP_PUT, EP_CLIENT, EP_PROXY
+};
 
 #define MG_HEADERS_SENT NSF_USER_1
 #define MG_LONG_RUNNING NSF_USER_2
 #define MG_CGI_CONN NSF_USER_3
+#define MG_PROXY_CONN NSF_USER_4
+#define MG_PROXY_DONT_PARSE NSF_USER_5
 
 struct connection {
-  struct ns_connection *ns_conn;
+  struct ns_connection *ns_conn;  // NOTE(lsm): main.c depends on this order
   struct mg_connection mg_conn;
   struct mg_server *server;
   union endpoint endpoint;
   enum endpoint_type endpoint_type;
   char *path_info;
   char *request;
-  int64_t num_bytes_sent; // Total number of bytes sent
+  int64_t num_bytes_recv; // Total number of bytes received
   int64_t cl;             // Reply content length, for Range support
   int request_len;  // Request length, including last \r\n after last header
-  //int flags;        // CONN_* flags: CONN_CLOSE, CONN_SPOOL_DONE, etc
-  //mg_handler_t handler;  // Callback for HTTP client
 };
 
 #define MG_CONN_2_CONN(c) ((struct connection *) ((char *) (c) - \
@@ -1193,6 +1451,7 @@ struct connection {
 
 static void open_local_endpoint(struct connection *conn, int skip_user);
 static void close_local_endpoint(struct connection *conn);
+static void mg_ev_handler(struct ns_connection *nc, int ev, void *p);
 
 static const struct {
   const char *extension;
@@ -1220,7 +1479,7 @@ static const struct {
   {".ogg", 4, "application/ogg"},
   {".ram", 4, "audio/x-pn-realaudio"},
   {".xml", 4, "text/xml"},
-  {".json",  5, "text/json"},
+  {".json",  5, "application/json"},
   {".xslt", 5, "application/xml"},
   {".xsl", 4, "application/xml"},
   {".ra",  3, "audio/x-pn-realaudio"},
@@ -1232,7 +1491,7 @@ static const struct {
   {".tar", 4, "application/x-tar"},
   {".gz",  3, "application/x-gunzip"},
   {".arj", 4, "application/x-arj-compressed"},
-  {".rar", 4, "application/x-arj-compressed"},
+  {".rar", 4, "application/x-rar-compressed"},
   {".rtf", 4, "application/rtf"},
   {".pdf", 4, "application/pdf"},
   {".swf", 4, "application/x-shockwave-flash"},
@@ -1255,17 +1514,44 @@ void *mg_start_thread(void *(*f)(void *), void *p) {
 }
 #endif  // MONGOOSE_NO_THREADS
 
+#ifndef MONGOOSE_NO_MMAP
 #ifdef _WIN32
-#ifndef MONGOOSE_NO_FILESYSTEM
-// Encode 'path' which is assumed UTF-8 string, into UNICODE string.
-// wbuf and wbuf_len is a target buffer and its length.
-static void to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len) {
-  char buf[MAX_PATH_SIZE * 2], buf2[MAX_PATH_SIZE * 2], *p;
-
-  strncpy(buf, path, sizeof(buf));
-  buf[sizeof(buf) - 1] = '\0';
-
-  // Trim trailing slashes. Leave backslash for paths like "X:\"
+static void *mmap(void *addr, int64_t len, int prot, int flags, int fd,
+                  int offset) {
+  HANDLE fh = (HANDLE) _get_osfhandle(fd);
+  HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0);
+  void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t) len);
+  CloseHandle(mh);
+  return p;
+}
+#define munmap(x, y)  UnmapViewOfFile(x)
+#define MAP_FAILED NULL
+#define MAP_PRIVATE 0
+#define PROT_READ 0
+#else
+#include <sys/mman.h>
+#endif
+
+void *mg_mmap(FILE *fp, size_t size) {
+  void *p = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
+  return p == MAP_FAILED ? NULL : p;
+}
+
+void mg_munmap(void *p, size_t size) {
+  munmap(p, size);
+}
+#endif  // MONGOOSE_NO_MMAP
+
+#if defined(_WIN32) && !defined(MONGOOSE_NO_FILESYSTEM)
+// Encode 'path' which is assumed UTF-8 string, into UNICODE string.
+// wbuf and wbuf_len is a target buffer and its length.
+static void to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len) {
+  char buf[MAX_PATH_SIZE * 2], buf2[MAX_PATH_SIZE * 2], *p;
+
+  strncpy(buf, path, sizeof(buf));
+  buf[sizeof(buf) - 1] = '\0';
+
+  // Trim trailing slashes. Leave backslash for paths like "X:\"
   p = buf + strlen(buf) - 1;
   while (p > buf && p[-1] != ':' && (p[0] == '\\' || p[0] == '/')) *p-- = '\0';
 
@@ -1294,13 +1580,12 @@ static FILE *mg_fopen(const char *path, const char *mode) {
   return _wfopen(wpath, wmode);
 }
 
-static int mg_open(const char *path, int flag) {
+static int mg_open(const char *path, int flag, int mode) {
   wchar_t wpath[MAX_PATH_SIZE];
   to_wchar(path, wpath, ARRAY_SIZE(wpath));
-  return _wopen(wpath, flag);
+  return _wopen(wpath, flag, mode);
 }
-#endif
-#endif // MONGOOSE_NO_FILESYSTEM
+#endif // _WIN32 && !MONGOOSE_NO_FILESYSTEM
 
 // A helper function for traversing a comma separated list of values.
 // It returns a list pointer shifted to the next value, or NULL if the end
@@ -1424,23 +1709,72 @@ static void parse_http_headers(char **buf, struct mg_connection *ri) {
 
 static const char *status_code_to_str(int status_code) {
   switch (status_code) {
+
+    case 100: return "Continue";
+    case 101: return "Switching Protocols";
+    case 102: return "Processing";
+
     case 200: return "OK";
     case 201: return "Created";
+    case 202: return "Accepted";
+    case 203: return "Non-Authoritative Information";
     case 204: return "No Content";
+    case 205: return "Reset Content";
+    case 206: return "Partial Content";
+    case 207: return "Multi-Status";
+    case 208: return "Already Reported";
+    case 226: return "IM Used";
+
+    case 300: return "Multiple Choices";
     case 301: return "Moved Permanently";
     case 302: return "Found";
+    case 303: return "See Other";
     case 304: return "Not Modified";
+    case 305: return "Use Proxy";
+    case 306: return "Switch Proxy";
+    case 307: return "Temporary Redirect";
+    case 308: return "Permanent Redirect";
+
     case 400: return "Bad Request";
+    case 401: return "Unauthorized";
+    case 402: return "Payment Required";
     case 403: return "Forbidden";
     case 404: return "Not Found";
     case 405: return "Method Not Allowed";
+    case 406: return "Not Acceptable";
+    case 407: return "Proxy Authentication Required";
+    case 408: return "Request Timeout";
     case 409: return "Conflict";
+    case 410: return "Gone";
     case 411: return "Length Required";
-    case 413: return "Request Entity Too Large";
+    case 412: return "Precondition Failed";
+    case 413: return "Payload Too Large";
+    case 414: return "URI Too Long";
     case 415: return "Unsupported Media Type";
+    case 416: return "Requested Range Not Satisfiable";
+    case 417: return "Expectation Failed";
+    case 418: return "I\'m a teapot";
+    case 422: return "Unprocessable Entity";
     case 423: return "Locked";
-    case 500: return "Server Error";
+    case 424: return "Failed Dependency";
+    case 426: return "Upgrade Required";
+    case 428: return "Precondition Required";
+    case 429: return "Too Many Requests";
+    case 431: return "Request Header Fields Too Large";
+    case 451: return "Unavailable For Legal Reasons";
+
+    case 500: return "Internal Server Error";
     case 501: return "Not Implemented";
+    case 502: return "Bad Gateway";
+    case 503: return "Service Unavailable";
+    case 504: return "Gateway Timeout";
+    case 505: return "HTTP Version Not Supported";
+    case 506: return "Variant Also Negotiates";
+    case 507: return "Insufficient Storage";
+    case 508: return "Loop Detected";
+    case 510: return "Not Extended";
+    case 511: return "Network Authentication Required";
+
     default:  return "Server Error";
   }
 }
@@ -1488,7 +1822,7 @@ static void send_http_error(struct connection *conn, int code,
     body_len += mg_vsnprintf(body + body_len, sizeof(body) - body_len, fmt, ap);
     va_end(ap);
   }
-  if (code >= 300 && code <= 399) {
+  if ((code >= 300 && code <= 399) || code == 204) {
     // 3xx errors do not have body
     body_len = 0;
   }
@@ -1509,16 +1843,21 @@ static void write_chunk(struct connection *conn, const char *buf, int len) {
   ns_send(conn->ns_conn, "\r\n", 2);
 }
 
-int mg_printf(struct mg_connection *conn, const char *fmt, ...) {
+size_t mg_printf(struct mg_connection *conn, const char *fmt, ...) {
   struct connection *c = MG_CONN_2_CONN(conn);
-  int len;
   va_list ap;
 
   va_start(ap, fmt);
-  len = ns_vprintf(c->ns_conn, fmt, ap);
+  ns_vprintf(c->ns_conn, fmt, ap);
   va_end(ap);
 
-  return len;
+  return c->ns_conn->send_iobuf.len;
+}
+
+static void ns_forward(struct ns_connection *from, struct ns_connection *to) {
+  DBG(("%p -> %p %lu bytes", from, to, (unsigned long)from->recv_iobuf.len));
+  ns_send(to, from->recv_iobuf.buf, from->recv_iobuf.len);
+  iobuf_remove(&from->recv_iobuf, from->recv_iobuf.len);
 }
 
 #ifndef MONGOOSE_NO_CGI
@@ -1537,7 +1876,7 @@ static int wait_until_ready(sock_t sock, int for_read) {
 }
 
 static void *push_to_stdin(void *arg) {
-  struct threadparam *tp = arg;
+  struct threadparam *tp = (struct threadparam *)arg;
   int n, sent, stop = 0;
   DWORD k;
   char buf[IOBUF_SIZE];
@@ -1557,8 +1896,8 @@ static void *push_to_stdin(void *arg) {
 }
 
 static void *pull_from_stdout(void *arg) {
-  struct threadparam *tp = arg;
-  int k, stop = 0;
+  struct threadparam *tp = (struct threadparam *)arg;
+  int k = 0, stop = 0;
   DWORD n, sent;
   char buf[IOBUF_SIZE];
 
@@ -1579,7 +1918,7 @@ static void *pull_from_stdout(void *arg) {
 
 static void spawn_stdio_thread(sock_t sock, HANDLE hPipe,
                                void *(*func)(void *)) {
-  struct threadparam *tp = malloc(sizeof(*tp));
+  struct threadparam *tp = (struct threadparam *)malloc(sizeof(*tp));
   if (tp != NULL) {
     tp->s = sock;
     tp->hPipe = hPipe;
@@ -1594,10 +1933,11 @@ static void abs_path(const char *utf8_path, char *abs_path, size_t len) {
   WideCharToMultiByte(CP_UTF8, 0, buf2, wcslen(buf2) + 1, abs_path, len, 0, 0);
 }
 
-static pid_t start_process(char *interp, const char *cmd, const char *env,
-                           const char *envp[], const char *dir, sock_t sock) {
-  STARTUPINFOW si = {0};
-  PROCESS_INFORMATION pi = {0};
+static process_id_t start_process(char *interp, const char *cmd,
+                                  const char *env, const char *envp[],
+                                  const char *dir, sock_t sock) {
+  STARTUPINFOW si;
+  PROCESS_INFORMATION pi;
   HANDLE a[2], b[2], me = GetCurrentProcess();
   wchar_t wcmd[MAX_PATH_SIZE], full_dir[MAX_PATH_SIZE];
   char buf[MAX_PATH_SIZE], buf4[MAX_PATH_SIZE], buf5[MAX_PATH_SIZE],
@@ -1605,6 +1945,9 @@ static pid_t start_process(char *interp, const char *cmd, const char *env,
   DWORD flags = DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS;
   FILE *fp;
 
+  memset(&si, 0, sizeof(si));
+  memset(&pi, 0, sizeof(pi));
+
   si.cb = sizeof(si);
   si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
   si.wShowWindow = SW_HIDE;
@@ -1621,7 +1964,7 @@ static pid_t start_process(char *interp, const char *cmd, const char *env,
     buf[sizeof(buf) - 1] = '\0';
     if (buf[0] == '#' && buf[1] == '!') {
       interp = buf + 2;
-      for (p = interp + strlen(interp);
+      for (p = interp + strlen(interp) - 1;
            isspace(* (uint8_t *) p) && p > interp; p--) *p = '\0';
     }
     fclose(fp);
@@ -1648,20 +1991,20 @@ static pid_t start_process(char *interp, const char *cmd, const char *env,
   }
   DBG(("CGI command: [%ls] -> %p", wcmd, pi.hProcess));
 
+  // Not closing a[0] and b[1] because we've used DUPLICATE_CLOSE_SOURCE
   CloseHandle(si.hStdOutput);
   CloseHandle(si.hStdInput);
-  CloseHandle(a[0]);
-  CloseHandle(b[1]);
-  CloseHandle(pi.hThread);
-  CloseHandle(pi.hProcess);
+  //CloseHandle(pi.hThread);
+  //CloseHandle(pi.hProcess);
 
-  return (pid_t) pi.hProcess;
+  return pi.hProcess;
 }
 #else
-static pid_t start_process(const char *interp, const char *cmd, const char *env,
-                           const char *envp[], const char *dir, sock_t sock) {
+static process_id_t start_process(const char *interp, const char *cmd,
+                                  const char *env, const char *envp[],
+                                  const char *dir, sock_t sock) {
   char buf[500];
-  pid_t pid = fork();
+  process_id_t pid = fork();
   (void) env;
 
   if (pid == 0) {
@@ -1677,9 +2020,9 @@ static pid_t start_process(const char *interp, const char *cmd, const char *env,
     signal(SIGCHLD, SIG_DFL);
 
     if (interp == NULL) {
-      execle(cmd, cmd, NULL, envp);
+      execle(cmd, cmd, (char *) 0, envp); // Using (char *) 0 to avoid warning
     } else {
-      execle(interp, interp, cmd, NULL, envp);
+      execle(interp, interp, cmd, (char *) 0, envp);
     }
     snprintf(buf, sizeof(buf), "Status: 500\r\n\r\n"
              "500 Server Error: %s%s%s: %s", interp == NULL ? "" : interp,
@@ -1758,7 +2101,7 @@ static void prepare_cgi_environment(struct connection *conn,
   if ((s = getenv("SERVER_NAME")) != NULL) {
     addenv(blk, "SERVER_NAME=%s", s);
   } else {
-    addenv(blk, "SERVER_NAME=%s", conn->server->local_ip);
+    addenv(blk, "SERVER_NAME=%s", ri->local_ip);
   }
   addenv(blk, "SERVER_ROOT=%s", opts[DOCUMENT_ROOT]);
   addenv(blk, "DOCUMENT_ROOT=%s", opts[DOCUMENT_ROOT]);
@@ -1870,14 +2213,17 @@ static void open_cgi_endpoint(struct connection *conn, const char *prog) {
   } while (fds[0] == INVALID_SOCKET);
 
   if (start_process(conn->server->config_options[CGI_INTERPRETER],
-                    prog, blk.buf, blk.vars, dir, fds[1]) > 0) {
+                    prog, blk.buf, blk.vars, dir, fds[1]) != 0) {
     conn->endpoint_type = EP_CGI;
-    conn->endpoint.cgi_conn = ns_add_sock(&conn->server->ns_server,
-                                          fds[0], conn);
-    conn->endpoint.cgi_conn->flags |= MG_CGI_CONN;
+    conn->endpoint.nc = ns_add_sock(&conn->server->ns_mgr, fds[0],
+                                    mg_ev_handler, conn);
+    conn->endpoint.nc->flags |= MG_CGI_CONN;
     ns_send(conn->ns_conn, cgi_status, sizeof(cgi_status) - 1);
     conn->mg_conn.status_code = 200;
     conn->ns_conn->flags |= NSF_BUFFER_BUT_DONT_SEND;
+    // Pass POST data to the CGI process
+    conn->endpoint.nc->send_iobuf = conn->ns_conn->recv_iobuf;
+    iobuf_init(&conn->ns_conn->recv_iobuf, 0);
   } else {
     closesocket(fds[0]);
     send_http_error(conn, 500, "start_process(%s) failed", prog);
@@ -1889,15 +2235,14 @@ static void open_cgi_endpoint(struct connection *conn, const char *prog) {
 }
 
 static void on_cgi_data(struct ns_connection *nc) {
-  struct connection *conn = (struct connection *) nc->connection_data;
+  struct connection *conn = (struct connection *) nc->user_data;
   const char *status = "500";
   struct mg_connection c;
 
   if (!conn) return;
 
   // Copy CGI data from CGI socket to the client send buffer
-  ns_send(conn->ns_conn, nc->recv_iobuf.buf, nc->recv_iobuf.len);
-  iobuf_remove(&nc->recv_iobuf, nc->recv_iobuf.len);
+  ns_forward(nc, conn->ns_conn);
 
   // If reply has not been parsed yet, parse it
   if (conn->ns_conn->flags & NSF_BUFFER_BUT_DONT_SEND) {
@@ -1909,8 +2254,10 @@ static void on_cgi_data(struct ns_connection *nc) {
     if (len == 0) return;
 
     if (len < 0 || len > (int) sizeof(buf)) {
+      len = io->len;
       iobuf_remove(io, io->len);
-      send_http_error(conn, 500, "%s", "CGI program sent malformed headers");
+      send_http_error(conn, 500, "CGI program sent malformed headers: [%.*s]",
+        len, io->buf);
     } else {
       memset(&c, 0, sizeof(c));
       memcpy(buf, io->buf + s_len, len);
@@ -1927,14 +2274,6 @@ static void on_cgi_data(struct ns_connection *nc) {
     conn->ns_conn->flags &= ~NSF_BUFFER_BUT_DONT_SEND;
   }
 }
-
-static void forward_post_data(struct connection *conn) {
-  struct iobuf *io = &conn->ns_conn->recv_iobuf;
-  if (conn->endpoint.cgi_conn != NULL) {
-    ns_send(conn->endpoint.cgi_conn, io->buf, io->len);
-    iobuf_remove(io, io->len);
-  }
-}
 #endif  // !MONGOOSE_NO_CGI
 
 static char *mg_strdup(const char *str) {
@@ -1989,21 +2328,6 @@ static int check_acl(const char *acl, uint32_t remote_ip) {
   return allowed == '+';
 }
 
-static void sockaddr_to_string(char *buf, size_t len,
-                               const union socket_address *usa) {
-  buf[0] = '\0';
-#if defined(NS_ENABLE_IPV6)
-  inet_ntop(usa->sa.sa_family, usa->sa.sa_family == AF_INET ?
-            (void *) &usa->sin.sin_addr :
-            (void *) &usa->sin6.sin6_addr, buf, len);
-#elif defined(_WIN32)
-  // Only Windoze Vista (and newer) have inet_ntop()
-  strncpy(buf, inet_ntoa(usa->sin.sin_addr), len);
-#else
-  inet_ntop(usa->sa.sa_family, (void *) &usa->sin.sin_addr, buf, len);
-#endif
-}
-
 // Protect against directory disclosure attack by removing '..',
 // excessive '/' and '\' characters
 static void remove_double_dots_and_double_slashes(char *s) {
@@ -2048,12 +2372,10 @@ int mg_url_decode(const char *src, int src_len, char *dst,
   return i >= src_len ? j : -1;
 }
 
-static int is_valid_http_method(const char *method) {
-  return !strcmp(method, "GET") || !strcmp(method, "POST") ||
-    !strcmp(method, "HEAD") || !strcmp(method, "CONNECT") ||
-    !strcmp(method, "PUT") || !strcmp(method, "DELETE") ||
-    !strcmp(method, "OPTIONS") || !strcmp(method, "PROPFIND")
-    || !strcmp(method, "MKCOL");
+static int is_valid_http_method(const char *s) {
+  return !strcmp(s, "GET") || !strcmp(s, "POST") || !strcmp(s, "HEAD") ||
+    !strcmp(s, "CONNECT") || !strcmp(s, "PUT") || !strcmp(s, "DELETE") ||
+    !strcmp(s, "OPTIONS") || !strcmp(s, "PROPFIND") || !strcmp(s, "MKCOL");
 }
 
 // Parse HTTP request, fill in mg_request structure.
@@ -2087,6 +2409,8 @@ static int parse_http_message(char *buf, int len, struct mg_connection *ri) {
   } else {
     if (is_request) {
       ri->http_version += 5;
+    } else {
+      ri->status_code = atoi(ri->uri);
     }
     parse_http_headers(&buf, ri);
 
@@ -2095,7 +2419,9 @@ static int parse_http_message(char *buf, int len, struct mg_connection *ri) {
     }
     n = (int) strlen(ri->uri);
     mg_url_decode(ri->uri, n, (char *) ri->uri, n + 1, 0);
-    remove_double_dots_and_double_slashes((char *) ri->uri);
+    if (*ri->uri == '/' || *ri->uri == '.') {
+      remove_double_dots_and_double_slashes((char *) ri->uri);
+    }
   }
 
   return len;
@@ -2137,16 +2463,15 @@ const char *mg_get_header(const struct mg_connection *ri, const char *s) {
   return NULL;
 }
 
-#ifndef MONGOOSE_NO_FILESYSTEM
 // Perform case-insensitive match of string against pattern
-static int match_prefix(const char *pattern, int pattern_len, const char *str) {
+int mg_match_prefix(const char *pattern, int pattern_len, const char *str) {
   const char *or_str;
   int len, res, i = 0, j = 0;
 
   if ((or_str = (const char *) memchr(pattern, '|', pattern_len)) != NULL) {
-    res = match_prefix(pattern, or_str - pattern, str);
-    return res > 0 ? res :
-        match_prefix(or_str + 1, (pattern + pattern_len) - (or_str + 1), str);
+    res = mg_match_prefix(pattern, or_str - pattern, str);
+    return res > 0 ? res : mg_match_prefix(or_str + 1,
+      (pattern + pattern_len) - (or_str + 1), str);
   }
 
   for (; i < pattern_len; i++, j++) {
@@ -2166,7 +2491,7 @@ static int match_prefix(const char *pattern, int pattern_len, const char *str) {
         return j + len;
       }
       do {
-        res = match_prefix(pattern + i, pattern_len - i, str + j + len);
+        res = mg_match_prefix(pattern + i, pattern_len - i, str + j + len);
       } while (res == -1 && len-- > 0);
       return res == -1 ? -1 : j + res + len;
     } else if (lowercase(&pattern[i]) != lowercase(&str[j])) {
@@ -2176,11 +2501,46 @@ static int match_prefix(const char *pattern, int pattern_len, const char *str) {
   return j;
 }
 
+// This function prints HTML pages, and expands "{{something}}" blocks
+// inside HTML by calling appropriate callback functions.
+// Note that {{@path/to/file}} construct outputs embedded file's contents,
+// which provides SSI-like functionality.
+void mg_template(struct mg_connection *conn, const char *s,
+                 struct mg_expansion *expansions) {
+  int i, j, pos = 0, inside_marker = 0;
+
+  for (i = 0; s[i] != '\0'; i++) {
+    if (inside_marker == 0 && !memcmp(&s[i], "{{", 2)) {
+      if (i > pos) {
+        mg_send_data(conn, &s[pos], i - pos);
+      }
+      pos = i;
+      inside_marker = 1;
+    }
+    if (inside_marker == 1 && !memcmp(&s[i], "}}", 2)) {
+      for (j = 0; expansions[j].keyword != NULL; j++) {
+        const char *kw = expansions[j].keyword;
+        if ((int) strlen(kw) == i - (pos + 2) &&
+            memcmp(kw, &s[pos + 2], i - (pos + 2)) == 0) {
+          expansions[j].handler(conn);
+          pos = i + 2;
+          break;
+        }
+      }
+      inside_marker = 0;
+    }
+  }
+  if (i > pos) {
+    mg_send_data(conn, &s[pos], i - pos);
+  }
+}
+
+#ifndef MONGOOSE_NO_FILESYSTEM
 static int must_hide_file(struct connection *conn, const char *path) {
   const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$";
   const char *pattern = conn->server->config_options[HIDE_FILES_PATTERN];
-  return match_prefix(pw_pattern, strlen(pw_pattern), path) > 0 ||
-    (pattern != NULL && match_prefix(pattern, strlen(pattern), path) > 0);
+  return mg_match_prefix(pw_pattern, strlen(pw_pattern), path) > 0 ||
+    (pattern != NULL && mg_match_prefix(pattern, strlen(pattern), path) > 0);
 }
 
 // Return 1 if real file has been found, 0 otherwise
@@ -2219,7 +2579,7 @@ static int convert_uri_to_file_name(struct connection *conn, char *buf,
   mg_snprintf(buf, buf_len, "%.*s%s", root_len, root, uri);
   rewrites = conn->server->config_options[URL_REWRITES];  // Re-initialize!
   while ((rewrites = next_option(rewrites, &a, &b)) != NULL) {
-    if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) {
+    if ((match_len = mg_match_prefix(a.ptr, a.len, uri)) > 0) {
       mg_snprintf(buf, buf_len, "%.*s%s", (int) b.len, b.ptr, uri + match_len);
       break;
     }
@@ -2232,7 +2592,8 @@ static int convert_uri_to_file_name(struct connection *conn, char *buf,
   for (p = buf + strlen(root) + 2; *p != '\0'; p++) {
     if (*p == '/') {
       *p = '\0';
-      if (match_prefix(cgi_pat, strlen(cgi_pat), buf) > 0 && !stat(buf, st)) {
+      if (mg_match_prefix(cgi_pat, strlen(cgi_pat), buf) > 0 &&
+          !stat(buf, st)) {
       DBG(("!!!! [%s]", buf));
         *p = '/';
         conn->path_info = mg_strdup(p);
@@ -2259,9 +2620,10 @@ static int should_keep_alive(const struct mg_connection *conn) {
      (header == NULL && http_version && !strcmp(http_version, "1.1")));
 }
 
-int mg_write(struct mg_connection *c, const void *buf, int len) {
+size_t mg_write(struct mg_connection *c, const void *buf, int len) {
   struct connection *conn = MG_CONN_2_CONN(c);
-  return ns_send(conn->ns_conn, buf, len);
+  ns_send(conn->ns_conn, buf, len);
+  return conn->ns_conn->send_iobuf.len;
 }
 
 void mg_send_status(struct mg_connection *c, int status) {
@@ -2288,12 +2650,14 @@ static void terminate_headers(struct mg_connection *c) {
   }
 }
 
-void mg_send_data(struct mg_connection *c, const void *data, int data_len) {
+size_t mg_send_data(struct mg_connection *c, const void *data, int data_len) {
+  struct connection *conn = MG_CONN_2_CONN(c);
   terminate_headers(c);
   write_chunk(MG_CONN_2_CONN(c), (const char *) data, data_len);
+  return conn->ns_conn->send_iobuf.len;
 }
 
-void mg_printf_data(struct mg_connection *c, const char *fmt, ...) {
+size_t mg_printf_data(struct mg_connection *c, const char *fmt, ...) {
   struct connection *conn = MG_CONN_2_CONN(c);
   va_list ap;
   int len;
@@ -2305,12 +2669,13 @@ void mg_printf_data(struct mg_connection *c, const char *fmt, ...) {
   len = ns_avprintf(&buf, sizeof(mem), fmt, ap);
   va_end(ap);
 
-  if (len > 0) {
+  if (len >= 0) {
     write_chunk((struct connection *) conn, buf, len);
   }
   if (buf != mem && buf != NULL) {
     free(buf);
   }
+  return conn->ns_conn->send_iobuf.len;
 }
 
 #if !defined(MONGOOSE_NO_WEBSOCKET) || !defined(MONGOOSE_NO_AUTH)
@@ -2341,6 +2706,14 @@ static uint32_t blk0(union char64long16 *block, int i) {
   return block->l[i];
 }
 
+/* Avoid redefine warning (ARM /usr/include/sys/ucontext.h define R0~R4) */
+#undef blk
+#undef R0
+#undef R1
+#undef R2
+#undef R3
+#undef R4
+
 #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
     ^block->l[(i+2)&15]^block->l[i&15],1))
 #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(block, i)+0x5A827999+rol(v,5);w=rol(w,30);
@@ -2397,7 +2770,7 @@ static void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) {
   (void) a; (void) b; (void) c; (void) d; (void) e;
 }
 
-static void SHA1Init(SHA1_CTXcontext) {
+static void SHA1Init(SHA1_CTX *context) {
   context->state[0] = 0x67452301;
   context->state[1] = 0xEFCDAB89;
   context->state[2] = 0x98BADCFE;
@@ -2406,7 +2779,7 @@ static void SHA1Init(SHA1_CTX* context) {
   context->count[0] = context->count[1] = 0;
 }
 
-static void SHA1Update(SHA1_CTX* context, const unsigned char* data,
+static void SHA1Update(SHA1_CTX *context, const unsigned char *data,
                        uint32_t len) {
   uint32_t i, j;
 
@@ -2427,7 +2800,7 @@ static void SHA1Update(SHA1_CTX* context, const unsigned char* data,
   memcpy(&context->buffer[j], &data[i], len - i);
 }
 
-static void SHA1Final(unsigned char digest[20], SHA1_CTXcontext) {
+static void SHA1Final(unsigned char digest[20], SHA1_CTX *context) {
   unsigned i;
   unsigned char finalcount[8], c;
 
@@ -2543,14 +2916,14 @@ static int deliver_websocket_frame(struct connection *conn) {
   return buffered;
 }
 
-int mg_websocket_write(struct mg_connection* conn, int opcode,
+size_t mg_websocket_write(struct mg_connection *conn, int opcode,
                        const char *data, size_t data_len) {
-    unsigned char *copy;
+    unsigned char mem[4192], *copy = mem;
     size_t copy_len = 0;
-    int retval = -1;
 
-    if ((copy = (unsigned char *) malloc(data_len + 10)) == NULL) {
-      return -1;
+    if (data_len + 10 > sizeof(mem) &&
+        (copy = (unsigned char *) malloc(data_len + 10)) == NULL) {
+      return 0;
     }
 
     copy[0] = 0x80 + (opcode & 0x0f);
@@ -2578,11 +2951,38 @@ int mg_websocket_write(struct mg_connection* conn, int opcode,
     }
 
     if (copy_len > 0) {
-      retval = mg_write(conn, copy, copy_len);
+      mg_write(conn, copy, copy_len);
+    }
+    if (copy != mem) {
+      free(copy);
     }
-    free(copy);
 
-    return retval;
+    // If we send closing frame, schedule a connection to be closed after
+    // data is drained to the client.
+    if (opcode == WEBSOCKET_OPCODE_CONNECTION_CLOSE) {
+      MG_CONN_2_CONN(conn)->ns_conn->flags |= NSF_FINISHED_SENDING_DATA;
+    }
+
+    return MG_CONN_2_CONN(conn)->ns_conn->send_iobuf.len;
+}
+
+size_t mg_websocket_printf(struct mg_connection *conn, int opcode,
+                           const char *fmt, ...) {
+  char mem[4192], *buf = mem;
+  va_list ap;
+  int len;
+
+  va_start(ap, fmt);
+  if ((len = ns_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) {
+    mg_websocket_write(conn, opcode, buf, len);
+  }
+  va_end(ap);
+
+  if (buf != mem && buf != NULL) {
+    free(buf);
+  }
+
+  return MG_CONN_2_CONN(conn)->ns_conn->send_iobuf.len;
 }
 
 static void send_websocket_handshake_if_requested(struct mg_connection *conn) {
@@ -2590,13 +2990,16 @@ static void send_websocket_handshake_if_requested(struct mg_connection *conn) {
         *key = mg_get_header(conn, "Sec-WebSocket-Key");
   if (ver != NULL && key != NULL) {
     conn->is_websocket = 1;
-    send_websocket_handshake(conn, key);
+    if (call_user(MG_CONN_2_CONN(conn), MG_WS_HANDSHAKE) == MG_FALSE) {
+      send_websocket_handshake(conn, key);
+    }
+    call_user(MG_CONN_2_CONN(conn), MG_WS_CONNECT);
   }
 }
 
 static void ping_idle_websocket_connection(struct connection *conn, time_t t) {
   if (t - conn->ns_conn->last_io_time > MONGOOSE_USE_WEBSOCKET_PING_INTERVAL) {
-    mg_websocket_write(&conn->mg_conn, 0x9, "", 0);
+    mg_websocket_write(&conn->mg_conn, WEBSOCKET_OPCODE_PING, "", 0);
   }
 }
 #else
@@ -2788,8 +3191,8 @@ static void gmt_time_string(char *buf, size_t buf_len, time_t *t) {
 }
 
 static void open_file_endpoint(struct connection *conn, const char *path,
-                               file_stat_t *st) {
-  char date[64], lm[64], etag[64], range[64], headers[500];
+                               file_stat_t *st, const char *extra_headers) {
+  char date[64], lm[64], etag[64], range[64], headers[1000];
   const char *msg = "OK", *hdr;
   time_t curtime = time(NULL);
   int64_t r1, r2;
@@ -2833,11 +3236,12 @@ static void open_file_endpoint(struct connection *conn, const char *path,
                   "Content-Length: %" INT64_FMT "\r\n"
                   "Connection: %s\r\n"
                   "Accept-Ranges: bytes\r\n"
-                  "%s%s\r\n",
+                  "%s%s%s\r\n",
                   conn->mg_conn.status_code, msg, date, lm, etag,
                   (int) mime_vec.len, mime_vec.ptr, conn->cl,
                   suggest_connection_header(&conn->mg_conn),
-                  range, MONGOOSE_USE_EXTRA_HTTP_HEADERS);
+                  range, extra_headers == NULL ? "" : extra_headers,
+                  MONGOOSE_USE_EXTRA_HTTP_HEADERS);
   ns_send(conn->ns_conn, headers, n);
 
   if (!strcmp(conn->mg_conn.request_method, "HEAD")) {
@@ -2846,18 +3250,22 @@ static void open_file_endpoint(struct connection *conn, const char *path,
     conn->endpoint_type = EP_NONE;
   }
 }
+
+void mg_send_file_data(struct mg_connection *c, int fd) {
+  struct connection *conn = MG_CONN_2_CONN(c);
+  conn->endpoint_type = EP_FILE;
+  conn->endpoint.fd = fd;
+  ns_set_close_on_exec(conn->endpoint.fd);
+}
 #endif  // MONGOOSE_NO_FILESYSTEM
 
 static void call_request_handler_if_data_is_buffered(struct connection *conn) {
-  struct iobuf *loc = &conn->ns_conn->recv_iobuf;
-  struct mg_connection *c = &conn->mg_conn;
-
 #ifndef MONGOOSE_NO_WEBSOCKET
   if (conn->mg_conn.is_websocket) {
     do { } while (deliver_websocket_frame(conn));
   } else
 #endif
-  if ((size_t) loc->len >= c->content_len &&
+  if (conn->num_bytes_recv >= (conn->cl + conn->request_len) &&
       call_request_handler(conn) == MG_FALSE) {
     open_local_endpoint(conn, 1);
   }
@@ -2991,24 +3399,25 @@ static int scan_directory(struct connection *conn, const char *dir,
   return arr_ind;
 }
 
-static void mg_url_encode(const char *src, char *dst, size_t dst_len) {
+int mg_url_encode(const char *src, size_t s_len, char *dst, size_t dst_len) {
   static const char *dont_escape = "._-$,;~()";
   static const char *hex = "0123456789abcdef";
-  const char *end = dst + dst_len - 1;
-
-  for (; *src != '\0' && dst < end; src++, dst++) {
-    if (isalnum(*(const unsigned char *) src) ||
-        strchr(dont_escape, * (const unsigned char *) src) != NULL) {
-      *dst = *src;
-    } else if (dst + 2 < end) {
-      dst[0] = '%';
-      dst[1] = hex[(* (const unsigned char *) src) >> 4];
-      dst[2] = hex[(* (const unsigned char *) src) & 0xf];
-      dst += 2;
+  size_t i = 0, j = 0;
+
+  for (i = j = 0; dst_len > 0 && i < s_len && j + 2 < dst_len - 1; i++, j++) {
+    if (isalnum(* (const unsigned char *) (src + i)) ||
+        strchr(dont_escape, * (const unsigned char *) (src + i)) != NULL) {
+      dst[j] = src[i];
+    } else if (j + 3 < dst_len) {
+      dst[j] = '%';
+      dst[j + 1] = hex[(* (const unsigned char *) (src + i)) >> 4];
+      dst[j + 2] = hex[(* (const unsigned char *) (src + i)) & 0xf];
+      j += 2;
     }
   }
 
-  *dst = '\0';
+  dst[j] = '\0';
+  return j;
 }
 #endif  // !NO_DIRECTORY_LISTING || !MONGOOSE_NO_DAV
 
@@ -3036,12 +3445,11 @@ static void print_dir_entry(const struct dir_entry *de) {
     }
   }
   strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&de->st.st_mtime));
-  mg_url_encode(de->file_name, href, sizeof(href));
+  mg_url_encode(de->file_name, strlen(de->file_name), href, sizeof(href));
   mg_printf_data(&de->conn->mg_conn,
-                  "<tr><td><a href=\"%s%s%s\">%s%s</a></td>"
+                  "<tr><td><a href=\"%s%s\">%s%s</a></td>"
                   "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
-                  de->conn->mg_conn.uri, href, slash, de->file_name, slash,
-                  mod, size);
+                  href, slash, de->file_name, slash, mod, size);
 }
 
 // Sort directory entries by size, or name, or modification time.
@@ -3126,40 +3534,51 @@ static void print_props(struct connection *conn, const char *uri,
 }
 
 static void handle_propfind(struct connection *conn, const char *path,
-                            file_stat_t *stp) {
+                            file_stat_t *stp, int exists) {
   static const char header[] = "HTTP/1.1 207 Multi-Status\r\n"
     "Connection: close\r\n"
     "Content-Type: text/xml; charset=utf-8\r\n\r\n"
     "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
     "<d:multistatus xmlns:d='DAV:'>\n";
   static const char footer[] = "</d:multistatus>";
-  const char *depth = mg_get_header(&conn->mg_conn, "Depth"),
-        *list_dir = conn->server->config_options[ENABLE_DIRECTORY_LISTING];
+  const char *depth = mg_get_header(&conn->mg_conn, "Depth");
+#ifdef MONGOOSE_NO_DIRECTORY_LISTING
+  const char *list_dir = "no";
+#else
+  const char *list_dir = conn->server->config_options[ENABLE_DIRECTORY_LISTING];
+#endif
 
   conn->mg_conn.status_code = 207;
-  ns_send(conn->ns_conn, header, sizeof(header) - 1);
 
   // Print properties for the requested resource itself
-  print_props(conn, conn->mg_conn.uri, stp);
-
-  // If it is a directory, print directory entries too if Depth is not 0
-  if (S_ISDIR(stp->st_mode) && !mg_strcasecmp(list_dir, "yes") &&
-      (depth == NULL || strcmp(depth, "0") != 0)) {
-    struct dir_entry *arr = NULL;
-    int i, num_entries = scan_directory(conn, path, &arr);
-
-    for (i = 0; i < num_entries; i++) {
-      char buf[MAX_PATH_SIZE], buf2[sizeof(buf) * 3];
-      struct dir_entry *de = &arr[i];
-
-      mg_snprintf(buf, sizeof(buf), "%s%s", de->conn->mg_conn.uri,
-                  de->file_name);
-      mg_url_encode(buf, buf2, sizeof(buf2) - 1);
-      print_props(conn, buf, &de->st);
+  if (!exists) {
+    conn->mg_conn.status_code = 404;
+    mg_printf(&conn->mg_conn, "%s", "HTTP/1.1 404 Not Found\r\n\r\n");
+  } else if (S_ISDIR(stp->st_mode) && mg_strcasecmp(list_dir, "yes") != 0) {
+    conn->mg_conn.status_code = 403;
+    mg_printf(&conn->mg_conn, "%s",
+              "HTTP/1.1 403 Directory Listing Denied\r\n\r\n");
+  } else {
+    ns_send(conn->ns_conn, header, sizeof(header) - 1);
+    print_props(conn, conn->mg_conn.uri, stp);
+
+    if (S_ISDIR(stp->st_mode) &&
+             (depth == NULL || strcmp(depth, "0") != 0)) {
+      struct dir_entry *arr = NULL;
+      int i, num_entries = scan_directory(conn, path, &arr);
+
+      for (i = 0; i < num_entries; i++) {
+        char buf[MAX_PATH_SIZE * 3];
+        struct dir_entry *de = &arr[i];
+        mg_url_encode(de->file_name, strlen(de->file_name), buf, sizeof(buf));
+        print_props(conn, buf, &de->st);
+        free(de->file_name);
+      }
+      free(arr);
     }
+    ns_send(conn->ns_conn, footer, sizeof(footer) - 1);
   }
 
-  ns_send(conn->ns_conn, footer, sizeof(footer) - 1);
   close_local_endpoint(conn);
 }
 
@@ -3212,7 +3631,7 @@ static void handle_delete(struct connection *conn, const char *path) {
   } else if (S_ISDIR(st.st_mode)) {
     remove_directory(path);
     send_http_error(conn, 204, NULL);
-  } else if (!remove(path) == 0) {
+  } else if (remove(path) == 0) {
     send_http_error(conn, 204, NULL);
   } else {
     send_http_error(conn, 423, NULL);
@@ -3254,17 +3673,11 @@ static void handle_put(struct connection *conn, const char *path) {
     send_http_error(conn, 500, "put_dir: %s", strerror(errno));
   } else if (cl_hdr == NULL) {
     send_http_error(conn, 411, NULL);
-#ifdef _WIN32
-    //On Windows, open() is a macro with 2 params
-  } else if ((conn->endpoint.fd =
-              open(path, O_RDWR | O_CREAT | O_TRUNC)) < 0) {
-#else
   } else if ((conn->endpoint.fd =
-              open(path, O_RDWR | O_CREAT | O_TRUNC, 0644)) < 0) {
-#endif
+              open(path, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0644)) < 0) {
     send_http_error(conn, 500, "open(%s): %s", path, strerror(errno));
   } else {
-    DBG(("PUT [%s] %d", path, conn->ns_conn->recv_iobuf.len));
+    DBG(("PUT [%s] %lu", path, (unsigned long) conn->ns_conn->recv_iobuf.len));
     conn->endpoint_type = EP_PUT;
     ns_set_close_on_exec(conn->endpoint.fd);
     range = mg_get_header(&conn->mg_conn, "Content-Range");
@@ -3282,22 +3695,24 @@ static void handle_put(struct connection *conn, const char *path) {
 
 static void forward_put_data(struct connection *conn) {
   struct iobuf *io = &conn->ns_conn->recv_iobuf;
-  int n = write(conn->endpoint.fd, io->buf, io->len);
+  size_t k = conn->cl < (int64_t) io->len ? conn->cl : (int64_t) io->len;   // To write
+  int n = write(conn->endpoint.fd, io->buf, k);   // Write them!
   if (n > 0) {
     iobuf_remove(io, n);
     conn->cl -= n;
-    if (conn->cl <= 0) {
-      close_local_endpoint(conn);
-    }
+  }
+  if (conn->cl <= 0) {
+    close_local_endpoint(conn);
   }
 }
 #endif //  MONGOOSE_NO_DAV
 
 static void send_options(struct connection *conn) {
-  static const char reply[] = "HTTP/1.1 200 OK\r\nAllow: GET, POST, HEAD, "
-    "CONNECT, PUT, DELETE, OPTIONS, PROPFIND, MKCOL\r\nDAV: 1\r\n\r\n";
-  ns_send(conn->ns_conn, reply, sizeof(reply) - 1);
-  conn->ns_conn->flags |= NSF_FINISHED_SENDING_DATA;
+  conn->mg_conn.status_code = 200;
+  mg_printf(&conn->mg_conn, "%s",
+            "HTTP/1.1 200 OK\r\nAllow: GET, POST, HEAD, CONNECT, PUT, "
+            "DELETE, OPTIONS, PROPFIND, MKCOL\r\nDAV: 1\r\n\r\n");
+  close_local_endpoint(conn);
 }
 
 #ifndef MONGOOSE_NO_AUTH
@@ -3315,16 +3730,16 @@ void mg_send_digest_auth_request(struct mg_connection *c) {
 
 // Use the global passwords file, if specified by auth_gpass option,
 // or search for .htpasswd in the requested directory.
-static FILE *open_auth_file(struct connection *conn, const char *path) {
+static FILE *open_auth_file(struct connection *conn, const char *path,
+                            int is_directory) {
   char name[MAX_PATH_SIZE];
   const char *p, *gpass = conn->server->config_options[GLOBAL_AUTH_FILE];
-  file_stat_t st;
   FILE *fp = NULL;
 
   if (gpass != NULL) {
     // Use global passwords file
     fp = fopen(gpass, "r");
-  } else if (!stat(path, &st) && S_ISDIR(st.st_mode)) {
+  } else if (is_directory) {
     mg_snprintf(name, sizeof(name), "%s%c%s", path, '/', PASSWORDS_FILE_NAME);
     fp = fopen(name, "r");
   } else {
@@ -3614,11 +4029,12 @@ int mg_authorize_digest(struct mg_connection *c, FILE *fp) {
 
 
 // Return 1 if request is authorised, 0 otherwise.
-static int is_authorized(struct connection *conn, const char *path) {
+static int is_authorized(struct connection *conn, const char *path,
+                         int is_directory) {
   FILE *fp;
   int authorized = MG_TRUE;
 
-  if ((fp = open_auth_file(conn, path)) != NULL) {
+  if ((fp = open_auth_file(conn, path, is_directory)) != NULL) {
     authorized = mg_authorize_digest(&conn->mg_conn, fp);
     fclose(fp);
   }
@@ -3628,10 +4044,14 @@ static int is_authorized(struct connection *conn, const char *path) {
 
 static int is_authorized_for_dav(struct connection *conn) {
   const char *auth_file = conn->server->config_options[DAV_AUTH_FILE];
+  const char *method = conn->mg_conn.request_method;
   FILE *fp;
   int authorized = MG_FALSE;
 
-  if (auth_file != NULL && (fp = fopen(auth_file, "r")) != NULL) {
+  // If dav_auth_file is not set, allow non-authorized PROPFIND
+  if (method != NULL && !strcmp(method, "PROPFIND") && auth_file == NULL) {
+    authorized = MG_TRUE;
+  } else if (auth_file != NULL && (fp = fopen(auth_file, "r")) != NULL) {
     authorized = mg_authorize_digest(&conn->mg_conn, fp);
     fclose(fp);
   }
@@ -3639,10 +4059,10 @@ static int is_authorized_for_dav(struct connection *conn) {
   return authorized;
 }
 
-static int is_dav_mutation(const struct connection *conn) {
+static int is_dav_request(const struct connection *conn) {
   const char *s = conn->mg_conn.request_method;
-  return s && (!strcmp(s, "PUT") || !strcmp(s, "DELETE") ||
-               !strcmp(s, "MKCOL"));
+  return !strcmp(s, "PUT") || !strcmp(s, "DELETE") ||
+    !strcmp(s, "MKCOL") || !strcmp(s, "PROPFIND");
 }
 #endif // MONGOOSE_NO_AUTH
 
@@ -3684,274 +4104,6 @@ int mg_parse_header(const char *s, const char *var_name, char *buf,
   return parse_header(s, s == NULL ? 0 : strlen(s), var_name, buf, buf_size);
 }
 
-#ifdef MONGOOSE_USE_LUA
-#ifdef _WIN32
-static void *mmap(void *addr, int64_t len, int prot, int flags, int fd,
-                  int offset) {
-  HANDLE fh = (HANDLE) _get_osfhandle(fd);
-  HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0);
-  void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t) len);
-  CloseHandle(mh);
-  return p;
-}
-#define munmap(x, y)  UnmapViewOfFile(x)
-#define MAP_FAILED NULL
-#define MAP_PRIVATE 0
-#define PROT_READ 0
-#else
-#include <sys/mman.h>
-#endif
-
-void reg_string(struct lua_State *L, const char *name, const char *val) {
-  lua_pushstring(L, name);
-  lua_pushstring(L, val);
-  lua_rawset(L, -3);
-}
-
-void reg_int(struct lua_State *L, const char *name, int val) {
-  lua_pushstring(L, name);
-  lua_pushinteger(L, val);
-  lua_rawset(L, -3);
-}
-
-void reg_function(struct lua_State *L, const char *name,
-                         lua_CFunction func, struct mg_connection *conn) {
-  lua_pushstring(L, name);
-  lua_pushlightuserdata(L, conn);
-  lua_pushcclosure(L, func, 1);
-  lua_rawset(L, -3);
-}
-
-static int lua_write(lua_State *L) {
-  int i, num_args;
-  const char *str;
-  size_t size;
-  struct mg_connection *conn = (struct mg_connection *)
-    lua_touserdata(L, lua_upvalueindex(1));
-
-  num_args = lua_gettop(L);
-  for (i = 1; i <= num_args; i++) {
-    if (lua_isstring(L, i)) {
-      str = lua_tolstring(L, i, &size);
-      mg_write(conn, str, size);
-    }
-  }
-
-  return 0;
-}
-
-static int lsp_sock_close(lua_State *L) {
-  if (lua_gettop(L) > 0 && lua_istable(L, -1)) {
-    lua_getfield(L, -1, "sock");
-    closesocket((sock_t) lua_tonumber(L, -1));
-  } else {
-    return luaL_error(L, "invalid :close() call");
-  }
-  return 1;
-}
-
-static int lsp_sock_recv(lua_State *L) {
-  char buf[2000];
-  int n;
-
-  if (lua_gettop(L) > 0 && lua_istable(L, -1)) {
-    lua_getfield(L, -1, "sock");
-    n = recv((sock_t) lua_tonumber(L, -1), buf, sizeof(buf), 0);
-    if (n <= 0) {
-      lua_pushnil(L);
-    } else {
-      lua_pushlstring(L, buf, n);
-    }
-  } else {
-    return luaL_error(L, "invalid :close() call");
-  }
-  return 1;
-}
-
-static int lsp_sock_send(lua_State *L) {
-  const char *buf;
-  size_t len, sent = 0;
-  int n, sock;
-
-  if (lua_gettop(L) > 1 && lua_istable(L, -2) && lua_isstring(L, -1)) {
-    buf = lua_tolstring(L, -1, &len);
-    lua_getfield(L, -2, "sock");
-    sock = (int) lua_tonumber(L, -1);
-    while (sent < len) {
-      if ((n = send(sock, buf + sent, len - sent, 0)) <= 0) break;
-      sent += n;
-    }
-    lua_pushnumber(L, sent);
-  } else {
-    return luaL_error(L, "invalid :close() call");
-  }
-  return 1;
-}
-
-static const struct luaL_Reg luasocket_methods[] = {
-  {"close", lsp_sock_close},
-  {"send", lsp_sock_send},
-  {"recv", lsp_sock_recv},
-  {NULL, NULL}
-};
-
-static sock_t conn2(const char *host, int port) {
-  struct sockaddr_in sin;
-  struct hostent *he = NULL;
-  sock_t sock = INVALID_SOCKET;
-
-  if (host != NULL &&
-      (he = gethostbyname(host)) != NULL &&
-    (sock = socket(AF_INET, SOCK_STREAM, 0)) != INVALID_SOCKET) {
-    ns_set_close_on_exec(sock);
-    sin.sin_family = AF_INET;
-    sin.sin_port = htons((uint16_t) port);
-    sin.sin_addr = * (struct in_addr *) he->h_addr_list[0];
-    if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
-      closesocket(sock);
-      sock = INVALID_SOCKET;
-    }
-  }
-  return sock;
-}
-
-static int lsp_connect(lua_State *L) {
-  sock_t sock;
-
-  if (lua_isstring(L, -2) && lua_isnumber(L, -1)) {
-    sock = conn2(lua_tostring(L, -2), (int) lua_tonumber(L, -1));
-    if (sock == INVALID_SOCKET) {
-      lua_pushnil(L);
-    } else {
-      lua_newtable(L);
-      reg_int(L, "sock", sock);
-      reg_string(L, "host", lua_tostring(L, -4));
-      luaL_getmetatable(L, "luasocket");
-      lua_setmetatable(L, -2);
-    }
-  } else {
-    return luaL_error(L, "connect(host,port): invalid parameter given.");
-  }
-  return 1;
-}
-
-static void prepare_lua_environment(struct mg_connection *ri, lua_State *L) {
-  extern void luaL_openlibs(lua_State *);
-  int i;
-
-  luaL_openlibs(L);
-
-  luaL_newmetatable(L, "luasocket");
-  lua_newtable(L);
-  luaL_newlib(L, luasocket_methods);
-  lua_rawset(L, -3);
-  lua_pop(L, 1);
-  lua_register(L, "connect", lsp_connect);
-
-  if (ri == NULL) return;
-
-  // Register mg module
-  lua_newtable(L);
-  reg_function(L, "write", lua_write, ri);
-
-  // Export request_info
-  lua_pushstring(L, "request_info");
-  lua_newtable(L);
-  reg_string(L, "request_method", ri->request_method);
-  reg_string(L, "uri", ri->uri);
-  reg_string(L, "http_version", ri->http_version);
-  reg_string(L, "query_string", ri->query_string);
-  reg_string(L, "remote_ip", ri->remote_ip);
-  reg_int(L, "remote_port", ri->remote_port);
-  reg_string(L, "local_ip", ri->local_ip);
-  reg_int(L, "local_port", ri->local_port);
-  lua_pushstring(L, "content");
-  lua_pushlstring(L, ri->content == NULL ? "" : ri->content, ri->content_len);
-  lua_rawset(L, -3);
-  reg_int(L, "num_headers", ri->num_headers);
-  lua_pushstring(L, "http_headers");
-  lua_newtable(L);
-  for (i = 0; i < ri->num_headers; i++) {
-    reg_string(L, ri->http_headers[i].name, ri->http_headers[i].value);
-  }
-  lua_rawset(L, -3);
-  lua_rawset(L, -3);
-
-  lua_setglobal(L, "mg");
-
-  // Register default mg.onerror function
-  (void) luaL_dostring(L, "mg.onerror = function(e) mg.write('\\nLua "
-                       "error:\\n', debug.traceback(e, 1)) end");
-}
-
-static int lua_error_handler(lua_State *L) {
-  const char *error_msg =  lua_isstring(L, -1) ?  lua_tostring(L, -1) : "?\n";
-
-  lua_getglobal(L, "mg");
-  if (!lua_isnil(L, -1)) {
-    lua_getfield(L, -1, "write");   // call mg.write()
-    lua_pushstring(L, error_msg);
-    lua_pushliteral(L, "\n");
-    lua_call(L, 2, 0);
-    (void) luaL_dostring(L, "mg.write(debug.traceback(), '\\n')");
-  } else {
-    printf("Lua error: [%s]\n", error_msg);
-    (void) luaL_dostring(L, "print(debug.traceback(), '\\n')");
-  }
-  // TODO(lsm): leave the stack balanced
-
-  return 0;
-}
-
-static void lsp(struct connection *conn, const char *p, int len, lua_State *L) {
-  int i, j, pos = 0;
-
-  for (i = 0; i < len; i++) {
-    if (p[i] == '<' && p[i + 1] == '?') {
-      for (j = i + 1; j < len ; j++) {
-        if (p[j] == '?' && p[j + 1] == '>') {
-          mg_write(&conn->mg_conn, p + pos, i - pos);
-          if (luaL_loadbuffer(L, p + (i + 2), j - (i + 2), "") == 0) {
-            lua_pcall(L, 0, LUA_MULTRET, 0);
-          }
-          pos = j + 2;
-          i = pos - 1;
-          break;
-        }
-      }
-    }
-  }
-  if (i > pos) mg_write(&conn->mg_conn, p + pos, i - pos);
-}
-
-static void handle_lsp_request(struct connection *conn, const char *path,
-                               file_stat_t *st) {
-  void *p = MAP_FAILED;
-  lua_State *L = NULL;
-  FILE *fp = NULL;
-
-  if ((fp = fopen(path, "r")) == NULL ||
-      (p = mmap(NULL, st->st_size, PROT_READ, MAP_PRIVATE,
-                fileno(fp), 0)) == MAP_FAILED ||
-      (L = luaL_newstate()) == NULL) {
-    send_http_error(conn, 500, "mmap(%s): %s", path, strerror(errno));
-  } else {
-    // We're not sending HTTP headers here, Lua page must do it.
-    prepare_lua_environment(&conn->mg_conn, L);
-    conn->mg_conn.connection_param = L;
-    call_user(conn, MG_LUA);
-    lua_pushcclosure(L, &lua_error_handler, 0);
-    lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
-    lsp(conn, p, (int) st->st_size, L);
-    close_local_endpoint(conn);
-  }
-
-  if (L != NULL) lua_close(L);
-  if (p != MAP_FAILED) munmap(p, st->st_size);
-  if (fp != NULL) fclose(fp);
-}
-#endif // MONGOOSE_USE_LUA
-
 #ifndef MONGOOSE_NO_SSI
 static void send_ssi_file(struct mg_connection *, const char *, FILE *, int);
 
@@ -3998,7 +4150,8 @@ static void do_ssi_include(struct mg_connection *conn, const char *ssi,
               tag, path, strerror(errno));
   } else {
     ns_set_close_on_exec(fileno(fp));
-    if (match_prefix(opts[SSI_PATTERN], strlen(opts[SSI_PATTERN]), path) > 0) {
+    if (mg_match_prefix(opts[SSI_PATTERN], strlen(opts[SSI_PATTERN]),
+        path) > 0) {
       send_ssi_file(conn, path, fp, include_level + 1);
     } else {
       send_file_data(conn, fp);
@@ -4108,12 +4261,118 @@ static void handle_ssi_request(struct connection *conn, const char *path) {
 }
 #endif
 
-static void open_local_endpoint(struct connection *conn, int skip_user) {
+static void proxy_request(struct ns_connection *pc, struct mg_connection *c) {
+  int i, sent_close_header = 0;
+
+  ns_printf(pc, "%s %s%s%s HTTP/%s\r\n", c->request_method, c->uri,
+            c->query_string ? "?" : "",
+            c->query_string ? c->query_string : "",
+            c->http_version);
+  for (i = 0; i < c->num_headers; i++) {
+    if (mg_strcasecmp(c->http_headers[i].name, "Connection") == 0) {
+      // Force connection close, cause we don't parse proxy replies
+      // therefore we don't know message boundaries
+      ns_printf(pc, "%s: %s\r\n", "Connection", "close");
+      sent_close_header = 1;
+    } else {
+      ns_printf(pc, "%s: %s\r\n", c->http_headers[i].name,
+                c->http_headers[i].value);
+    }
+  }
+  if (!sent_close_header) {
+    ns_printf(pc, "%s: %s\r\n", "Connection", "close");
+  }
+  ns_printf(pc, "%s", "\r\n");
+  ns_send(pc, c->content, c->content_len);
+
+}
+
+#ifdef NS_ENABLE_SSL
+int mg_terminate_ssl(struct mg_connection *c, const char *cert) {
+  static const char ok[] = "HTTP/1.0 200 OK\r\n\r\n";
+  struct connection *conn = MG_CONN_2_CONN(c);
+  SSL_CTX *ctx;
+
+  DBG(("%p MITM", conn));
+  if ((ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) return 0;
+
+  SSL_CTX_use_certificate_file(ctx, cert, 1);
+  SSL_CTX_use_PrivateKey_file(ctx, cert, 1);
+  SSL_CTX_use_certificate_chain_file(ctx, cert);
+
+  // When clear-text reply is pushed to client, switch to SSL mode.
+  // TODO(lsm): check for send() failure
+  send(conn->ns_conn->sock, ok, sizeof(ok) - 1, 0);
+  //DBG(("%p %lu %d SEND", c, (unsigned long) sizeof(ok) - 1, n));
+  conn->ns_conn->send_iobuf.len = 0;
+  conn->endpoint_type = EP_USER;  // To keep-alive in close_local_endpoint()
+  close_local_endpoint(conn);     // Clean up current CONNECT request
+  if ((conn->ns_conn->ssl = SSL_new(ctx)) != NULL) {
+    SSL_set_fd(conn->ns_conn->ssl, conn->ns_conn->sock);
+  }
+  SSL_CTX_free(ctx);
+  return 1;
+}
+#endif
+
+int mg_forward(struct mg_connection *c, const char *addr) {
+  static const char ok[] = "HTTP/1.1 200 OK\r\n\r\n";
+  struct connection *conn = MG_CONN_2_CONN(c);
+  struct ns_connection *pc;
+
+  if ((pc = ns_connect(&conn->server->ns_mgr, addr,
+      mg_ev_handler, conn)) == NULL) {
+    conn->ns_conn->flags |= NSF_CLOSE_IMMEDIATELY;
+    return 0;
+  }
+
+  // Interlink two connections
+  pc->flags |= MG_PROXY_CONN;
+  conn->endpoint_type = EP_PROXY;
+  conn->endpoint.nc = pc;
+  DBG(("%p [%s] [%s] -> %p %p", conn, c->uri, addr, pc, conn->ns_conn->ssl));
+
+  if (strcmp(c->request_method, "CONNECT") == 0) {
+    // For CONNECT request, reply with 200 OK. Tunnel is established.
+    // TODO(lsm): check for send() failure
+    (void) send(conn->ns_conn->sock, ok, sizeof(ok) - 1, 0);
+  } else {
+    // Strip "http://host:port" part from the URI
+    if (memcmp(c->uri, "http://", 7) == 0) c->uri += 7;
+    while (*c->uri != '\0' && *c->uri != '/') c->uri++;
+    proxy_request(pc, c);
+  }
+  return 1;
+}
+
+static void proxify_connection(struct connection *conn) {
+  char proto[10], host[500], cert[500], addr[1000];
+  unsigned short port = 80;
+  struct mg_connection *c = &conn->mg_conn;
+  int n = 0;
+  const char *url = c->uri;
+
+  proto[0] = host[0] = cert[0] = '\0';
+  if (sscanf(url, "%499[^: ]:%hu%n", host, &port, &n) != 2 &&
+      sscanf(url, "%9[a-z]://%499[^: ]:%hu%n", proto, host, &port, &n) != 3 &&
+      sscanf(url, "%9[a-z]://%499[^/ ]%n", proto, host, &n) != 2) {
+    n = 0;
+  }
+
+  snprintf(addr, sizeof(addr), "%s://%s:%hu",
+           conn->ns_conn->ssl != NULL ? "ssl" : "tcp", host, port);
+  if (n <= 0 || !mg_forward(c, addr)) {
+    conn->ns_conn->flags |= NSF_CLOSE_IMMEDIATELY;
+  }
+}
+
 #ifndef MONGOOSE_NO_FILESYSTEM
-  static const char lua_pat[] = LUA_SCRIPT_PATTERN;
-  file_stat_t st;
+void mg_send_file_internal(struct mg_connection *c, const char *file_name,
+                           file_stat_t *st, int exists,
+                           const char *extra_headers) {
+  struct connection *conn = MG_CONN_2_CONN(c);
   char path[MAX_PATH_SIZE];
-  int exists = 0, is_directory = 0;
+  const int is_directory = S_ISDIR(st->st_mode);
 #ifndef MONGOOSE_NO_CGI
   const char *cgi_pat = conn->server->config_options[CGI_PATTERN];
 #else
@@ -4124,6 +4383,62 @@ static void open_local_endpoint(struct connection *conn, int skip_user) {
 #else
   const char *dir_lst = "yes";
 #endif
+
+  mg_snprintf(path, sizeof(path), "%s", file_name);
+
+  if (!exists || must_hide_file(conn, path)) {
+    send_http_error(conn, 404, NULL);
+  } else if (is_directory &&
+             conn->mg_conn.uri[strlen(conn->mg_conn.uri) - 1] != '/') {
+    conn->mg_conn.status_code = 301;
+    mg_printf(&conn->mg_conn, "HTTP/1.1 301 Moved Permanently\r\n"
+              "Location: %s/\r\n\r\n", conn->mg_conn.uri);
+    close_local_endpoint(conn);
+  } else if (is_directory && !find_index_file(conn, path, sizeof(path), st)) {
+    if (!mg_strcasecmp(dir_lst, "yes")) {
+#ifndef MONGOOSE_NO_DIRECTORY_LISTING
+      send_directory_listing(conn, path);
+#else
+      send_http_error(conn, 501, NULL);
+#endif
+    } else {
+      send_http_error(conn, 403, NULL);
+    }
+  } else if (mg_match_prefix(cgi_pat, strlen(cgi_pat), path) > 0) {
+#if !defined(MONGOOSE_NO_CGI)
+    open_cgi_endpoint(conn, path);
+#else
+    send_http_error(conn, 501, NULL);
+#endif // !MONGOOSE_NO_CGI
+#ifndef MONGOOSE_NO_SSI
+  } else if (mg_match_prefix(conn->server->config_options[SSI_PATTERN],
+                             strlen(conn->server->config_options[SSI_PATTERN]),
+                             path) > 0) {
+    handle_ssi_request(conn, path);
+#endif
+  } else if (is_not_modified(conn, st)) {
+    send_http_error(conn, 304, NULL);
+  } else if ((conn->endpoint.fd = open(path, O_RDONLY | O_BINARY, 0)) != -1) {
+    // O_BINARY is required for Windows, otherwise in default text mode
+    // two bytes \r\n will be read as one.
+    open_file_endpoint(conn, path, st, extra_headers);
+  } else {
+    send_http_error(conn, 404, NULL);
+  }
+}
+void mg_send_file(struct mg_connection *c, const char *file_name,
+                  const char *extra_headers) {
+  file_stat_t st;
+  const int exists = stat(file_name, &st) == 0;
+  mg_send_file_internal(c, file_name, &st, exists, extra_headers);
+}
+#endif  // !MONGOOSE_NO_FILESYSTEM
+
+static void open_local_endpoint(struct connection *conn, int skip_user) {
+#ifndef MONGOOSE_NO_FILESYSTEM
+  char path[MAX_PATH_SIZE];
+  file_stat_t st;
+  int exists = 0;
 #endif
 
   // If EP_USER was set in a prev call, reset it
@@ -4142,39 +4457,54 @@ static void open_local_endpoint(struct connection *conn, int skip_user) {
 #if MONGOOSE_POST_SIZE_LIMIT > 1
     {
       const char *cl = mg_get_header(&conn->mg_conn, "Content-Length");
-      if (!strcmp(conn->mg_conn.request_method, "POST") &&
+      if ((strcmp(conn->mg_conn.request_method, "POST") == 0 ||
+           strcmp(conn->mg_conn.request_method, "PUT") == 0) &&
           (cl == NULL || to64(cl) > MONGOOSE_POST_SIZE_LIMIT)) {
-        send_http_error(conn, 500, "POST size > %zu",
-                        (size_t) MONGOOSE_POST_SIZE_LIMIT);
+        send_http_error(conn, 500, "POST size > %lu",
+                        (unsigned long) MONGOOSE_POST_SIZE_LIMIT);
       }
     }
 #endif
     return;
   }
 
-#ifdef MONGOOSE_NO_FILESYSTEM
+  if (strcmp(conn->mg_conn.request_method, "CONNECT") == 0 ||
+      mg_strncasecmp(conn->mg_conn.uri, "http", 4) == 0) {
+    const char *enp = conn->server->config_options[ENABLE_PROXY];
+    if (enp == NULL || strcmp(enp, "yes") != 0) {
+      send_http_error(conn, 405, NULL);
+    } else {
+      proxify_connection(conn);
+    }
+    return;
+  }
+
   if (!strcmp(conn->mg_conn.request_method, "OPTIONS")) {
     send_options(conn);
-  } else {
-    send_http_error(conn, 404, NULL);
+    return;
   }
+
+#ifdef MONGOOSE_NO_FILESYSTEM
+  send_http_error(conn, 404, NULL);
 #else
   exists = convert_uri_to_file_name(conn, path, sizeof(path), &st);
-  is_directory = S_ISDIR(st.st_mode);
 
   if (!strcmp(conn->mg_conn.request_method, "OPTIONS")) {
     send_options(conn);
   } else if (conn->server->config_options[DOCUMENT_ROOT] == NULL) {
     send_http_error(conn, 404, NULL);
 #ifndef MONGOOSE_NO_AUTH
-  } else if ((!is_dav_mutation(conn) && !is_authorized(conn, path)) ||
-             (is_dav_mutation(conn) && !is_authorized_for_dav(conn))) {
+  } else if ((!is_dav_request(conn) && !is_authorized(conn, path,
+               exists && S_ISDIR(st.st_mode))) ||
+             (is_dav_request(conn) && !is_authorized_for_dav(conn))) {
     mg_send_digest_auth_request(&conn->mg_conn);
     close_local_endpoint(conn);
 #endif
 #ifndef MONGOOSE_NO_DAV
+  } else if (must_hide_file(conn, path)) {
+    send_http_error(conn, 404, NULL);
   } else if (!strcmp(conn->mg_conn.request_method, "PROPFIND")) {
-    handle_propfind(conn, path, &st);
+    handle_propfind(conn, path, &st, exists);
   } else if (!strcmp(conn->mg_conn.request_method, "MKCOL")) {
     handle_mkcol(conn, path);
   } else if (!strcmp(conn->mg_conn.request_method, "DELETE")) {
@@ -4182,50 +4512,8 @@ static void open_local_endpoint(struct connection *conn, int skip_user) {
   } else if (!strcmp(conn->mg_conn.request_method, "PUT")) {
     handle_put(conn, path);
 #endif
-  } else if (!exists || must_hide_file(conn, path)) {
-    send_http_error(conn, 404, NULL);
-  } else if (is_directory &&
-             conn->mg_conn.uri[strlen(conn->mg_conn.uri) - 1] != '/') {
-    conn->mg_conn.status_code = 301;
-    mg_printf(&conn->mg_conn, "HTTP/1.1 301 Moved Permanently\r\n"
-              "Location: %s/\r\n\r\n", conn->mg_conn.uri);
-    close_local_endpoint(conn);
-  } else if (is_directory && !find_index_file(conn, path, sizeof(path), &st)) {
-    if (!mg_strcasecmp(dir_lst, "yes")) {
-#ifndef MONGOOSE_NO_DIRECTORY_LISTING
-      send_directory_listing(conn, path);
-#else
-      send_http_error(conn, 501, NULL);
-#endif
-    } else {
-      send_http_error(conn, 403, NULL);
-    }
-  } else if (match_prefix(lua_pat, sizeof(lua_pat) - 1, path) > 0) {
-#ifdef MONGOOSE_USE_LUA
-    handle_lsp_request(conn, path, &st);
-#else
-    send_http_error(conn, 501, NULL);
-#endif
-  } else if (match_prefix(cgi_pat, strlen(cgi_pat), path) > 0) {
-#if !defined(MONGOOSE_NO_CGI)
-    open_cgi_endpoint(conn, path);
-#else
-    send_http_error(conn, 501, NULL);
-#endif // !MONGOOSE_NO_CGI
-#ifndef MONGOOSE_NO_SSI
-  } else if (match_prefix(conn->server->config_options[SSI_PATTERN],
-                          strlen(conn->server->config_options[SSI_PATTERN]),
-                          path) > 0) {
-    handle_ssi_request(conn, path);
-#endif
-  } else if (is_not_modified(conn, &st)) {
-    send_http_error(conn, 304, NULL);
-  } else if ((conn->endpoint.fd = open(path, O_RDONLY | O_BINARY)) != -1) {
-    // O_BINARY is required for Windows, otherwise in default text mode
-    // two bytes \r\n will be read as one.
-    open_file_endpoint(conn, path, &st);
   } else {
-    send_http_error(conn, 404, NULL);
+    mg_send_file_internal(&conn->mg_conn, path, &st, exists, NULL);
   }
 #endif  // MONGOOSE_NO_FILESYSTEM
 }
@@ -4239,10 +4527,13 @@ static void send_continue_if_expected(struct connection *conn) {
   }
 }
 
+// Conform to http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
 static int is_valid_uri(const char *uri) {
-  // Conform to http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
-  // URI can be an asterisk (*) or should start with slash.
-  return uri[0] == '/' || (uri[0] == '*' && uri[1] == '\0');
+  unsigned short n;
+  return uri[0] == '/' ||
+    strcmp(uri, "*") == 0 ||            // OPTIONS method can use asterisk URI
+    mg_strncasecmp(uri, "http", 4) == 0 || // Naive check for the absolute URI
+    sscanf(uri, "%*[^ :]:%hu", &n) > 0; // CONNECT method can use host:port
 }
 
 static void try_parse(struct connection *conn) {
@@ -4255,24 +4546,45 @@ static void try_parse(struct connection *conn) {
     // become invalid.
     conn->request = (char *) malloc(conn->request_len);
     memcpy(conn->request, io->buf, conn->request_len);
-    DBG(("%p [%.*s]", conn, conn->request_len, conn->request));
+    //DBG(("%p [%.*s]", conn, conn->request_len, conn->request));
     iobuf_remove(io, conn->request_len);
     conn->request_len = parse_http_message(conn->request, conn->request_len,
                                            &conn->mg_conn);
     if (conn->request_len > 0) {
       const char *cl_hdr = mg_get_header(&conn->mg_conn, "Content-Length");
       conn->cl = cl_hdr == NULL ? 0 : to64(cl_hdr);
-      conn->mg_conn.content_len = (long int) conn->cl;
+      conn->mg_conn.content_len = (size_t) conn->cl;
     }
   }
 }
 
-static void process_request(struct connection *conn) {
+static void do_proxy(struct connection *conn) {
+  if (0 && conn->request_len == 0) {
+    try_parse(conn);
+    DBG(("%p parsing -> %d", conn, conn->request_len));
+    if (conn->request_len > 0 && call_user(conn, MG_REQUEST) == MG_FALSE) {
+      proxy_request(conn->endpoint.nc, &conn->mg_conn);
+    } else if (conn->request_len < 0) {
+      ns_forward(conn->ns_conn, conn->endpoint.nc);
+    }
+  } else {
+    DBG(("%p forwarding", conn));
+    ns_forward(conn->ns_conn, conn->endpoint.nc);
+  }
+}
+
+static void on_recv_data(struct connection *conn) {
   struct iobuf *io = &conn->ns_conn->recv_iobuf;
+  int n;
+
+  if (conn->endpoint_type == EP_PROXY) {
+    if (conn->endpoint.nc != NULL) do_proxy(conn);
+    return;
+  }
 
   try_parse(conn);
-  DBG(("%p %d %d %d [%.*s]", conn, conn->request_len, io->len,
-       conn->ns_conn->flags, io->len, io->buf));
+  DBG(("%p %d %lu %d", conn, conn->request_len, (unsigned long)io->len,
+       conn->ns_conn->flags));
   if (conn->request_len < 0 ||
       (conn->request_len > 0 && !is_valid_uri(conn->mg_conn.uri))) {
     send_http_error(conn, 400, NULL);
@@ -4291,11 +4603,19 @@ static void process_request(struct connection *conn) {
   }
 
 #ifndef MONGOOSE_NO_CGI
-  if (conn->endpoint_type == EP_CGI && io->len > 0) {
-    forward_post_data(conn);
+  if (conn->endpoint_type == EP_CGI && conn->endpoint.nc != NULL) {
+    ns_forward(conn->ns_conn, conn->endpoint.nc);
   }
 #endif
   if (conn->endpoint_type == EP_USER) {
+    conn->mg_conn.content = io->buf;
+    conn->mg_conn.content_len = io->len;
+    n = call_user(conn, MG_RECV);
+    if (n < 0) {
+      conn->ns_conn->flags |= NSF_FINISHED_SENDING_DATA;
+    } else if ((size_t) n <= io->len) {
+      iobuf_remove(io, n);
+    }
     call_request_handler_if_data_is_buffered(conn);
   }
 #ifndef MONGOOSE_NO_DAV
@@ -4317,7 +4637,7 @@ static void call_http_client_handler(struct connection *conn) {
   }
   iobuf_remove(&conn->ns_conn->recv_iobuf, conn->mg_conn.content_len);
   conn->mg_conn.status_code = 0;
-  conn->cl = conn->num_bytes_sent = conn->request_len = 0;
+  conn->cl = conn->num_bytes_recv = conn->request_len = 0;
   free(conn->request);
   conn->request = NULL;
 }
@@ -4326,22 +4646,20 @@ static void process_response(struct connection *conn) {
   struct iobuf *io = &conn->ns_conn->recv_iobuf;
 
   try_parse(conn);
-  DBG(("%p %d %d [%.*s]", conn, conn->request_len, io->len,
-       io->len > 40 ? 40 : io->len, io->buf));
+  DBG(("%p %d %lu", conn, conn->request_len, (unsigned long)io->len));
   if (conn->request_len < 0 ||
       (conn->request_len == 0 && io->len > MAX_REQUEST_SIZE)) {
     call_http_client_handler(conn);
-  } else if (io->len >= conn->cl) {
+  } else if ((int64_t) io->len >= conn->cl) {
     call_http_client_handler(conn);
   }
 }
 
-struct mg_connection *mg_connect(struct mg_server *server, const char *host,
-                                 int port, int use_ssl) {
+struct mg_connection *mg_connect(struct mg_server *server, const char *addr) {
   struct ns_connection *nsconn;
   struct connection *conn;
 
-  nsconn = ns_connect(&server->ns_server, host, port, use_ssl, NULL);
+  nsconn = ns_connect(&server->ns_mgr, addr, mg_ev_handler, NULL);
   if (nsconn == NULL) return 0;
 
   if ((conn = (struct connection *) calloc(1, sizeof(*conn))) == NULL) {
@@ -4351,12 +4669,12 @@ struct mg_connection *mg_connect(struct mg_server *server, const char *host,
 
   // Interlink two structs
   conn->ns_conn = nsconn;
-  nsconn->connection_data = conn;
+  nsconn->user_data = conn;
 
   conn->server = server;
   conn->endpoint_type = EP_CLIENT;
   //conn->handler = handler;
-  conn->mg_conn.server_param = server->ns_server.server_data;
+  conn->mg_conn.server_param = server->ns_mgr.user_data;
   conn->ns_conn->flags = NSF_CONNECTING;
 
   return &conn->mg_conn;
@@ -4387,12 +4705,12 @@ static void log_access(const struct connection *conn, const char *path) {
   flockfile(fp);
   mg_parse_header(mg_get_header(&conn->mg_conn, "Authorization"), "username",
                   user, sizeof(user));
-  fprintf(fp, "%s - %s [%s] \"%s %s%s%s HTTP/%s\" %d %" INT64_FMT,
+  fprintf(fp, "%s - %s [%s] \"%s %s%s%s HTTP/%s\" %d 0",
           c->remote_ip, user[0] == '\0' ? "-" : user, date,
           c->request_method ? c->request_method : "-",
           c->uri ? c->uri : "-", c->query_string ? "?" : "",
           c->query_string ? c->query_string : "",
-          c->http_version, c->status_code, conn->num_bytes_sent);
+          c->http_version, c->status_code);
   log_header(c, "Referer", fp);
   log_header(c, "User-Agent", fp);
   fputc('\n', fp);
@@ -4417,9 +4735,11 @@ static void close_local_endpoint(struct connection *conn) {
       close(conn->endpoint.fd);
       break;
     case EP_CGI:
-      if (conn->endpoint.cgi_conn != NULL) {
-        conn->endpoint.cgi_conn->flags |= NSF_CLOSE_IMMEDIATELY;
-        conn->endpoint.cgi_conn->connection_data = NULL;
+    case EP_PROXY:
+      if (conn->endpoint.nc != NULL) {
+        DBG(("%p %p %p :-)", conn, conn->ns_conn, conn->endpoint.nc));
+        conn->endpoint.nc->flags |= NSF_CLOSE_IMMEDIATELY;
+        conn->endpoint.nc->user_data = NULL;
       }
       break;
     default: break;
@@ -4433,19 +4753,26 @@ static void close_local_endpoint(struct connection *conn) {
 #endif
 
   // Gobble possible POST data sent to the URI handler
-  iobuf_remove(&conn->ns_conn->recv_iobuf, conn->mg_conn.content_len);
+  iobuf_free(&conn->ns_conn->recv_iobuf);
+  free(conn->request);
+  free(conn->path_info);
+  conn->endpoint.nc = NULL;
+  conn->request = conn->path_info = NULL;
+
   conn->endpoint_type = EP_NONE;
-  conn->cl = conn->num_bytes_sent = conn->request_len = 0;
+  conn->cl = conn->num_bytes_recv = conn->request_len = 0;
   conn->ns_conn->flags &= ~(NSF_FINISHED_SENDING_DATA |
                             NSF_BUFFER_BUT_DONT_SEND | NSF_CLOSE_IMMEDIATELY |
                             MG_HEADERS_SENT | MG_LONG_RUNNING);
+
+  // Do not memset() the whole structure, as some of the fields
+  // (IP addresses & ports, server_param) must survive. Nullify the rest.
   c->request_method = c->uri = c->http_version = c->query_string = NULL;
   c->num_headers = c->status_code = c->is_websocket = c->content_len = 0;
-  free(conn->request); conn->request = NULL;
-  free(conn->path_info); conn->path_info = NULL;
+  c->connection_param = c->callback_param = NULL;
 
   if (keep_alive) {
-    process_request(conn);  // Can call us recursively if pipelining is used
+    on_recv_data(conn);  // Can call us recursively if pipelining is used
   } else {
     conn->ns_conn->flags |= conn->ns_conn->send_iobuf.len == 0 ?
       NSF_CLOSE_IMMEDIATELY : NSF_FINISHED_SENDING_DATA;
@@ -4454,8 +4781,15 @@ static void close_local_endpoint(struct connection *conn) {
 
 static void transfer_file_data(struct connection *conn) {
   char buf[IOBUF_SIZE];
-  int n = read(conn->endpoint.fd, buf, conn->cl < (int64_t) sizeof(buf) ?
-               (int) conn->cl : (int) sizeof(buf));
+  int n;
+
+  // If output buffer is too big, don't send anything. Wait until
+  // mongoose drains already buffered data to the client.
+  if (conn->ns_conn->send_iobuf.len > sizeof(buf) * 2) return;
+
+  // Do not send anyt
+  n = read(conn->endpoint.fd, buf, conn->cl < (int64_t) sizeof(buf) ?
+           (int) conn->cl : (int) sizeof(buf));
 
   if (n <= 0) {
     close_local_endpoint(conn);
@@ -4469,7 +4803,7 @@ static void transfer_file_data(struct connection *conn) {
 }
 
 int mg_poll_server(struct mg_server *server, int milliseconds) {
-  return ns_server_poll(&server->ns_server, milliseconds);
+  return ns_mgr_poll(&server->ns_mgr, milliseconds);
 }
 
 void mg_destroy_server(struct mg_server **server) {
@@ -4477,7 +4811,7 @@ void mg_destroy_server(struct mg_server **server) {
     struct mg_server *s = *server;
     int i;
 
-    ns_server_free(&s->ns_server);
+    ns_mgr_free(&s->ns_mgr);
     for (i = 0; i < (int) ARRAY_SIZE(s->config_options); i++) {
       free(s->config_options[i]);  // It is OK to free(NULL)
     }
@@ -4486,26 +4820,16 @@ void mg_destroy_server(struct mg_server **server) {
   }
 }
 
-struct mg_iterator {
-  mg_handler_t cb;
-  void *param;
-};
-union variant { mg_handler_t cb; void *p; };
-
-static void iter(struct ns_connection *nsconn, enum ns_event ev, void *param) {
-  if (ev == NS_POLL) {
-    union variant *variant = (union variant *) param;
-    struct connection *c = (struct connection *) nsconn->connection_data;
-    variant->cb(&c->mg_conn, MG_POLL);
+struct mg_connection *mg_next(struct mg_server *s, struct mg_connection *c) {
+  struct ns_connection *nc = ns_next(&s->ns_mgr, c == NULL ? NULL :
+                                     MG_CONN_2_CONN(c)->ns_conn);
+  if (nc != NULL && nc->user_data != NULL) {
+    return & ((struct connection *) nc->user_data)->mg_conn;
+  } else {
+    return NULL;
   }
 }
 
-// Apply function to all active connections.
-void mg_iterate_over_connections(struct mg_server *server, mg_handler_t cb) {
-  union variant variant = { cb };
-  ns_iterate(&server->ns_server, iter, &variant);
-}
-
 static int get_var(const char *data, size_t data_len, const char *name,
                    char *dst, size_t dst_len) {
   const char *p, *e, *s;
@@ -4612,6 +4936,19 @@ const char **mg_get_valid_option_names(void) {
   return static_config_options;
 }
 
+void mg_copy_listeners(struct mg_server *s, struct mg_server *to) {
+  struct ns_connection *c;
+  for (c = ns_next(&s->ns_mgr, NULL); c != NULL; c = ns_next(&s->ns_mgr, c)) {
+    struct ns_connection *tmp;
+    if ((c->flags & NSF_LISTENING) &&
+        (tmp = (struct ns_connection *) malloc(sizeof(*tmp))) != NULL) {
+      memcpy(tmp, c, sizeof(*tmp));
+      tmp->mgr = &to->ns_mgr;
+      ns_add_conn(tmp->mgr, tmp);
+    }
+  }
+}
+
 static int get_option_index(const char *name) {
   int i;
 
@@ -4639,60 +4976,76 @@ const char *mg_set_option(struct mg_server *server, const char *name,
                           const char *value) {
   int ind = get_option_index(name);
   const char *error_msg = NULL;
+  char **v = NULL;
 
-  if (ind < 0) {
-    error_msg = "No such option";
-  } else {
-    if (server->config_options[ind] != NULL) {
-      free(server->config_options[ind]);
-    }
-    server->config_options[ind] = mg_strdup(value);
-    DBG(("%s [%s]", name, value));
+  if (ind < 0) return  "No such option";
+  v = &server->config_options[ind];
+
+  // Return success immediately if setting to the same value
+  if ((*v == NULL && value == NULL) ||
+      (value != NULL && *v != NULL && !strcmp(value, *v))) {
+    return NULL;
+  }
 
-    if (ind == LISTENING_PORT) {
-      int port = ns_bind(&server->ns_server, value);
-      if (port < 0) {
+  if (*v != NULL) {
+    free(*v);
+    *v = NULL;
+  }
+
+  if (value == NULL || value[0] == '\0') return NULL;
+
+  *v = mg_strdup(value);
+  DBG(("%s [%s]", name, *v));
+
+  if (ind == LISTENING_PORT) {
+    struct vec vec;
+    while ((value = next_option(value, &vec, NULL)) != NULL) {
+      struct ns_connection *c = ns_bind(&server->ns_mgr, vec.ptr,
+                                        mg_ev_handler, NULL);
+      if (c== NULL) {
         error_msg = "Cannot bind to port";
+        break;
       } else {
-        sockaddr_to_string(server->local_ip, sizeof(server->local_ip),
-                           &server->ns_server.listening_sa);
-        if (!strcmp(value, "0")) {
-          char buf[10];
-          mg_snprintf(buf, sizeof(buf), "%d", port);
-          free(server->config_options[ind]);
-          server->config_options[ind] = mg_strdup(buf);
-        }
-      }
-#ifndef _WIN32
-    } else if (ind == RUN_AS_USER) {
-      struct passwd *pw;
-      if ((pw = getpwnam(value)) == NULL) {
-        error_msg = "Unknown user";
-      } else if (setgid(pw->pw_gid) != 0) {
-        error_msg = "setgid() failed";
-      } else if (setuid(pw->pw_uid) != 0) {
-        error_msg = "setuid() failed";
-      }
-#endif
-#ifdef NS_ENABLE_SSL
-    } else if (ind == SSL_CERTIFICATE) {
-      int res = ns_set_ssl_cert(&server->ns_server, value);
-      if (res == -2) {
-        error_msg = "Cannot load PEM";
-      } else if (res == -3) {
-        error_msg = "SSL not enabled";
-      } else if (res == -1) {
-        error_msg = "SSL_CTX_new() failed";
+        char buf[100];
+        ns_sock_to_str(c->sock, buf, sizeof(buf), 2);
+        free(*v);
+        *v = mg_strdup(buf);
       }
+    }
+#ifndef MONGOOSE_NO_FILESYSTEM
+  } else if (ind == HEXDUMP_FILE) {
+    server->ns_mgr.hexdump_file = *v;
 #endif
+#ifndef _WIN32
+  } else if (ind == RUN_AS_USER) {
+    struct passwd *pw;
+    if ((pw = getpwnam(value)) == NULL) {
+      error_msg = "Unknown user";
+    } else if (setgid(pw->pw_gid) != 0) {
+      error_msg = "setgid() failed";
+    } else if (setuid(pw->pw_uid) != 0) {
+      error_msg = "setuid() failed";
     }
+#endif
   }
 
   return error_msg;
 }
 
+static void set_ips(struct ns_connection *nc, int is_rem) {
+  struct connection *conn = (struct connection *) nc->user_data;
+  struct mg_connection *c = &conn->mg_conn;
+  char buf[100];
+
+  ns_sock_to_str(nc->sock, buf, sizeof(buf), is_rem ? 7 : 3);
+  sscanf(buf, "%47[^:]:%hu",
+         is_rem ? c->remote_ip : c->local_ip,
+         is_rem ? &c->remote_port : &c->local_port);
+  //DBG(("%p %s %s", conn, is_rem ? "rem" : "loc", buf));
+}
+
 static void on_accept(struct ns_connection *nc, union socket_address *sa) {
-  struct mg_server *server = (struct mg_server *) nc->server;
+  struct mg_server *server = (struct mg_server *) nc->mgr;
   struct connection *conn;
 
   if (!check_acl(server->config_options[ACCESS_CONTROL_LIST],
@@ -4700,44 +5053,87 @@ static void on_accept(struct ns_connection *nc, union socket_address *sa) {
       (conn = (struct connection *) calloc(1, sizeof(*conn))) == NULL) {
     nc->flags |= NSF_CLOSE_IMMEDIATELY;
   } else {
-    conn->server = (struct mg_server *) nc->server;
-    sockaddr_to_string(conn->mg_conn.remote_ip,
-                       sizeof(conn->mg_conn.remote_ip), sa);
-    conn->mg_conn.remote_port = ntohs(sa->sin.sin_port);
-    conn->mg_conn.server_param = nc->server->server_data;
-    conn->mg_conn.local_ip = server->local_ip;
-    conn->mg_conn.local_port =
-      ntohs(server->ns_server.listening_sa.sin.sin_port);
-
     // Circularly link two connection structures
-    nc->connection_data = conn;
+    nc->user_data = conn;
     conn->ns_conn = nc;
+
+    // Initialize the rest of connection attributes
+    conn->server = server;
+    conn->mg_conn.server_param = nc->mgr->user_data;
+    set_ips(nc, 1);
+    set_ips(nc, 0);
   }
 }
 
-static void mg_ev_handler(struct ns_connection *nc, enum ns_event ev, void *p) {
-  struct connection *conn = (struct connection *) nc->connection_data;
+static void process_udp(struct ns_connection *nc) {
+  struct iobuf *io = &nc->recv_iobuf;
+  struct connection conn;
+
+  memset(&conn, 0, sizeof(conn));
+  conn.ns_conn = nc;
+  conn.server = (struct mg_server *) nc->mgr;
+  conn.request_len = parse_http_message(io->buf, io->len, &conn.mg_conn);
+  on_recv_data(&conn);
+  //ns_printf(nc, "%s", "HTTP/1.0 200 OK\r\n\r\n");
+}
+
+static void mg_ev_handler(struct ns_connection *nc, int ev, void *p) {
+  struct connection *conn = (struct connection *) nc->user_data;
+
+  // Send NS event to the handler. Note that call_user won't send an event
+  // if conn == NULL. Therefore, repeat this for NS_ACCEPT event as well.
+#ifdef MONGOOSE_SEND_NS_EVENTS
+  {
+    struct connection *conn = (struct connection *) nc->user_data;
+    void *param[2] = { nc, p };
+    if (conn != NULL) conn->mg_conn.callback_param = param;
+    call_user(conn, (enum mg_event) ev);
+  }
+#endif
 
   switch (ev) {
     case NS_ACCEPT:
       on_accept(nc, (union socket_address *) p);
+#ifdef MONGOOSE_SEND_NS_EVENTS
+      {
+        struct connection *conn = (struct connection *) nc->user_data;
+        void *param[2] = { nc, p };
+        if (conn != NULL) conn->mg_conn.callback_param = param;
+        call_user(conn, (enum mg_event) ev);
+      }
+#endif
       break;
 
     case NS_CONNECT:
+      if (nc->user_data != NULL) {
+        set_ips(nc, 1);
+        set_ips(nc, 0);
+      }
       conn->mg_conn.status_code = * (int *) p;
       if (conn->mg_conn.status_code != 0 ||
-          call_user(conn, MG_CONNECT) == MG_FALSE) {
+          (!(nc->flags & MG_PROXY_CONN) &&
+           call_user(conn, MG_CONNECT) == MG_FALSE)) {
         nc->flags |= NSF_CLOSE_IMMEDIATELY;
       }
       break;
 
     case NS_RECV:
-      if (nc->flags & NSF_ACCEPTED) {
-        process_request(conn);
+      if (conn != NULL) {
+        conn->num_bytes_recv += * (int *) p;
+      }
+
+      if (nc->flags & NSF_UDP) {
+        process_udp(nc);
+      } else if (nc->listener != NULL) {
+        on_recv_data(conn);
 #ifndef MONGOOSE_NO_CGI
       } else if (nc->flags & MG_CGI_CONN) {
         on_cgi_data(nc);
 #endif
+      } else if (nc->flags & MG_PROXY_CONN) {
+        if (conn != NULL) {
+          ns_forward(nc, conn->ns_conn);
+        }
       } else {
         process_response(conn);
       }
@@ -4747,14 +5143,17 @@ static void mg_ev_handler(struct ns_connection *nc, enum ns_event ev, void *p) {
       break;
 
     case NS_CLOSE:
-      nc->connection_data = NULL;
-      if ((nc->flags & MG_CGI_CONN) && conn && conn->ns_conn) {
-        conn->ns_conn->flags &= ~NSF_BUFFER_BUT_DONT_SEND;
-        conn->ns_conn->flags |= conn->ns_conn->send_iobuf.len > 0 ?
-          NSF_FINISHED_SENDING_DATA : NSF_CLOSE_IMMEDIATELY;
-        conn->endpoint.cgi_conn = NULL;
+      nc->user_data = NULL;
+      if (nc->flags & (MG_CGI_CONN | MG_PROXY_CONN)) {
+        DBG(("%p %p closing cgi/proxy conn", conn, nc));
+        if (conn && conn->ns_conn) {
+          conn->ns_conn->flags &= ~NSF_BUFFER_BUT_DONT_SEND;
+          conn->ns_conn->flags |= conn->ns_conn->send_iobuf.len > 0 ?
+            NSF_FINISHED_SENDING_DATA : NSF_CLOSE_IMMEDIATELY;
+          conn->endpoint.nc = NULL;
+        }
       } else if (conn != NULL) {
-        DBG(("%p %d closing", conn, conn->endpoint_type));
+        DBG(("%p %p %d closing", conn, nc, conn->endpoint_type));
 
         if (conn->endpoint_type == EP_CLIENT && nc->recv_iobuf.len > 0) {
           call_http_client_handler(conn);
@@ -4762,17 +5161,23 @@ static void mg_ev_handler(struct ns_connection *nc, enum ns_event ev, void *p) {
 
         call_user(conn, MG_CLOSE);
         close_local_endpoint(conn);
+        conn->ns_conn = NULL;
         free(conn);
       }
       break;
 
     case NS_POLL:
-      if (call_user(conn, MG_POLL) == MG_TRUE) {
-        nc->flags |= NSF_FINISHED_SENDING_DATA;
-      }
+      if (conn != NULL) {
+        if (call_user(conn, MG_POLL) == MG_TRUE) {
+          if (conn->ns_conn->flags & MG_HEADERS_SENT) {
+            write_terminating_chunk(conn);
+          }
+          close_local_endpoint(conn);
+        }
 
-      if (conn != NULL && conn->endpoint_type == EP_FILE) {
-        transfer_file_data(conn);
+        if (conn->endpoint_type == EP_FILE) {
+          transfer_file_data(conn);
+        }
       }
 
       // Expire idle connections
@@ -4783,7 +5188,8 @@ static void mg_ev_handler(struct ns_connection *nc, enum ns_event ev, void *p) {
           ping_idle_websocket_connection(conn, current_time);
         }
 
-        if (nc->last_io_time + MONGOOSE_IDLE_TIMEOUT_SECONDS < current_time) {
+        if (nc->listener != NULL &&
+            nc->last_io_time + MONGOOSE_IDLE_TIMEOUT_SECONDS < current_time) {
           mg_ev_handler(nc, NS_CLOSE, NULL);
           nc->flags |= NSF_CLOSE_IMMEDIATELY;
         }
@@ -4795,19 +5201,38 @@ static void mg_ev_handler(struct ns_connection *nc, enum ns_event ev, void *p) {
   }
 }
 
-void mg_wakeup_server(struct mg_server *server) {
-  ns_server_wakeup(&server->ns_server);
-}
+static void iter2(struct ns_connection *nc, int ev, void *param) {
+  mg_handler_t func = NULL;
+  struct connection *conn = (struct connection *) nc->user_data;
+  const char *msg = (const char *) param;
+  int n;
+  (void) ev;
 
-void mg_set_listening_socket(struct mg_server *server, int sock) {
-  if (server->ns_server.listening_sock != INVALID_SOCKET) {
-    closesocket(server->ns_server.listening_sock);
+  //DBG(("%p [%s]", conn, msg));
+  if (sscanf(msg, "%p %n", &func, &n) && func != NULL) {
+    conn->mg_conn.callback_param = (void *) (msg + n);
+    func(&conn->mg_conn, MG_POLL);
   }
-  server->ns_server.listening_sock = (sock_t) sock;
 }
 
-int mg_get_listening_socket(struct mg_server *server) {
-  return server->ns_server.listening_sock;
+void mg_wakeup_server_ex(struct mg_server *server, mg_handler_t cb,
+                         const char *fmt, ...) {
+  va_list ap;
+  char buf[8 * 1024];
+  int len;
+
+  // Encode callback (cb) into a buffer
+  len = snprintf(buf, sizeof(buf), "%p ", cb);
+  va_start(ap, fmt);
+  len += vsnprintf(buf + len, sizeof(buf) - len, fmt, ap);
+  va_end(ap);
+
+  // "len + 1" is to include terminating \0 in the message
+  ns_broadcast(&server->ns_mgr, iter2, buf, len + 1);
+}
+
+void mg_wakeup_server(struct mg_server *server) {
+  ns_broadcast(&server->ns_mgr, NULL, (void *) "", 0);
 }
 
 const char *mg_get_option(const struct mg_server *server, const char *name) {
@@ -4818,7 +5243,7 @@ const char *mg_get_option(const struct mg_server *server, const char *name) {
 
 struct mg_server *mg_create_server(void *server_data, mg_handler_t handler) {
   struct mg_server *server = (struct mg_server *) calloc(1, sizeof(*server));
-  ns_server_init(&server->ns_server, server_data, mg_ev_handler);
+  ns_mgr_init(&server->ns_mgr, server_data);
   set_default_option_values(server->config_options);
   server->event_handler = handler;
   return server;
index ca717591844e7d653b9ccb45c7b62fee7d83a0cb..96c8c90b1f7b507c11d79650b03c4b607a90452a 100644 (file)
-// Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>\r
-// Copyright (c) 2013-2014 Cesanta Software Limited\r
-// All rights reserved\r
-//\r
-// This library is dual-licensed: you can redistribute it and/or modify\r
-// it under the terms of the GNU General Public License version 2 as\r
-// published by the Free Software Foundation. For the terms of this\r
-// license, see <http://www.gnu.org/licenses/>.\r
-//\r
-// You are free to use this library under the terms of the GNU General\r
-// Public License, but WITHOUT ANY WARRANTY; without even the implied\r
-// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\r
-// See the GNU General Public License for more details.\r
-//\r
-// Alternatively, you can license this library under a commercial\r
-// license, as set out in <http://cesanta.com/>.\r
-//\r
-// NOTE: Detailed API documentation is at http://cesanta.com/#docs\r
-\r
-#ifndef MONGOOSE_HEADER_INCLUDED\r
-#define  MONGOOSE_HEADER_INCLUDED\r
-\r
-#define MONGOOSE_VERSION "5.3"\r
-\r
-#include <stdio.h>      // required for FILE\r
-#include <stddef.h>     // required for size_t\r
-\r
-#ifdef __cplusplus\r
-extern "C" {\r
-#endif // __cplusplus\r
-\r
-// This structure contains information about HTTP request.\r
-struct mg_connection {\r
-  const char *request_method; // "GET", "POST", etc\r
-  const char *uri;            // URL-decoded URI\r
-  const char *http_version;   // E.g. "1.0", "1.1"\r
-  const char *query_string;   // URL part after '?', not including '?', or NULL\r
-\r
-  char remote_ip[48];         // Max IPv6 string length is 45 characters\r
-  const char *local_ip;       // Local IP address\r
-  unsigned short remote_port; // Client's port\r
-  unsigned short local_port;  // Local port number\r
-\r
-  int num_headers;            // Number of HTTP headers\r
-  struct mg_header {\r
-    const char *name;         // HTTP header name\r
-    const char *value;        // HTTP header value\r
-  } http_headers[30];\r
-\r
-  char *content;              // POST (or websocket message) data, or NULL\r
-  size_t content_len;       // content length\r
-\r
-  int is_websocket;           // Connection is a websocket connection\r
-  int status_code;            // HTTP status code for HTTP error handler\r
-  int wsbits;                 // First byte of the websocket frame\r
-  void *server_param;         // Parameter passed to mg_add_uri_handler()\r
-  void *connection_param;     // Placeholder for connection-specific data\r
-};\r
-\r
-struct mg_server; // Opaque structure describing server instance\r
-enum mg_result { MG_FALSE, MG_TRUE, MG_MORE };\r
-enum mg_event {\r
-  MG_POLL = 100,  // Callback return value is ignored\r
-  MG_CONNECT,     // If callback returns MG_FALSE, connect fails\r
-  MG_AUTH,        // If callback returns MG_FALSE, authentication fails\r
-  MG_REQUEST,     // If callback returns MG_FALSE, Mongoose continues with req\r
-  MG_REPLY,       // If callback returns MG_FALSE, Mongoose closes connection\r
-  MG_CLOSE,       // Connection is closed\r
-  MG_LUA,         // Called before LSP page invoked\r
-  MG_HTTP_ERROR   // If callback returns MG_FALSE, Mongoose continues with err\r
-};\r
-typedef int (*mg_handler_t)(struct mg_connection *, enum mg_event);\r
-\r
-// Server management functions\r
-struct mg_server *mg_create_server(void *server_param, mg_handler_t handler);\r
-void mg_destroy_server(struct mg_server **);\r
-const char *mg_set_option(struct mg_server *, const char *opt, const char *val);\r
-int mg_poll_server(struct mg_server *, int milliseconds);\r
-const char **mg_get_valid_option_names(void);\r
-const char *mg_get_option(const struct mg_server *server, const char *name);\r
-void mg_set_listening_socket(struct mg_server *, int sock);\r
-int mg_get_listening_socket(struct mg_server *);\r
-void mg_iterate_over_connections(struct mg_server *, mg_handler_t);\r
-void mg_wakeup_server(struct mg_server *);\r
-struct mg_connection *mg_connect(struct mg_server *, const char *, int, int);\r
-\r
-// Connection management functions\r
-void mg_send_status(struct mg_connection *, int status_code);\r
-void mg_send_header(struct mg_connection *, const char *name, const char *val);\r
-void mg_send_data(struct mg_connection *, const void *data, int data_len);\r
-void mg_printf_data(struct mg_connection *, const char *format, ...);\r
-\r
-int mg_websocket_write(struct mg_connection *, int opcode,\r
-                       const char *data, size_t data_len);\r
-\r
-// Deprecated in favor of mg_send_* interface\r
-int mg_write(struct mg_connection *, const void *buf, int len);\r
-int mg_printf(struct mg_connection *conn, const char *fmt, ...);\r
-\r
-const char *mg_get_header(const struct mg_connection *, const char *name);\r
-const char *mg_get_mime_type(const char *name, const char *default_mime_type);\r
-int mg_get_var(const struct mg_connection *conn, const char *var_name,\r
-               char *buf, size_t buf_len);\r
-int mg_parse_header(const char *hdr, const char *var_name, char *buf, size_t);\r
-int mg_parse_multipart(const char *buf, int buf_len,\r
-                       char *var_name, int var_name_len,\r
-                       char *file_name, int file_name_len,\r
-                       const char **data, int *data_len);\r
-\r
-// Utility functions\r
-void *mg_start_thread(void *(*func)(void *), void *param);\r
-char *mg_md5(char buf[33], ...);\r
-int mg_authorize_digest(struct mg_connection *c, FILE *fp);\r
-\r
-// Lua utility functions\r
-#ifdef MONGOOSE_USE_LUA\r
-#include <lua.h>\r
-#include <lauxlib.h>\r
-void reg_string(lua_State *L, const char *name, const char *val);\r
-void reg_int(lua_State *L, const char *name, int val);\r
-void reg_function(lua_State *L, const char *,\r
-                         lua_CFunction, struct mg_connection *);\r
-#endif\r
-\r
-#ifdef __cplusplus\r
-}\r
-#endif // __cplusplus\r
-\r
-#endif // MONGOOSE_HEADER_INCLUDED\r
+// Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
+// Copyright (c) 2013-2014 Cesanta Software Limited
+// All rights reserved
+//
+// This library 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 library 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 library under a commercial
+// license, as set out in <http://cesanta.com/>.
+//
+// $Date: 2014-09-09 17:07:55 UTC $
+
+#ifndef MONGOOSE_HEADER_INCLUDED
+#define  MONGOOSE_HEADER_INCLUDED
+
+#define MONGOOSE_VERSION "5.5"
+
+#include <stdio.h>      // required for FILE
+#include <stddef.h>     // required for size_t
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+// This structure contains information about HTTP request.
+struct mg_connection {
+  const char *request_method; // "GET", "POST", etc
+  const char *uri;            // URL-decoded URI
+  const char *http_version;   // E.g. "1.0", "1.1"
+  const char *query_string;   // URL part after '?', not including '?', or NULL
+
+  char remote_ip[48];         // Max IPv6 string length is 45 characters
+  char local_ip[48];          // Local IP address
+  unsigned short remote_port; // Client's port
+  unsigned short local_port;  // Local port number
+
+  int num_headers;            // Number of HTTP headers
+  struct mg_header {
+    const char *name;         // HTTP header name
+    const char *value;        // HTTP header value
+  } http_headers[30];
+
+  char *content;              // POST (or websocket message) data, or NULL
+  size_t content_len;         // Data length
+
+  int is_websocket;           // Connection is a websocket connection
+  int status_code;            // HTTP status code for HTTP error handler
+  int wsbits;                 // First byte of the websocket frame
+  void *server_param;         // Parameter passed to mg_add_uri_handler()
+  void *connection_param;     // Placeholder for connection-specific data
+  void *callback_param;
+};
+
+struct mg_server; // Opaque structure describing server instance
+enum mg_result { MG_FALSE, MG_TRUE, MG_MORE };
+enum mg_event {
+  MG_POLL = 100,  // Callback return value is ignored
+  MG_CONNECT,     // If callback returns MG_FALSE, connect fails
+  MG_AUTH,        // If callback returns MG_FALSE, authentication fails
+  MG_REQUEST,     // If callback returns MG_FALSE, Mongoose continues with req
+  MG_REPLY,       // If callback returns MG_FALSE, Mongoose closes connection
+  MG_RECV,        // Mongoose has received POST data chunk.
+                  // Callback should return a number of bytes to discard from
+                  // the receive buffer, or -1 to close the connection.
+  MG_CLOSE,       // Connection is closed, callback return value is ignored
+  MG_WS_HANDSHAKE,  // New websocket connection, handshake request
+  MG_WS_CONNECT,  // New websocket connection established
+  MG_HTTP_ERROR   // If callback returns MG_FALSE, Mongoose continues with err
+};
+typedef int (*mg_handler_t)(struct mg_connection *, enum mg_event);
+
+// Websocket opcodes, from http://tools.ietf.org/html/rfc6455
+enum {
+  WEBSOCKET_OPCODE_CONTINUATION = 0x0,
+  WEBSOCKET_OPCODE_TEXT = 0x1,
+  WEBSOCKET_OPCODE_BINARY = 0x2,
+  WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8,
+  WEBSOCKET_OPCODE_PING = 0x9,
+  WEBSOCKET_OPCODE_PONG = 0xa
+};
+
+// Server management functions
+struct mg_server *mg_create_server(void *server_param, mg_handler_t handler);
+void mg_destroy_server(struct mg_server **);
+const char *mg_set_option(struct mg_server *, const char *opt, const char *val);
+int mg_poll_server(struct mg_server *, int milliseconds);
+const char **mg_get_valid_option_names(void);
+const char *mg_get_option(const struct mg_server *server, const char *name);
+void mg_copy_listeners(struct mg_server *from, struct mg_server *to);
+struct mg_connection *mg_next(struct mg_server *, struct mg_connection *);
+void mg_wakeup_server(struct mg_server *);
+void mg_wakeup_server_ex(struct mg_server *, mg_handler_t, const char *, ...);
+struct mg_connection *mg_connect(struct mg_server *, const char *);
+
+// Connection management functions
+void mg_send_status(struct mg_connection *, int status_code);
+void mg_send_header(struct mg_connection *, const char *name, const char *val);
+size_t mg_send_data(struct mg_connection *, const void *data, int data_len);
+size_t mg_printf_data(struct mg_connection *, const char *format, ...);
+size_t mg_write(struct mg_connection *, const void *buf, int len);
+size_t mg_printf(struct mg_connection *conn, const char *fmt, ...);
+
+size_t mg_websocket_write(struct mg_connection *, int opcode,
+                          const char *data, size_t data_len);
+size_t mg_websocket_printf(struct mg_connection* conn, int opcode,
+                           const char *fmt, ...);
+
+void mg_send_file(struct mg_connection *, const char *path, const char *);
+void mg_send_file_data(struct mg_connection *, int fd);
+
+const char *mg_get_header(const struct mg_connection *, const char *name);
+const char *mg_get_mime_type(const char *name, const char *default_mime_type);
+int mg_get_var(const struct mg_connection *conn, const char *var_name,
+               char *buf, size_t buf_len);
+int mg_parse_header(const char *hdr, const char *var_name, char *buf, size_t);
+int mg_parse_multipart(const char *buf, int buf_len,
+                       char *var_name, int var_name_len,
+                       char *file_name, int file_name_len,
+                       const char **data, int *data_len);
+
+
+// Utility functions
+void *mg_start_thread(void *(*func)(void *), void *param);
+char *mg_md5(char buf[33], ...);
+int mg_authorize_digest(struct mg_connection *c, FILE *fp);
+int mg_url_encode(const char *src, size_t s_len, char *dst, size_t dst_len);
+int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, int);
+int mg_terminate_ssl(struct mg_connection *c, const char *cert);
+int mg_forward(struct mg_connection *c, const char *addr);
+void *mg_mmap(FILE *fp, size_t size);
+void mg_munmap(void *p, size_t size);
+
+
+// Templates support
+struct mg_expansion {
+  const char *keyword;
+  void (*handler)(struct mg_connection *);
+};
+void mg_template(struct mg_connection *, const char *text,
+                 struct mg_expansion *expansions);
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif // MONGOOSE_HEADER_INCLUDED
index 81f8c9aac2802fde3bbe7af4831ebfc275d84978..38860930dd6e706cb40d96aa9c28160fc2dcbd9e 100644 (file)
@@ -596,8 +596,6 @@ int MongooseHttpd::staticRequestHandler(struct mg_connection * connection, mg_ev
     case MG_HTTP_ERROR:  // If callback returns MG_FALSE, Mongoose continues with err 
       return MG_FALSE;   // we don't handle errors - let mongoose do the work
 
-      // LUA not implemented - and will go away anyway
-    case MG_LUA:         // Called before LSP page invoked   
       // client services not used/implemented. Signal 'close connection' to be sure
     case MG_CONNECT:     // If callback returns MG_FALSE, connect fails
     case MG_REPLY:       // If callback returns MG_FALSE, Mongoose closes connection