From 6083b9c5c534b47a36b260b45ce9d2e4cc4ec5e2 Mon Sep 17 00:00:00 2001
From: Sergey Lyubka <valenok@gmail.com>
Date: Tue, 9 Sep 2014 18:07:55 +0100
Subject: [PATCH] Updated to the recent skeleton. SSL address format changed

---
 examples/server.c                             | 1016 +++++++++--------
 examples/unit_test.c                          |    9 +-
 examples/websocket_chat/websocket_chat.c      |    2 +-
 .../websocket_echo_server.c                   |    2 +-
 examples/ws_ssl/Makefile                      |   10 +-
 examples/ws_ssl/net_skeleton.c                |    2 +-
 examples/ws_ssl/net_skeleton.h                |    2 +-
 examples/ws_ssl/ssl_wrapper.c                 |    2 +-
 examples/ws_ssl/ssl_wrapper.h                 |    2 +-
 mongoose.c                                    |  671 +++++------
 mongoose.h                                    |   11 +-
 11 files changed, 873 insertions(+), 856 deletions(-)

diff --git a/examples/server.c b/examples/server.c
index 7c524cecc..c46f12d1e 100644
--- a/examples/server.c
+++ b/examples/server.c
@@ -1,507 +1,509 @@
-// Copyright (c) 2004-2013 Sergey Lyubka
-// Copyright (c) 2013-2014 Cesanta Software Limited
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-#undef UNICODE                    // Use ANSI WinAPI functions
-#undef _UNICODE                   // Use multibyte encoding on Windows
-#define _MBCS                     // Use multibyte encoding on Windows
-#define _WIN32_WINNT 0x500        // Enable MIIM_BITMAP
-#define _CRT_SECURE_NO_WARNINGS   // Disable deprecation warning in VS2005
-#define _XOPEN_SOURCE 600         // For PATH_MAX on linux
-#undef WIN32_LEAN_AND_MEAN        // Let windows.h always include winsock2.h
-
-#include <sys/stat.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <string.h>
-#include <errno.h>
-#include <limits.h>
-#include <stddef.h>
-#include <stdarg.h>
-#include <ctype.h>
-#include <time.h>
-
-#include "mongoose.h"
-
-#ifdef _WIN32
-#include <windows.h>
-#include <direct.h>  // For chdir()
-#include <winsvc.h>
-#include <shlobj.h>
-
-#ifndef PATH_MAX
-#define PATH_MAX MAX_PATH
-#endif
-
-#ifndef S_ISDIR
-#define S_ISDIR(x) ((x) & _S_IFDIR)
-#endif
-
-#define DIRSEP '\\'
-#define snprintf _snprintf
-#define vsnprintf _vsnprintf
-#define sleep(x) Sleep((x) * 1000)
-#define abs_path(rel, abs, abs_size) _fullpath((abs), (rel), (abs_size))
-#define SIGCHLD 0
-typedef struct _stat file_stat_t;
-#define stat(x, y) _stat((x), (y))
-#else
-typedef struct stat file_stat_t;
-#include <sys/wait.h>
-#include <unistd.h>
-
-#ifdef IOS
-#include <ifaddrs.h>
-#endif
-
-#define DIRSEP '/'
-#define __cdecl
-#define abs_path(rel, abs, abs_size) realpath((rel), (abs))
-#endif // _WIN32
-
-#define MAX_OPTIONS 100
-#define MAX_CONF_FILE_LINE_SIZE (8 * 1024)
-
-#ifndef MVER
-#define MVER MONGOOSE_VERSION
-#endif
-
-static int exit_flag;
-static char server_name[50];        // Set by init_server_name()
-static char s_config_file[PATH_MAX];  // Set by process_command_line_arguments
-static struct mg_server *server;    // Set by start_mongoose()
-static const char *s_default_document_root = ".";
-static const char *s_default_listening_port = "8080";
-static char **s_argv = { NULL };
-
-static void set_options(char *argv[]);
-
-#if !defined(CONFIG_FILE)
-#define CONFIG_FILE "mongoose.conf"
-#endif /* !CONFIG_FILE */
-
-static void __cdecl signal_handler(int sig_num) {
-  // Reinstantiate signal handler
-  signal(sig_num, signal_handler);
-
-#ifndef _WIN32
-  // Do not do the trick with ignoring SIGCHLD, cause not all OSes (e.g. QNX)
-  // reap zombies if SIGCHLD is ignored. On QNX, for example, waitpid()
-  // fails if SIGCHLD is ignored, making system() non-functional.
-  if (sig_num == SIGCHLD) {
-    do {} while (waitpid(-1, &sig_num, WNOHANG) > 0);
-  } else
-#endif
-  { exit_flag = sig_num; }
-}
-
-static void vnotify(const char *fmt, va_list ap, int must_exit) {
-  char msg[200];
-
-  vsnprintf(msg, sizeof(msg), fmt, ap);
-  fprintf(stderr, "%s\n", msg);
-
-  if (must_exit) {
-    exit(EXIT_FAILURE);
-  }
-}
-
-static void notify(const char *fmt, ...) {
-  va_list ap;
-  va_start(ap, fmt);
-  vnotify(fmt, ap, 0);
-  va_end(ap);
-}
-
-static void die(const char *fmt, ...) {
-  va_list ap;
-  va_start(ap, fmt);
-  vnotify(fmt, ap, 1);
-  va_end(ap);
-}
-
-static void show_usage_and_exit(void) {
-  const char **names;
-  int i;
-
-  fprintf(stderr, "Mongoose version %s (c) Sergey Lyubka, built on %s\n",
-          MVER, __DATE__);
-  fprintf(stderr, "Usage:\n");
-#if !defined(MONGOOSE_NO_AUTH) && !defined(MONGOOSE_NO_FILESYSTEM)
-  fprintf(stderr, "  mongoose -A <htpasswd_file> <realm> <user> <passwd>\n");
-#endif
-  fprintf(stderr, "  mongoose [config_file]\n");
-  fprintf(stderr, "  mongoose [-option value ...]\n");
-  fprintf(stderr, "\nOPTIONS:\n");
-
-  names = mg_get_valid_option_names();
-  for (i = 0; names[i] != NULL; i += 2) {
-    fprintf(stderr, "  -%s %s\n",
-            names[i], names[i + 1] == NULL ? "<empty>" : names[i + 1]);
-  }
-  exit(EXIT_FAILURE);
-}
-
-#define EV_HANDLER NULL
-
-static char *sdup(const char *str) {
-  char *p;
-  if ((p = (char *) malloc(strlen(str) + 1)) != NULL) {
-    strcpy(p, str);
-  }
-  return p;
-}
-
-static void set_option(char **options, const char *name, const char *value) {
-  int i;
-
-  for (i = 0; i < MAX_OPTIONS - 3; i++) {
-    if (options[i] == NULL) {
-      options[i] = sdup(name);
-      options[i + 1] = sdup(value);
-      options[i + 2] = NULL;
-      break;
-    } else if (!strcmp(options[i], name)) {
-      free(options[i + 1]);
-      options[i + 1] = sdup(value);
-      break;
-    }
-  }
-
-  if (i == MAX_OPTIONS - 3) {
-    die("%s", "Too many options specified");
-  }
-}
-
-static void process_command_line_arguments(char *argv[], char **options) {
-  char line[MAX_CONF_FILE_LINE_SIZE], opt[sizeof(line)], val[sizeof(line)],
-       *p, cpath[PATH_MAX];
-  FILE *fp = NULL;
-  size_t i, cmd_line_opts_start = 1, line_no = 0;
-
-  // Should we use a config file ?
-  if (argv[1] != NULL && argv[1][0] != '-') {
-    snprintf(cpath, sizeof(cpath), "%s", argv[1]);
-    cmd_line_opts_start = 2;
-  } else if ((p = strrchr(argv[0], DIRSEP)) == NULL) {
-    // No command line flags specified. Look where binary lives
-    snprintf(cpath, sizeof(cpath), "%s", CONFIG_FILE);
-  } else {
-    snprintf(cpath, sizeof(cpath), "%.*s%c%s",
-             (int) (p - argv[0]), argv[0], DIRSEP, CONFIG_FILE);
-  }
-  abs_path(cpath, s_config_file, sizeof(s_config_file));
-
-  fp = fopen(s_config_file, "r");
-
-  // If config file was set in command line and open failed, die
-  if (cmd_line_opts_start == 2 && fp == NULL) {
-    die("Cannot open config file %s: %s", s_config_file, strerror(errno));
-  }
-
-  // Load config file settings first
-  if (fp != NULL) {
-    fprintf(stderr, "Loading config file %s\n", s_config_file);
-
-    // Loop over the lines in config file
-    while (fgets(line, sizeof(line), fp) != NULL) {
-      line_no++;
-
-      // Ignore empty lines and comments
-      for (i = 0; isspace(* (unsigned char *) &line[i]); ) i++;
-      if (line[i] == '#' || line[i] == '\0') {
-        continue;
-      }
-
-      if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) {
-        printf("%s: line %d is invalid, ignoring it:\n %s",
-               s_config_file, (int) line_no, line);
-      } else {
-        set_option(options, opt, val);
-      }
-    }
-
-    fclose(fp);
-  }
-
-  // If we're under MacOS and started by launchd, then the second
-  // argument is process serial number, -psn_.....
-  // In this case, don't process arguments at all.
-  if (argv[1] == NULL || memcmp(argv[1], "-psn_", 5) != 0) {
-    // Handle command line flags.
-    // They override config file and default settings.
-    for (i = cmd_line_opts_start; argv[i] != NULL; i += 2) {
-      if (argv[i][0] != '-' || argv[i + 1] == NULL) {
-        show_usage_and_exit();
-      }
-      set_option(options, &argv[i][1], argv[i + 1]);
-    }
-  }
-}
-
-static void init_server_name(void) {
-  const char *descr = "";
-  snprintf(server_name, sizeof(server_name), "Mongoose web server v.%s%s",
-           MVER, descr);
-}
-
-static int is_path_absolute(const char *path) {
-#ifdef _WIN32
-  return path != NULL &&
-    ((path[0] == '\\' && path[1] == '\\') ||  // UNC path, e.g. \\server\dir
-     (isalpha(path[0]) && path[1] == ':' && path[2] == '\\'));  // E.g. X:\dir
-#else
-  return path != NULL && path[0] == '/';
-#endif
-}
-
-static char *get_option(char **options, const char *option_name) {
-  int i;
-
-  for (i = 0; options[i] != NULL; i++)
-    if (!strcmp(options[i], option_name))
-      return options[i + 1];
-
-  return NULL;
-}
-
-static void *serving_thread_func(void *param) {
-  struct mg_server *srv = (struct mg_server *) param;
-  while (exit_flag == 0) {
-    mg_poll_server(srv, 1000);
-  }
-  return NULL;
-}
-
-static int path_exists(const char *path, int is_dir) {
-  file_stat_t st;
-  return path == NULL || (stat(path, &st) == 0 &&
-                          ((S_ISDIR(st.st_mode) ? 1 : 0) == is_dir));
-}
-
-static void verify_existence(char **options, const char *name, int is_dir) {
-  const char *path = get_option(options, name);
-  if (!path_exists(path, is_dir)) {
-    notify("Invalid path for %s: [%s]: (%s). Make sure that path is either "
-           "absolute, or it is relative to mongoose executable.",
-           name, path, strerror(errno));
-  }
-}
-
-static void set_absolute_path(char *options[], const char *option_name) {
-  char path[PATH_MAX], abs[PATH_MAX], *option_value;
-  const char *p;
-
-  // Check whether option is already set
-  option_value = get_option(options, option_name);
-
-  // If option is already set and it is an absolute path,
-  // leave it as it is -- it's already absolute.
-  if (option_value != NULL && !is_path_absolute(option_value)) {
-    // Not absolute. Use the directory where mongoose executable lives
-    // be the relative directory for everything.
-    // Extract mongoose executable directory into path.
-    if ((p = strrchr(s_config_file, DIRSEP)) == NULL) {
-      getcwd(path, sizeof(path));
-    } else {
-      snprintf(path, sizeof(path), "%.*s", (int) (p - s_config_file),
-               s_config_file);
-    }
-
-    strncat(path, "/", sizeof(path) - 1);
-    strncat(path, option_value, sizeof(path) - 1);
-
-    // Absolutize the path, and set the option
-    abs_path(path, abs, sizeof(abs));
-    set_option(options, option_name, abs);
-  }
-}
-
-#if !defined(MONGOOSE_NO_AUTH) && !defined(MONGOOSE_NO_FILESYSTEM)
-int modify_passwords_file(const char *fname, const char *domain,
-                          const char *user, const char *pass) {
-  int found;
-  char line[512], u[512], d[512], ha1[33], tmp[PATH_MAX];
-  FILE *fp, *fp2;
-
-  found = 0;
-  fp = fp2 = NULL;
-
-  // Regard empty password as no password - remove user record.
-  if (pass != NULL && pass[0] == '\0') {
-    pass = NULL;
-  }
-
-  (void) snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
-
-  // Create the file if does not exist
-  if ((fp = fopen(fname, "a+")) != NULL) {
-    fclose(fp);
-  }
-
-  // Open the given file and temporary file
-  if ((fp = fopen(fname, "r")) == NULL) {
-    return 0;
-  } else if ((fp2 = fopen(tmp, "w+")) == NULL) {
-    fclose(fp);
-    return 0;
-  }
-
-  // Copy the stuff to temporary file
-  while (fgets(line, sizeof(line), fp) != NULL) {
-    if (sscanf(line, "%[^:]:%[^:]:%*s", u, d) != 2) {
-      continue;
-    }
-
-    if (!strcmp(u, user) && !strcmp(d, domain)) {
-      found++;
-      if (pass != NULL) {
-        mg_md5(ha1, user, ":", domain, ":", pass, NULL);
-        fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
-      }
-    } else {
-      fprintf(fp2, "%s", line);
-    }
-  }
-
-  // If new user, just add it
-  if (!found && pass != NULL) {
-    mg_md5(ha1, user, ":", domain, ":", pass, NULL);
-    fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
-  }
-
-  // Close files
-  fclose(fp);
-  fclose(fp2);
-
-  // Put the temp file in place of real file
-  remove(fname);
-  rename(tmp, fname);
-
-  return 1;
-}
-#endif
-
-static void start_mongoose(int argc, char *argv[]) {
-  s_argv = argv;
-  if ((server = mg_create_server(NULL, EV_HANDLER)) == NULL) {
-    die("%s", "Failed to start Mongoose.");
-  }
-
-#if !defined(MONGOOSE_NO_AUTH) && !defined(MONGOOSE_NO_FILESYSTEM)
-  // Edit passwords file if -A option is specified
-  if (argc > 1 && !strcmp(argv[1], "-A")) {
-    if (argc != 6) {
-      show_usage_and_exit();
-    }
-    exit(modify_passwords_file(argv[2], argv[3], argv[4], argv[5]) ?
-         EXIT_SUCCESS : EXIT_FAILURE);
-  }
-#endif
-
-  // Show usage if -h or --help options are specified
-  if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) {
-    show_usage_and_exit();
-  }
-  set_options(argv);
-}
-
-static void set_options(char *argv[]) {
-  char *options[MAX_OPTIONS];
-  int i;
-
-  options[0] = NULL;
-  set_option(options, "document_root", s_default_document_root);
-  set_option(options, "listening_port", s_default_listening_port);
-
-  // Update config based on command line arguments
-  process_command_line_arguments(argv, options);
-
-  // Make sure we have absolute paths for files and directories
-  // https://github.com/valenok/mongoose/issues/181
-  set_absolute_path(options, "document_root");
-  set_absolute_path(options, "dav_auth_file");
-  set_absolute_path(options, "cgi_interpreter");
-  set_absolute_path(options, "access_log_file");
-  set_absolute_path(options, "global_auth_file");
-  set_absolute_path(options, "ssl_certificate");
-
-  if (!path_exists(get_option(options, "document_root"), 1)) {
-    set_option(options, "document_root", s_default_document_root);
-    set_absolute_path(options, "document_root");
-    notify("Setting document_root to [%s]",
-           mg_get_option(server, "document_root"));
-  }
-
-  // Make extra verification for certain options
-  verify_existence(options, "document_root", 1);
-  verify_existence(options, "cgi_interpreter", 0);
-  verify_existence(options, "ssl_certificate", 0);
-
-  for (i = 0; options[i] != NULL; i += 2) {
-    const char *msg = mg_set_option(server, options[i], options[i + 1]);
-    if (msg != NULL) {
-      notify("Failed to set option [%s] to [%s]: %s",
-             options[i], options[i + 1], msg);
-      if (!strcmp(options[i], "listening_port")) {
-        mg_set_option(server, "listening_port", s_default_listening_port);
-        notify("Setting %s to [%s]", options[i], s_default_listening_port);
-      }
-    }
-    free(options[i]);
-    free(options[i + 1]);
-  }
-
-  // Change current working directory to document root. This way,
-  // scripts can use relative paths.
-  chdir(mg_get_option(server, "document_root"));
-
-  // Add an ability to pass listening socket to mongoose
-  {
-    const char *env = getenv("MONGOOSE_LISTENING_SOCKET");
-    if (env != NULL && atoi(env) > 0 ) {
-      mg_set_listening_socket(server, atoi(env));
-    }
-  }
-
-  // Setup signal handler: quit on Ctrl-C
-  signal(SIGTERM, signal_handler);
-  signal(SIGINT, signal_handler);
-#ifndef _WIN32
-  signal(SIGCHLD, signal_handler);
-#endif
-}
-
-int main(int argc, char *argv[]) {
-  init_server_name();
-  start_mongoose(argc, argv);
-  printf("%s serving [%s] on port %s\n",
-         server_name, mg_get_option(server, "document_root"),
-         mg_get_option(server, "listening_port"));
-  fflush(stdout);  // Needed, Windows terminals might not be line-buffered
-  serving_thread_func(server);
-  printf("Exiting on signal %d ...", exit_flag);
-  fflush(stdout);
-  mg_destroy_server(&server);
-  printf("%s\n", " done.");
-
-  return EXIT_SUCCESS;
-}
+// Copyright (c) 2004-2013 Sergey Lyubka
+// Copyright (c) 2013-2014 Cesanta Software Limited
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#undef UNICODE                    // Use ANSI WinAPI functions
+#undef _UNICODE                   // Use multibyte encoding on Windows
+#define _MBCS                     // Use multibyte encoding on Windows
+#define _WIN32_WINNT 0x500        // Enable MIIM_BITMAP
+#define _CRT_SECURE_NO_WARNINGS   // Disable deprecation warning in VS2005
+#define _XOPEN_SOURCE 600         // For PATH_MAX on linux
+#undef WIN32_LEAN_AND_MEAN        // Let windows.h always include winsock2.h
+
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <time.h>
+
+#include "mongoose.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#include <direct.h>  // For chdir()
+#include <winsvc.h>
+#include <shlobj.h>
+
+#ifndef PATH_MAX
+#define PATH_MAX MAX_PATH
+#endif
+
+#ifndef S_ISDIR
+#define S_ISDIR(x) ((x) & _S_IFDIR)
+#endif
+
+#define DIRSEP '\\'
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#define sleep(x) Sleep((x) * 1000)
+#define abs_path(rel, abs, abs_size) _fullpath((abs), (rel), (abs_size))
+#define SIGCHLD 0
+typedef struct _stat file_stat_t;
+#define stat(x, y) _stat((x), (y))
+#else
+typedef struct stat file_stat_t;
+#include <sys/wait.h>
+#include <unistd.h>
+
+#ifdef IOS
+#include <ifaddrs.h>
+#endif
+
+#define DIRSEP '/'
+#define __cdecl
+#define abs_path(rel, abs, abs_size) realpath((rel), (abs))
+#endif // _WIN32
+
+#define MAX_OPTIONS 100
+#define MAX_CONF_FILE_LINE_SIZE (8 * 1024)
+
+#ifndef MVER
+#define MVER MONGOOSE_VERSION
+#endif
+
+static int exit_flag;
+static char server_name[50];        // Set by init_server_name()
+static char s_config_file[PATH_MAX];  // Set by process_command_line_arguments
+static struct mg_server *server;    // Set by start_mongoose()
+static const char *s_default_document_root = ".";
+static const char *s_default_listening_port = "8080";
+static char **s_argv = { NULL };
+
+static void set_options(char *argv[]);
+
+#if !defined(CONFIG_FILE)
+#define CONFIG_FILE "mongoose.conf"
+#endif /* !CONFIG_FILE */
+
+static void __cdecl signal_handler(int sig_num) {
+  // Reinstantiate signal handler
+  signal(sig_num, signal_handler);
+
+#ifndef _WIN32
+  // Do not do the trick with ignoring SIGCHLD, cause not all OSes (e.g. QNX)
+  // reap zombies if SIGCHLD is ignored. On QNX, for example, waitpid()
+  // fails if SIGCHLD is ignored, making system() non-functional.
+  if (sig_num == SIGCHLD) {
+    do {} while (waitpid(-1, &sig_num, WNOHANG) > 0);
+  } else
+#endif
+  { exit_flag = sig_num; }
+}
+
+static void vnotify(const char *fmt, va_list ap, int must_exit) {
+  char msg[200];
+
+  vsnprintf(msg, sizeof(msg), fmt, ap);
+  fprintf(stderr, "%s\n", msg);
+
+  if (must_exit) {
+    exit(EXIT_FAILURE);
+  }
+}
+
+static void notify(const char *fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  vnotify(fmt, ap, 0);
+  va_end(ap);
+}
+
+static void die(const char *fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  vnotify(fmt, ap, 1);
+  va_end(ap);
+}
+
+static void show_usage_and_exit(void) {
+  const char **names;
+  int i;
+
+  fprintf(stderr, "Mongoose version %s (c) Sergey Lyubka, built on %s\n",
+          MVER, __DATE__);
+  fprintf(stderr, "Usage:\n");
+#if !defined(MONGOOSE_NO_AUTH) && !defined(MONGOOSE_NO_FILESYSTEM)
+  fprintf(stderr, "  mongoose -A <htpasswd_file> <realm> <user> <passwd>\n");
+#endif
+  fprintf(stderr, "  mongoose [config_file]\n");
+  fprintf(stderr, "  mongoose [-option value ...]\n");
+  fprintf(stderr, "\nOPTIONS:\n");
+
+  names = mg_get_valid_option_names();
+  for (i = 0; names[i] != NULL; i += 2) {
+    fprintf(stderr, "  -%s %s\n",
+            names[i], names[i + 1] == NULL ? "<empty>" : names[i + 1]);
+  }
+  exit(EXIT_FAILURE);
+}
+
+#define EV_HANDLER NULL
+
+static char *sdup(const char *str) {
+  char *p;
+  if ((p = (char *) malloc(strlen(str) + 1)) != NULL) {
+    strcpy(p, str);
+  }
+  return p;
+}
+
+static void set_option(char **options, const char *name, const char *value) {
+  int i;
+
+  for (i = 0; i < MAX_OPTIONS - 3; i++) {
+    if (options[i] == NULL) {
+      options[i] = sdup(name);
+      options[i + 1] = sdup(value);
+      options[i + 2] = NULL;
+      break;
+    } else if (!strcmp(options[i], name)) {
+      free(options[i + 1]);
+      options[i + 1] = sdup(value);
+      break;
+    }
+  }
+
+  if (i == MAX_OPTIONS - 3) {
+    die("%s", "Too many options specified");
+  }
+}
+
+static void process_command_line_arguments(char *argv[], char **options) {
+  char line[MAX_CONF_FILE_LINE_SIZE], opt[sizeof(line)], val[sizeof(line)],
+       *p, cpath[PATH_MAX];
+  FILE *fp = NULL;
+  size_t i, cmd_line_opts_start = 1, line_no = 0;
+
+  // Should we use a config file ?
+  if (argv[1] != NULL && argv[1][0] != '-') {
+    snprintf(cpath, sizeof(cpath), "%s", argv[1]);
+    cmd_line_opts_start = 2;
+  } else if ((p = strrchr(argv[0], DIRSEP)) == NULL) {
+    // No command line flags specified. Look where binary lives
+    snprintf(cpath, sizeof(cpath), "%s", CONFIG_FILE);
+  } else {
+    snprintf(cpath, sizeof(cpath), "%.*s%c%s",
+             (int) (p - argv[0]), argv[0], DIRSEP, CONFIG_FILE);
+  }
+  abs_path(cpath, s_config_file, sizeof(s_config_file));
+
+  fp = fopen(s_config_file, "r");
+
+  // If config file was set in command line and open failed, die
+  if (cmd_line_opts_start == 2 && fp == NULL) {
+    die("Cannot open config file %s: %s", s_config_file, strerror(errno));
+  }
+
+  // Load config file settings first
+  if (fp != NULL) {
+    fprintf(stderr, "Loading config file %s\n", s_config_file);
+
+    // Loop over the lines in config file
+    while (fgets(line, sizeof(line), fp) != NULL) {
+      line_no++;
+
+      // Ignore empty lines and comments
+      for (i = 0; isspace(* (unsigned char *) &line[i]); ) i++;
+      if (line[i] == '#' || line[i] == '\0') {
+        continue;
+      }
+
+      if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) {
+        printf("%s: line %d is invalid, ignoring it:\n %s",
+               s_config_file, (int) line_no, line);
+      } else {
+        set_option(options, opt, val);
+      }
+    }
+
+    fclose(fp);
+  }
+
+  // If we're under MacOS and started by launchd, then the second
+  // argument is process serial number, -psn_.....
+  // In this case, don't process arguments at all.
+  if (argv[1] == NULL || memcmp(argv[1], "-psn_", 5) != 0) {
+    // Handle command line flags.
+    // They override config file and default settings.
+    for (i = cmd_line_opts_start; argv[i] != NULL; i += 2) {
+      if (argv[i][0] != '-' || argv[i + 1] == NULL) {
+        show_usage_and_exit();
+      }
+      set_option(options, &argv[i][1], argv[i + 1]);
+    }
+  }
+}
+
+static void init_server_name(void) {
+  const char *descr = "";
+  snprintf(server_name, sizeof(server_name), "Mongoose web server v.%s%s",
+           MVER, descr);
+}
+
+static int is_path_absolute(const char *path) {
+#ifdef _WIN32
+  return path != NULL &&
+    ((path[0] == '\\' && path[1] == '\\') ||  // UNC path, e.g. \\server\dir
+     (isalpha(path[0]) && path[1] == ':' && path[2] == '\\'));  // E.g. X:\dir
+#else
+  return path != NULL && path[0] == '/';
+#endif
+}
+
+static char *get_option(char **options, const char *option_name) {
+  int i;
+
+  for (i = 0; options[i] != NULL; i++)
+    if (!strcmp(options[i], option_name))
+      return options[i + 1];
+
+  return NULL;
+}
+
+static void *serving_thread_func(void *param) {
+  struct mg_server *srv = (struct mg_server *) param;
+  while (exit_flag == 0) {
+    mg_poll_server(srv, 1000);
+  }
+  return NULL;
+}
+
+static int path_exists(const char *path, int is_dir) {
+  file_stat_t st;
+  return path == NULL || (stat(path, &st) == 0 &&
+                          ((S_ISDIR(st.st_mode) ? 1 : 0) == is_dir));
+}
+
+static void verify_existence(char **options, const char *name, int is_dir) {
+  const char *path = get_option(options, name);
+  if (!path_exists(path, is_dir)) {
+    notify("Invalid path for %s: [%s]: (%s). Make sure that path is either "
+           "absolute, or it is relative to mongoose executable.",
+           name, path, strerror(errno));
+  }
+}
+
+static void set_absolute_path(char *options[], const char *option_name) {
+  char path[PATH_MAX], abs[PATH_MAX], *option_value;
+  const char *p;
+
+  // Check whether option is already set
+  option_value = get_option(options, option_name);
+
+  // If option is already set and it is an absolute path,
+  // leave it as it is -- it's already absolute.
+  if (option_value != NULL && !is_path_absolute(option_value)) {
+    // Not absolute. Use the directory where mongoose executable lives
+    // be the relative directory for everything.
+    // Extract mongoose executable directory into path.
+    if ((p = strrchr(s_config_file, DIRSEP)) == NULL) {
+      getcwd(path, sizeof(path));
+    } else {
+      snprintf(path, sizeof(path), "%.*s", (int) (p - s_config_file),
+               s_config_file);
+    }
+
+    strncat(path, "/", sizeof(path) - 1);
+    strncat(path, option_value, sizeof(path) - 1);
+
+    // Absolutize the path, and set the option
+    abs_path(path, abs, sizeof(abs));
+    set_option(options, option_name, abs);
+  }
+}
+
+#if !defined(MONGOOSE_NO_AUTH) && !defined(MONGOOSE_NO_FILESYSTEM)
+int modify_passwords_file(const char *fname, const char *domain,
+                          const char *user, const char *pass) {
+  int found;
+  char line[512], u[512], d[512], ha1[33], tmp[PATH_MAX];
+  FILE *fp, *fp2;
+
+  found = 0;
+  fp = fp2 = NULL;
+
+  // Regard empty password as no password - remove user record.
+  if (pass != NULL && pass[0] == '\0') {
+    pass = NULL;
+  }
+
+  (void) snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
+
+  // Create the file if does not exist
+  if ((fp = fopen(fname, "a+")) != NULL) {
+    fclose(fp);
+  }
+
+  // Open the given file and temporary file
+  if ((fp = fopen(fname, "r")) == NULL) {
+    return 0;
+  } else if ((fp2 = fopen(tmp, "w+")) == NULL) {
+    fclose(fp);
+    return 0;
+  }
+
+  // Copy the stuff to temporary file
+  while (fgets(line, sizeof(line), fp) != NULL) {
+    if (sscanf(line, "%[^:]:%[^:]:%*s", u, d) != 2) {
+      continue;
+    }
+
+    if (!strcmp(u, user) && !strcmp(d, domain)) {
+      found++;
+      if (pass != NULL) {
+        mg_md5(ha1, user, ":", domain, ":", pass, NULL);
+        fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
+      }
+    } else {
+      fprintf(fp2, "%s", line);
+    }
+  }
+
+  // If new user, just add it
+  if (!found && pass != NULL) {
+    mg_md5(ha1, user, ":", domain, ":", pass, NULL);
+    fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
+  }
+
+  // Close files
+  fclose(fp);
+  fclose(fp2);
+
+  // Put the temp file in place of real file
+  remove(fname);
+  rename(tmp, fname);
+
+  return 1;
+}
+#endif
+
+static void start_mongoose(int argc, char *argv[]) {
+  s_argv = argv;
+  if ((server = mg_create_server(NULL, EV_HANDLER)) == NULL) {
+    die("%s", "Failed to start Mongoose.");
+  }
+
+#if !defined(MONGOOSE_NO_AUTH) && !defined(MONGOOSE_NO_FILESYSTEM)
+  // Edit passwords file if -A option is specified
+  if (argc > 1 && !strcmp(argv[1], "-A")) {
+    if (argc != 6) {
+      show_usage_and_exit();
+    }
+    exit(modify_passwords_file(argv[2], argv[3], argv[4], argv[5]) ?
+         EXIT_SUCCESS : EXIT_FAILURE);
+  }
+#endif
+
+  // Show usage if -h or --help options are specified
+  if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) {
+    show_usage_and_exit();
+  }
+  set_options(argv);
+}
+
+static void set_options(char *argv[]) {
+  char *options[MAX_OPTIONS];
+  int i;
+
+  options[0] = NULL;
+  set_option(options, "document_root", s_default_document_root);
+  set_option(options, "listening_port", s_default_listening_port);
+
+  // Update config based on command line arguments
+  process_command_line_arguments(argv, options);
+
+  // Make sure we have absolute paths for files and directories
+  // https://github.com/valenok/mongoose/issues/181
+  set_absolute_path(options, "document_root");
+  set_absolute_path(options, "dav_auth_file");
+  set_absolute_path(options, "cgi_interpreter");
+  set_absolute_path(options, "access_log_file");
+  set_absolute_path(options, "global_auth_file");
+  set_absolute_path(options, "ssl_certificate");
+
+  if (!path_exists(get_option(options, "document_root"), 1)) {
+    set_option(options, "document_root", s_default_document_root);
+    set_absolute_path(options, "document_root");
+    notify("Setting document_root to [%s]",
+           mg_get_option(server, "document_root"));
+  }
+
+  // Make extra verification for certain options
+  verify_existence(options, "document_root", 1);
+  verify_existence(options, "cgi_interpreter", 0);
+  verify_existence(options, "ssl_certificate", 0);
+
+  for (i = 0; options[i] != NULL; i += 2) {
+    const char *msg = mg_set_option(server, options[i], options[i + 1]);
+    if (msg != NULL) {
+      notify("Failed to set option [%s] to [%s]: %s",
+             options[i], options[i + 1], msg);
+      if (!strcmp(options[i], "listening_port")) {
+        mg_set_option(server, "listening_port", s_default_listening_port);
+        notify("Setting %s to [%s]", options[i], s_default_listening_port);
+      }
+    }
+    free(options[i]);
+    free(options[i + 1]);
+  }
+
+  // Change current working directory to document root. This way,
+  // scripts can use relative paths.
+  chdir(mg_get_option(server, "document_root"));
+
+#if 0
+  // Add an ability to pass listening socket to mongoose
+  {
+    const char *env = getenv("MONGOOSE_LISTENING_SOCKET");
+    if (env != NULL && atoi(env) > 0 ) {
+      mg_set_listening_socket(server, atoi(env));
+    }
+  }
+#endif
+
+  // Setup signal handler: quit on Ctrl-C
+  signal(SIGTERM, signal_handler);
+  signal(SIGINT, signal_handler);
+#ifndef _WIN32
+  signal(SIGCHLD, signal_handler);
+#endif
+}
+
+int main(int argc, char *argv[]) {
+  init_server_name();
+  start_mongoose(argc, argv);
+  printf("%s serving [%s] on port %s\n",
+         server_name, mg_get_option(server, "document_root"),
+         mg_get_option(server, "listening_port"));
+  fflush(stdout);  // Needed, Windows terminals might not be line-buffered
+  serving_thread_func(server);
+  printf("Exiting on signal %d ...", exit_flag);
+  fflush(stdout);
+  mg_destroy_server(&server);
+  printf("%s\n", " done.");
+
+  return EXIT_SUCCESS;
+}
diff --git a/examples/unit_test.c b/examples/unit_test.c
index 64e350cc6..18dfcaa6e 100644
--- a/examples/unit_test.c
+++ b/examples/unit_test.c
@@ -396,9 +396,9 @@ static const char *test_server(void) {
   ASSERT(mg_set_option(server, "listening_port", LISTENING_ADDR) == NULL);
   ASSERT(mg_set_option(server, "document_root", ".") == NULL);
 
-  ASSERT((conn = mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0)) != NULL);
+  ASSERT((conn = mg_connect(server, "127.0.0.1:" HTTP_PORT)) != NULL);
   conn->connection_param = buf1;
-  ASSERT((conn = mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0)) != NULL);
+  ASSERT((conn = mg_connect(server, "127.0.0.1:" HTTP_PORT)) != NULL);
   conn->connection_param = buf2;
 
   { int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
@@ -483,7 +483,7 @@ static const char *test_mg_set_option(void) {
 }
 
 static const char *test_rewrites(void) {
-  char buf1[100] = "xx";
+  char buf1[100] = "xx", addr[50];
   struct mg_server *server = mg_create_server(NULL, evh2);
   struct mg_connection *conn;
   const char *port;
@@ -492,7 +492,8 @@ static const char *test_rewrites(void) {
   ASSERT(mg_set_option(server, "document_root", ".") == NULL);
   ASSERT(mg_set_option(server, "url_rewrites", "/xx=unit_test.c") == NULL);
   ASSERT((port = mg_get_option(server, "listening_port")) != NULL);
-  ASSERT((conn = mg_connect(server, "127.0.0.1", atoi(port), 0)) != NULL);
+  snprintf(addr, sizeof(addr), "127.0.0.1:%s", port);
+  ASSERT((conn = mg_connect(server, addr)) != NULL);
   conn->connection_param = buf1;
 
   { int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
diff --git a/examples/websocket_chat/websocket_chat.c b/examples/websocket_chat/websocket_chat.c
index bbc92bafb..a1de7234e 100644
--- a/examples/websocket_chat/websocket_chat.c
+++ b/examples/websocket_chat/websocket_chat.c
@@ -1,5 +1,5 @@
 // Copyright (c) 2013-2014 Cesanta Software Limited
-// $Date: 2014-09-08 22:30:52 UTC $
+// $Date: 2014-09-09 17:07:55 UTC $
 
 #include <string.h>
 #include <time.h>
diff --git a/examples/websocket_echo_server/websocket_echo_server.c b/examples/websocket_echo_server/websocket_echo_server.c
index 7e430fa86..ceb40917a 100644
--- a/examples/websocket_echo_server/websocket_echo_server.c
+++ b/examples/websocket_echo_server/websocket_echo_server.c
@@ -1,5 +1,5 @@
 // Copyright (c) 2013-2014 Cesanta Software Limited
-// $Date: 2014-09-09 08:27:35 UTC $
+// $Date: 2014-09-09 17:07:55 UTC $
 
 #include <string.h>
 #include <time.h>
diff --git a/examples/ws_ssl/Makefile b/examples/ws_ssl/Makefile
index 52f92802a..b4fc4faf9 100644
--- a/examples/ws_ssl/Makefile
+++ b/examples/ws_ssl/Makefile
@@ -1,19 +1,17 @@
 # Copyright (c) 2014 Cesanta Software
 # All rights reserved
 
-CFLAGS = -W -Wall -I../.. -g -O0 $(CFLAGS_EXTRA)
-NS = ../../../net_skeleton
-SW = ../../../ssl_wrapper
+CFLAGS = -W -Wall -I../.. -I. -g -O0 $(CFLAGS_EXTRA)
 
-SOURCES = ws_ssl.c ../../mongoose.c $(NS)/net_skeleton.c $(SW)/ssl_wrapper.c
+SOURCES = ws_ssl.c ../../mongoose.c net_skeleton.c ssl_wrapper.c
 PROG = ws_ssl
 
 all: $(PROG)
 
 $(PROG): $(SOURCES)
 	$(CC) -o $(PROG) $(SOURCES) \
-		-I$(NS) -DNS_ENABLE_SSL -DNOEMBED_NET_SKELETON \
-		-I$(SW) -DSSL_WRAPPER_USE_AS_LIBRARY -lssl $(CFLAGS)
+		-DNS_ENABLE_SSL -DNOEMBED_NET_SKELETON \
+		-DSSL_WRAPPER_USE_AS_LIBRARY -lssl $(CFLAGS)
 
 clean:
 	rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
diff --git a/examples/ws_ssl/net_skeleton.c b/examples/ws_ssl/net_skeleton.c
index 669f25893..55b2a546d 100644
--- a/examples/ws_ssl/net_skeleton.c
+++ b/examples/ws_ssl/net_skeleton.c
@@ -14,7 +14,7 @@
 // Alternatively, you can license this software under a commercial
 // license, as set out in <http://cesanta.com/>.
 //
-// $Date: 2014-09-09 16:03:50 UTC $
+// $Date: 2014-09-09 17:07:55 UTC $
 
 #include "net_skeleton.h"
 
diff --git a/examples/ws_ssl/net_skeleton.h b/examples/ws_ssl/net_skeleton.h
index 0985990a3..d662d17a2 100644
--- a/examples/ws_ssl/net_skeleton.h
+++ b/examples/ws_ssl/net_skeleton.h
@@ -14,7 +14,7 @@
 // Alternatively, you can license this software under a commercial
 // license, as set out in <http://cesanta.com/>.
 //
-// $Date: 2014-09-09 16:03:50 UTC $
+// $Date: 2014-09-09 17:07:55 UTC $
 
 #ifndef NS_SKELETON_HEADER_INCLUDED
 #define NS_SKELETON_HEADER_INCLUDED
diff --git a/examples/ws_ssl/ssl_wrapper.c b/examples/ws_ssl/ssl_wrapper.c
index 9d0b44e58..9c33300e8 100644
--- a/examples/ws_ssl/ssl_wrapper.c
+++ b/examples/ws_ssl/ssl_wrapper.c
@@ -14,7 +14,7 @@
 // Alternatively, you can license this software under a commercial
 // license, as set out in <http://cesanta.com/products.html>.
 //
-// $Date: 2014-09-09 16:03:50 UTC $
+// $Date: 2014-09-09 17:07:55 UTC $
 
 #include "net_skeleton.h"
 #include "ssl_wrapper.h"
diff --git a/examples/ws_ssl/ssl_wrapper.h b/examples/ws_ssl/ssl_wrapper.h
index b912103e2..d4c92e3ee 100644
--- a/examples/ws_ssl/ssl_wrapper.h
+++ b/examples/ws_ssl/ssl_wrapper.h
@@ -14,7 +14,7 @@
 // Alternatively, you can license this software under a commercial
 // license, as set out in <http://cesanta.com/products.html>.
 //
-// $Date: 2014-09-09 16:03:50 UTC $
+// $Date: 2014-09-09 17:07:55 UTC $
 
 #ifndef SSL_WRAPPER_HEADER_INCLUDED
 #define SSL_WRAPPER_HEADER_INCLUDED
diff --git a/mongoose.c b/mongoose.c
index 3b2259fd1..1a0e7ccbb 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -15,7 +15,7 @@
 // Alternatively, you can license this library under a commercial
 // license, as set out in <http://cesanta.com/>.
 //
-// $Date: 2014-09-01 19:53:26 UTC $
+// $Date: 2014-09-09 17:07:55 UTC $
 
 #ifdef NOEMBED_NET_SKELETON
 #include "net_skeleton.h"
@@ -36,11 +36,13 @@
 //
 // Alternatively, you can license this software under a commercial
 // license, as set out in <http://cesanta.com/>.
+//
+// $Date: 2014-09-09 17:07:55 UTC $
 
 #ifndef NS_SKELETON_HEADER_INCLUDED
 #define NS_SKELETON_HEADER_INCLUDED
 
-#define NS_SKELETON_VERSION "1.1"
+#define NS_SKELETON_VERSION "2.0.0"
 
 #undef UNICODE                  // Use ANSI WinAPI functions
 #undef _UNICODE                 // Use multibyte encoding on Windows
@@ -133,7 +135,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__
@@ -174,7 +178,7 @@ void iobuf_remove(struct iobuf *, size_t data_size);
 // Net skeleton interface
 // Events. Meaning of event parameter (evp) is given in the comment.
 enum ns_event {
-  NS_POLL,     // Sent to each connection on each call to ns_server_poll()
+  NS_POLL,     // Sent to each connection on each call to ns_mgr_poll()
   NS_ACCEPT,   // New connection accept()-ed. union socket_address *remote_addr
   NS_CONNECT,  // connect() succeeded or failed. int *success_status
   NS_RECV,     // Data has benn received. int *num_bytes
@@ -187,36 +191,37 @@ enum ns_event {
 struct ns_connection;
 typedef void (*ns_callback_t)(struct ns_connection *, enum ns_event, void *evp);
 
-struct ns_server {
-  void *server_data;
-  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;
-  const char *hexdump_file;
-  sock_t ctl[2];
+  ns_callback_t callback;           // Event handler function
+  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;
+  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;
   union socket_address sa;
   struct iobuf recv_iobuf;
   struct iobuf send_iobuf;
   SSL *ssl;
+  SSL_CTX *ssl_ctx;
   void *connection_data;
   time_t last_io_time;
+
   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_WANT_READ               (1 << 6)
-#define NSF_WANT_WRITE              (1 << 7)
+#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 << 26)
 #define NSF_USER_2                  (1 << 27)
@@ -226,20 +231,15 @@ struct ns_connection {
 #define NSF_USER_6                  (1 << 31)
 };
 
-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_server_wakeup_ex(struct ns_server *, ns_callback_t, void *, size_t);
-void ns_iterate(struct ns_server *, ns_callback_t cb, void *param);
-struct ns_connection *ns_next(struct ns_server *, struct ns_connection *);
-struct ns_connection *ns_add_sock(struct ns_server *, sock_t sock, void *p);
-
-int ns_bind(struct ns_server *, const char *addr);
-int ns_set_ssl_cert(struct ns_server *, const char *ssl_cert);
-int ns_set_ssl_ca_cert(struct ns_server *, const char *ssl_ca_cert);
-struct ns_connection *ns_connect(struct ns_server *, const char *host,
-                                 int port, int ssl, void *connection_param);
+void ns_mgr_init(struct ns_mgr *, void *data, ns_callback_t);
+void ns_mgr_free(struct ns_mgr *);
+int ns_mgr_poll(struct ns_mgr *, int milli);
+void ns_broadcast(struct ns_mgr *, ns_callback_t, void *, size_t);
+
+struct ns_connection *ns_next(struct ns_mgr *, struct ns_connection *);
+struct ns_connection *ns_add_sock(struct ns_mgr *, sock_t sock, void *p);
+struct ns_connection *ns_bind(struct ns_mgr *, const char *addr, void *p);
+struct ns_connection *ns_connect(struct ns_mgr *, const char *addr, void *p);
 
 int ns_send(struct ns_connection *, const void *buf, int len);
 int ns_printf(struct ns_connection *, const char *fmt, ...);
@@ -253,6 +253,7 @@ 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
 }
@@ -274,6 +275,8 @@ int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap);
 //
 // Alternatively, you can license this software under a commercial
 // license, as set out in <http://cesanta.com/>.
+//
+// $Date: 2014-09-09 17:07:55 UTC $
 
 
 #ifndef NS_MALLOC
@@ -288,6 +291,9 @@ int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap);
 #define NS_FREE free
 #endif
 
+#define NS_UDP_RECEIVE_BUFFER_SIZE  2000
+#define NS_VPRINTF_BUFFER_SIZE      500
+
 struct ctl_msg {
   ns_callback_t callback;
   char message[1024 * 8];
@@ -338,6 +344,16 @@ void iobuf_remove(struct iobuf *io, size_t n) {
   }
 }
 
+static size_t ns_out(struct ns_connection *nc, const void *buf, size_t len) {
+  if (nc->flags & NSF_UDP) {
+    long n = send(nc->sock, buf, len, 0);
+    DBG(("%p %d send %ld (%d)", nc, nc->sock, n, 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
@@ -361,15 +377,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;
 }
@@ -412,12 +428,12 @@ 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);
@@ -450,7 +466,7 @@ static void hexdump(struct ns_connection *nc, const char *path,
             ev == NS_RECV ? "<-" : ev == NS_SEND ? "->" :
             ev == NS_ACCEPT ? "<A" : ev == NS_CONNECT ? "C>" : "XX",
             dst, num_bytes);
-    if (num_bytes > 0 && (buf = (char *) malloc(buf_size)) != NULL) {
+    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);
@@ -461,17 +477,14 @@ static void hexdump(struct ns_connection *nc, const char *path,
 }
 
 static void ns_call(struct ns_connection *conn, enum ns_event ev, void *p) {
-  if (conn->server->hexdump_file != NULL && ev != NS_POLL) {
+  if (conn->mgr->hexdump_file != NULL && ev != NS_POLL) {
     int len = (ev == NS_RECV || ev == NS_SEND) ? * (int *) p : 0;
-    hexdump(conn, conn->server->hexdump_file, len, ev);
+    hexdump(conn, conn->mgr->hexdump_file, len, ev);
   }
-  if (conn->server->callback) conn->server->callback(conn, ev, p);
+  if (conn->mgr->callback) conn->mgr->callback(conn, ev, p);
 }
 
-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_destroy_conn(struct ns_connection *conn) {
   closesocket(conn->sock);
   iobuf_free(&conn->recv_iobuf);
   iobuf_free(&conn->send_iobuf);
@@ -479,10 +492,20 @@ static void ns_close_conn(struct ns_connection *conn) {
   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);
@@ -543,10 +566,31 @@ int ns_socketpair(sock_t sp[2]) {
 }
 #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
@@ -557,36 +601,57 @@ 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);
+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,
@@ -596,12 +661,11 @@ static sock_t ns_open_listening_socket(union socket_address *sa) {
       // scenarios. Therefore, SO_REUSEADDR was disabled on Windows.
       !setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)) &&
 #endif
-      !bind(sock, &sa->sa, sa->sa.sa_family == AF_INET ?
-            sizeof(sa->sin) : sizeof(sa->sin6)) &&
-      !listen(sock, SOMAXCONN)) {
+      !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;
@@ -610,80 +674,97 @@ static sock_t ns_open_listening_socket(union socket_address *sa) {
   return sock;
 }
 
-// Certificate generation script is at
-// https://github.com/cesanta/net_skeleton/blob/master/examples/gen_certs.sh
-int ns_set_ssl_ca_cert(struct ns_server *server, const char *cert) {
 #ifdef NS_ENABLE_SSL
-  STACK_OF(X509_NAME) *list = SSL_load_client_CA_file(cert);
-  if (cert != NULL && server->ssl_ctx != NULL && list != NULL) {
-    SSL_CTX_set_client_CA_list(server->ssl_ctx, list);
-    SSL_CTX_set_verify(server->ssl_ctx, SSL_VERIFY_PEER |
-                       SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 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;
   }
-#endif
-  return server != NULL && cert == NULL ? 0 : -1;
+  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;
 }
 
-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) {
+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_set_mode(server->ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
-    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;
   }
-#endif
-  return server != NULL && cert == NULL ? 0 : -3;
 }
+#endif  // NS_ENABLE_SSL
 
-int ns_bind(struct ns_server *server, const char *str) {
+struct ns_connection *ns_bind(struct ns_mgr *srv, const char *str, void *data) {
   union socket_address sa;
-  ns_parse_port_string(str, &sa);
-  if (server->listening_sock != INVALID_SOCKET) {
-    closesocket(server->listening_sock);
+  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, NULL)) == NULL) {
+    closesocket(sock);
+  } else {
+    nc->sa = sa;
+    nc->flags |= NSF_LISTENING;
+    nc->connection_data = data;
+
+    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(&sa);
-  return server->listening_sock == INVALID_SOCKET ? -1 :
-  (int) ntohs(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) {
-  } 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, NULL)) == 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;
     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;
@@ -720,7 +801,7 @@ void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags) {
       // 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, len);
+      inet_ntop(sa.sa.sa_family, (void *) &sa.sin.sin_addr, buf,(socklen_t)len);
 #endif
     }
     if (flags & 2) {
@@ -822,7 +903,7 @@ static void ns_read_from_socket(struct ns_connection *conn) {
   } else
 #endif
   {
-    while ((n = recv(conn->sock, buf, sizeof(buf), 0)) > 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);
@@ -851,7 +932,7 @@ static void ns_write_to_socket(struct ns_connection *conn) {
     }
   } else
 #endif
-  { n = send(conn->sock, io->buf, io->len, 0); }
+  { n = (int) send(conn->sock, io->buf, io->len, 0); }
 
   DBG(("%p %d -> %d bytes", conn, conn->flags, n));
 
@@ -864,7 +945,27 @@ static void ns_write_to_socket(struct ns_connection *conn) {
 }
 
 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.recv_iobuf.buf = buf;
+    nc.recv_iobuf.len = nc.recv_iobuf.size = n;
+    nc.sock = ls->sock;
+    nc.mgr = ls->mgr;
+    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) {
@@ -876,7 +977,7 @@ 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) {
+int ns_mgr_poll(struct ns_mgr *mgr, int milli) {
   struct ns_connection *conn, *tmp_conn;
   struct timeval tv;
   fd_set read_set, write_set;
@@ -884,15 +985,11 @@ int ns_server_poll(struct ns_server *server, int milli) {
   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);
     if (!(conn->flags & NSF_WANT_WRITE)) {
@@ -918,34 +1015,38 @@ int ns_server_poll(struct ns_server *server, int milli) {
     // now to prevent last_io_time being set to the past.
     current_time = time(NULL);
 
-    // 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;
-      }
-    }
-
     // Read wakeup messages
-    if (server->ctl[1] != INVALID_SOCKET &&
-        FD_ISSET(server->ctl[1], &read_set)) {
+    if (mgr->ctl[1] != INVALID_SOCKET &&
+        FD_ISSET(mgr->ctl[1], &read_set)) {
       struct ctl_msg ctl_msg;
-      int len = recv(server->ctl[1], (char *) &ctl_msg, sizeof(ctl_msg), 0);
-      send(server->ctl[1], ctl_msg.message, 1, 0);
+      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) {
-        ns_iterate(server, ctl_msg.callback, ctl_msg.message);
+        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);
+        }
       }
     }
 
-    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);
@@ -957,7 +1058,7 @@ 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) ||
@@ -971,65 +1072,62 @@ int ns_server_poll(struct ns_server *server, int milli) {
   return num_active_connections;
 }
 
-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, void *param) {
   sock_t sock = INVALID_SOCKET;
-  struct sockaddr_in sin;
-  struct hostent *he = NULL;
-  struct ns_connection *conn = NULL;
-  int connect_ret_val;
-
-  (void) use_ssl;
+  struct ns_connection *nc = NULL;
+  union socket_address sa;
+  char cert[100], ca_cert[100];
+  int connect_ret_val, use_ssl, proto;
 
-  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);
+  connect_ret_val = 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 (connect_ret_val != 0 && ns_is_error(connect_ret_val)) {
     closesocket(sock);
     return NULL;
-  } else if ((conn = (struct ns_connection *)
-              NS_MALLOC(sizeof(*conn))) == NULL) {
+  } else if ((nc = ns_add_sock(mgr, sock, param)) == 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;   // Essential, cause UDP conns will use sendto()
+  if (proto == SOCK_DGRAM) {
+    nc->flags = NSF_UDP;
+  } else {
+    nc->flags = 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, void *p) {
   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->mgr = s;
     conn->last_io_time = time(NULL);
     ns_add_conn(s, conn);
     DBG(("%p %d", conn, sock));
@@ -1037,40 +1135,26 @@ struct ns_connection *ns_add_sock(struct ns_server *s, sock_t sock, void *p) {
   return conn;
 }
 
-struct ns_connection *ns_next(struct ns_server *s, struct ns_connection *conn) {
+struct ns_connection *ns_next(struct ns_mgr *s, struct ns_connection *conn) {
   return conn == NULL ? s->active_connections : conn->next;
 }
 
-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);
-  }
-}
-
-void ns_server_wakeup_ex(struct ns_server *server, ns_callback_t cb,
-                         void *data, size_t len) {
+void ns_broadcast(struct ns_mgr *mgr, ns_callback_t cb,void *data, size_t len) {
   struct ctl_msg ctl_msg;
-  if (server->ctl[0] != INVALID_SOCKET && data != NULL &&
+  if (mgr->ctl[0] != INVALID_SOCKET && data != NULL &&
       len < sizeof(ctl_msg.message)) {
     ctl_msg.callback = cb;
     memcpy(ctl_msg.message, data, len);
-    send(server->ctl[0], (char *) &ctl_msg,
+    send(mgr->ctl[0], (char *) &ctl_msg,
          offsetof(struct ctl_msg, message) + len, 0);
-    recv(server->ctl[0], (char *) &len, 1, 0);
+    recv(mgr->ctl[0], (char *) &len, 1, 0);
   }
 }
 
-void ns_server_wakeup(struct ns_server *server) {
-  ns_server_wakeup_ex(server, NULL, (void *) "", 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, ns_callback_t cb) {
   memset(s, 0, sizeof(*s));
-  s->listening_sock = s->ctl[0] = s->ctl[1] = INVALID_SOCKET;
-  s->server_data = server_data;
+  s->ctl[0] = s->ctl[1] = INVALID_SOCKET;
+  s->user_data = user_data;
   s->callback = cb;
 
 #ifdef _WIN32
@@ -1089,33 +1173,25 @@ void ns_server_init(struct ns_server *s, void *server_data, ns_callback_t cb) {
 
 #ifdef NS_ENABLE_SSL
   {static int init_done; if (!init_done) { SSL_library_init(); init_done++; }}
-  s->client_ssl_ctx = SSL_CTX_new(SSLv23_client_method());
 #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);
-  s->ssl_ctx = s->client_ssl_ctx = NULL;
-#endif
 }
 // net_skeleton end
 #endif  // NOEMBED_NET_SKELETON
@@ -1264,11 +1340,6 @@ enum {
 #endif
 #ifndef MONGOOSE_NO_SSI
   SSI_PATTERN,
-#endif
-#ifdef NS_ENABLE_SSL
-  SSL_CERTIFICATE,
-  SSL_CA_CERTIFICATE,
-  SSL_MITM_CERTS,
 #endif
   URL_REWRITES,
   NUM_OPTIONS
@@ -1299,7 +1370,7 @@ static const char *static_config_options[] = {
 #ifndef MONGOOSE_NO_FILESYSTEM
   "hide_files_patterns", NULL,
   "hexdump_file", NULL,
-  "index_files","index.html,index.htm,index.shtml,index.cgi,index.php,index.lp",
+  "index_files","index.html,index.htm,index.shtml,index.cgi,index.php",
 #endif
   "listening_port", NULL,
 #ifndef _WIN32
@@ -1307,18 +1378,13 @@ static const char *static_config_options[] = {
 #endif
 #ifndef MONGOOSE_NO_SSI
   "ssi_pattern", "**.shtml$|**.shtm$",
-#endif
-#ifdef NS_ENABLE_SSL
-  "ssl_certificate", NULL,
-  "ssl_ca_certificate", NULL,
-  "ssl_mitm_certs", 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];
@@ -1420,6 +1486,32 @@ void *mg_start_thread(void *(*f)(void *), void *p) {
 }
 #endif  // MONGOOSE_NO_THREADS
 
+#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 *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);
+}
+
 #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.
@@ -2093,7 +2185,7 @@ static void open_cgi_endpoint(struct connection *conn, const char *prog) {
   if (start_process(conn->server->config_options[CGI_INTERPRETER],
                     prog, blk.buf, blk.vars, dir, fds[1]) != 0) {
     conn->endpoint_type = EP_CGI;
-    conn->endpoint.nc = ns_add_sock(&conn->server->ns_server, fds[0], conn);
+    conn->endpoint.nc = ns_add_sock(&conn->server->ns_mgr, fds[0], 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;
@@ -4160,7 +4252,6 @@ int mg_terminate_ssl(struct mg_connection *c, const char *cert) {
   SSL_CTX *ctx;
 
   DBG(("%p MITM", conn));
-  SSL_library_init();
   if ((ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) return 0;
 
   SSL_CTX_use_certificate_file(ctx, cert, 1);
@@ -4170,7 +4261,7 @@ int mg_terminate_ssl(struct mg_connection *c, const char *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));
+  //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
@@ -4182,12 +4273,12 @@ int mg_terminate_ssl(struct mg_connection *c, const char *cert) {
 }
 #endif
 
-int mg_forward(struct mg_connection *c, const char *host, int port, int ssl) {
+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_server *server = &conn->server->ns_server;
   struct ns_connection *pc;
 
-  if ((pc = ns_connect(server, host, port, ssl, conn)) == NULL) {
+  if ((pc = ns_connect(&conn->server->ns_mgr, addr, conn)) == NULL) {
     conn->ns_conn->flags |= NSF_CLOSE_IMMEDIATELY;
     return 0;
   }
@@ -4196,12 +4287,12 @@ int mg_forward(struct mg_connection *c, const char *host, int port, int ssl) {
   pc->flags |= MG_PROXY_CONN;
   conn->endpoint_type = EP_PROXY;
   conn->endpoint.nc = pc;
-  DBG(("%p [%s] [%s:%d] -> %p %p",
-      conn, c->uri, host, port, pc, conn->ns_conn->ssl));
+  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.
-    mg_printf(c, "%s", "HTTP/1.1 200 OK\r\n\r\n");
+    // 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;
@@ -4212,7 +4303,7 @@ int mg_forward(struct mg_connection *c, const char *host, int port, int ssl) {
 }
 
 static void proxify_connection(struct connection *conn) {
-  char proto[10], host[500], cert[500];
+  char proto[10], host[500], cert[500], addr[1000];
   unsigned short port = 80;
   struct mg_connection *c = &conn->mg_conn;
   int n = 0;
@@ -4225,25 +4316,9 @@ static void proxify_connection(struct connection *conn) {
     n = 0;
   }
 
-#ifdef NS_ENABLE_SSL
-  // Find out whether we should be in the MITM mode
-  {
-    const char *certs = conn->server->config_options[SSL_MITM_CERTS];
-    int host_len = strlen(host);
-    struct vec a, b;
-
-    while (conn->ns_conn->ssl == NULL && port != 80 &&
-           (certs = next_option(certs, &a, &b)) != NULL) {
-      if (a.len != host_len || mg_strncasecmp(a.ptr, host, a.len)) continue;
-      snprintf(cert, sizeof(cert), "%.*s", b.len, b.ptr);
-      mg_terminate_ssl(&conn->mg_conn, cert);
-      return;
-    }
-  }
-#endif
-
-  if (n > 0 && mg_forward(c, host, port, conn->ns_conn->ssl != NULL)) {
-  } else {
+  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;
   }
 }
@@ -4535,12 +4610,11 @@ static void process_response(struct connection *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, NULL);
   if (nsconn == NULL) return 0;
 
   if ((conn = (struct connection *) calloc(1, sizeof(*conn))) == NULL) {
@@ -4555,7 +4629,7 @@ struct mg_connection *mg_connect(struct mg_server *server, const char *host,
   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;
@@ -4684,7 +4758,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) {
@@ -4692,7 +4766,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)
     }
@@ -4701,34 +4775,15 @@ void mg_destroy_server(struct mg_server **server) {
   }
 }
 
-struct mg_iterator {
-  mg_handler_t cb;
-  void *param;
-};
-
-static void iter(struct ns_connection *nsconn, enum ns_event ev, void *param) {
-  if (ev == NS_POLL) {
-    struct mg_iterator *it = (struct mg_iterator *) param;
-    struct connection *c = (struct connection *) nsconn->connection_data;
-    if (c != NULL) c->mg_conn.callback_param = it->param;
-    it->cb(&c->mg_conn, MG_POLL);
-  }
-}
-
 struct mg_connection *mg_next(struct mg_server *s, struct mg_connection *c) {
   struct connection *conn = MG_CONN_2_CONN(c);
-  struct ns_connection *nc = ns_next(&s->ns_server,
+  struct ns_connection *nc = ns_next(&s->ns_mgr,
                                      c == NULL ? NULL : conn->ns_conn);
-
-  return nc == NULL ? NULL :
-    & ((struct connection *) nc->connection_data)->mg_conn;
-}
-
-// Apply function to all active connections.
-void mg_iterate_over_connections(struct mg_server *server, mg_handler_t cb,
-  void *param) {
-  struct mg_iterator it = { cb, param };
-  ns_iterate(&server->ns_server, iter, &it);
+  if (nc != NULL && nc->connection_data != NULL) {
+    return & ((struct connection *) nc->connection_data)->mg_conn;
+  } else {
+    return NULL;
+  }
 }
 
 static int get_var(const char *data, size_t data_len, const char *name,
@@ -4886,18 +4941,18 @@ const char *mg_set_option(struct mg_server *server, const char *name,
   DBG(("%s [%s]", name, *v));
 
   if (ind == LISTENING_PORT) {
-    int port = ns_bind(&server->ns_server, value);
-    if (port < 0) {
+    struct ns_connection *c = ns_bind(&server->ns_mgr, value, NULL);
+    if (c == NULL) {
       error_msg = "Cannot bind to port";
     } else {
       char buf[100];
-      ns_sock_to_str(server->ns_server.listening_sock, buf, sizeof(buf), 2);
+      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_server.hexdump_file = *v;
+    server->ns_mgr.hexdump_file = *v;
 #endif
 #ifndef _WIN32
   } else if (ind == RUN_AS_USER) {
@@ -4909,21 +4964,6 @@ const char *mg_set_option(struct mg_server *server, const char *name,
     } 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";
-    }
-  } else if (ind == SSL_CA_CERTIFICATE) {
-    if (ns_set_ssl_ca_cert(&server->ns_server, value) != 0) {
-       error_msg = "Error setting CA cert";
-     }
 #endif
   }
 
@@ -4943,7 +4983,7 @@ static void set_ips(struct ns_connection *nc, int is_rem) {
 }
 
 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],
@@ -4957,7 +4997,7 @@ static void on_accept(struct ns_connection *nc, union socket_address *sa) {
 
     // Initialize the rest of connection attributes
     conn->server = server;
-    conn->mg_conn.server_param = nc->server->server_data;
+    conn->mg_conn.server_param = nc->mgr->user_data;
     set_ips(nc, 1);
     set_ips(nc, 0);
   }
@@ -5007,7 +5047,7 @@ static void mg_ev_handler(struct ns_connection *nc, enum ns_event ev, void *p) {
       if (conn != NULL) {
         conn->num_bytes_recv += * (int *) p;
       }
-      if (nc->flags & NSF_ACCEPTED) {
+      if (nc->listener != NULL) {
         on_recv_data(conn);
 #ifndef MONGOOSE_NO_CGI
       } else if (nc->flags & MG_CGI_CONN) {
@@ -5110,23 +5150,25 @@ void mg_wakeup_server_ex(struct mg_server *server, mg_handler_t cb,
   va_end(ap);
 
   // "len + 1" is to include terminating \0 in the message
-  ns_server_wakeup_ex(&server->ns_server, iter2, buf, len + 1);
+  ns_broadcast(&server->ns_mgr, iter2, buf, len + 1);
 }
 
 void mg_wakeup_server(struct mg_server *server) {
-  ns_server_wakeup_ex(&server->ns_server, NULL, (void *) "", 0);
+  ns_broadcast(&server->ns_mgr, NULL, (void *) "", 0);
 }
 
+#if 0
 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);
+  if (server->ns_mgr.listening_sock != INVALID_SOCKET) {
+    closesocket(server->ns_mgr.listening_sock);
   }
-  server->ns_server.listening_sock = (sock_t) sock;
+  server->ns_mgr.listening_sock = (sock_t) sock;
 }
 
 int mg_get_listening_socket(struct mg_server *server) {
-  return server->ns_server.listening_sock;
+  return server->ns_mgr.listening_sock;
 }
+#endif
 
 const char *mg_get_option(const struct mg_server *server, const char *name) {
   const char **opts = (const char **) server->config_options;
@@ -5136,33 +5178,8 @@ 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, mg_ev_handler);
   set_default_option_values(server->config_options);
   server->event_handler = handler;
   return server;
 }
-
-#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 *mg_mmap(FILE *fp, size_t size) {
-  return mmap(NULL, size, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
-}
-
-void mg_munmap(void *p, size_t size) {
-  munmap(p, size);
-}
diff --git a/mongoose.h b/mongoose.h
index d39808ee1..572b863e3 100644
--- a/mongoose.h
+++ b/mongoose.h
@@ -15,7 +15,7 @@
 // Alternatively, you can license this library under a commercial
 // license, as set out in <http://cesanta.com/>.
 //
-// $Date: 2014-09-01 19:53:26 UTC $
+// $Date: 2014-09-09 17:07:55 UTC $
 
 #ifndef MONGOOSE_HEADER_INCLUDED
 #define  MONGOOSE_HEADER_INCLUDED
@@ -55,7 +55,7 @@ struct mg_connection {
   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;       // Needed by mg_iterate_over_connections()
+  void *callback_param;
 };
 
 struct mg_server; // Opaque structure describing server instance
@@ -95,11 +95,10 @@ const char **mg_get_valid_option_names(void);
 const char *mg_get_option(const struct mg_server *server, const char *name);
 void mg_set_listening_socket(struct mg_server *, int sock);
 int mg_get_listening_socket(struct mg_server *);
-void mg_iterate_over_connections(struct mg_server *, mg_handler_t, void *);
 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 *, int, int);
+struct mg_connection *mg_connect(struct mg_server *, const char *);
 
 // Connection management functions
 void mg_send_status(struct mg_connection *, int status_code);
@@ -127,6 +126,7 @@ int mg_parse_multipart(const char *buf, int buf_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], ...);
@@ -134,7 +134,7 @@ 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 *, const char *host, int port, int use_ssl);
+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);
 
@@ -147,7 +147,6 @@ struct mg_expansion {
 void mg_template(struct mg_connection *, const char *text,
                  struct mg_expansion *expansions);
 
-
 #ifdef __cplusplus
 }
 #endif // __cplusplus
-- 
GitLab