From 47606a71eeedf7e7836d643e5a95875b2336d888 Mon Sep 17 00:00:00 2001
From: Sergey Lyubka <valenok@gmail.com>
Date: Wed, 2 Oct 2013 23:21:13 +0100
Subject: [PATCH] Moved unix and win32 code to different files

---
 build/Makefile       |   3 +-
 build/src/internal.h |   7 +-
 build/src/mongoose.c | 510 -------------------------------------------
 build/src/unix.c     | 105 +++++++++
 build/src/win32.c    | 401 ++++++++++++++++++++++++++++++++++
 mongoose.c           | 307 +++++++++++++-------------
 6 files changed, 667 insertions(+), 666 deletions(-)
 create mode 100644 build/src/unix.c
 create mode 100644 build/src/win32.c

diff --git a/build/Makefile b/build/Makefile
index c05b1d5f5..eafe3a554 100644
--- a/build/Makefile
+++ b/build/Makefile
@@ -27,7 +27,8 @@ VERSION = $(shell perl -lne \
 
 # The order in which files are listed is important
 SOURCES = src/internal.h src/string.c src/parse_date.c src/options.c \
-          src/crypto.c src/auth.c src/mongoose.c src/lua.c
+          src/crypto.c src/auth.c src/win32.c src/unix.c src/mongoose.c \
+          src/lua.c
 
 TINY_SOURCES = ../mongoose.c main.c
 LUA_SOURCES = $(TINY_SOURCES) sqlite3.c lsqlite3.c lua_5.2.1.c
diff --git a/build/src/internal.h b/build/src/internal.h
index 2e1f3e4eb..b84def028 100644
--- a/build/src/internal.h
+++ b/build/src/internal.h
@@ -323,7 +323,7 @@ struct ssl_func {
   void  (*ptr)(void); // Function pointer
 };
 
-static struct ssl_func ssl_sw[];
+static struct ssl_func ssl_sw[30];
 
 #define SSL_free (* (void (*)(SSL *)) ssl_sw[0].ptr)
 #define SSL_accept (* (int (*)(SSL *)) ssl_sw[1].ptr)
@@ -460,6 +460,11 @@ struct de {
 
 static FILE *mg_fopen(const char *path, const char *mode);
 static int mg_stat(const char *path, struct file *filep);
+static void send_http_error(struct mg_connection *, int, const char *,
+                            PRINTF_FORMAT_STRING(const char *fmt), ...)
+                            PRINTF_ARGS(4, 5);
+static void cry(struct mg_connection *conn,
+                PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
 
 #ifdef USE_LUA
 #include "lua_5.2.1.h"
diff --git a/build/src/mongoose.c b/build/src/mongoose.c
index e07bb14ca..5f86022e1 100644
--- a/build/src/mongoose.c
+++ b/build/src/mongoose.c
@@ -43,9 +43,6 @@ static void sockaddr_to_string(char *buf, size_t len,
 #endif
 }
 
-static void cry(struct mg_connection *conn,
-                PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
-
 // Print error message to the opened error log stream.
 static void cry(struct mg_connection *conn, const char *fmt, ...) {
   char buf[MG_BUF_LEN], src_addr[IP_ADDR_STR_LEN];
@@ -119,11 +116,6 @@ static const char *suggest_connection_header(const struct mg_connection *conn) {
   return should_keep_alive(conn) ? "keep-alive" : "close";
 }
 
-static void send_http_error(struct mg_connection *, int, const char *,
-                            PRINTF_FORMAT_STRING(const char *fmt), ...)
-  PRINTF_ARGS(4, 5);
-
-
 static void send_http_error(struct mg_connection *conn, int status,
                             const char *reason, const char *fmt, ...) {
   char buf[MG_BUF_LEN];
@@ -151,508 +143,6 @@ static void send_http_error(struct mg_connection *conn, int status,
   conn->num_bytes_sent += mg_printf(conn, "%s", buf);
 }
 
-#if defined(_WIN32) && !defined(__SYMBIAN32__)
-static pthread_t pthread_self(void) {
-  return GetCurrentThreadId();
-}
-
-static int pthread_mutex_init(pthread_mutex_t *mutex, void *unused) {
-  (void) unused;
-  *mutex = CreateMutex(NULL, FALSE, NULL);
-  return *mutex == NULL ? -1 : 0;
-}
-
-static int pthread_mutex_destroy(pthread_mutex_t *mutex) {
-  return CloseHandle(*mutex) == 0 ? -1 : 0;
-}
-
-static int pthread_mutex_lock(pthread_mutex_t *mutex) {
-  return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0? 0 : -1;
-}
-
-static int pthread_mutex_unlock(pthread_mutex_t *mutex) {
-  return ReleaseMutex(*mutex) == 0 ? -1 : 0;
-}
-
-static int pthread_cond_init(pthread_cond_t *cv, const void *unused) {
-  (void) unused;
-  cv->signal = CreateEvent(NULL, FALSE, FALSE, NULL);
-  cv->broadcast = CreateEvent(NULL, TRUE, FALSE, NULL);
-  return cv->signal != NULL && cv->broadcast != NULL ? 0 : -1;
-}
-
-static int pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex) {
-  HANDLE handles[] = {cv->signal, cv->broadcast};
-  ReleaseMutex(*mutex);
-  WaitForMultipleObjects(2, handles, FALSE, INFINITE);
-  return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0? 0 : -1;
-}
-
-static int pthread_cond_signal(pthread_cond_t *cv) {
-  return SetEvent(cv->signal) == 0 ? -1 : 0;
-}
-
-static int pthread_cond_broadcast(pthread_cond_t *cv) {
-  // Implementation with PulseEvent() has race condition, see
-  // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
-  return PulseEvent(cv->broadcast) == 0 ? -1 : 0;
-}
-
-static int pthread_cond_destroy(pthread_cond_t *cv) {
-  return CloseHandle(cv->signal) && CloseHandle(cv->broadcast) ? 0 : -1;
-}
-
-// For Windows, change all slashes to backslashes in path names.
-static void change_slashes_to_backslashes(char *path) {
-  int i;
-
-  for (i = 0; path[i] != '\0'; i++) {
-    if (path[i] == '/')
-      path[i] = '\\';
-    // i > 0 check is to preserve UNC paths, like \\server\file.txt
-    if (path[i] == '\\' && i > 0)
-      while (path[i + 1] == '\\' || path[i + 1] == '/')
-        (void) memmove(path + i + 1,
-            path + i + 2, strlen(path + i + 1));
-  }
-}
-
-// 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_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len) {
-  char buf[PATH_MAX * 2], buf2[PATH_MAX * 2];
-
-  mg_strlcpy(buf, path, sizeof(buf));
-  change_slashes_to_backslashes(buf);
-
-  // Convert to Unicode and back. If doubly-converted string does not
-  // match the original, something is fishy, reject.
-  memset(wbuf, 0, wbuf_len * sizeof(wchar_t));
-  MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len);
-  WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2),
-                      NULL, NULL);
-  if (strcmp(buf, buf2) != 0) {
-    wbuf[0] = L'\0';
-  }
-}
-
-#if defined(_WIN32_WCE)
-static time_t time(time_t *ptime) {
-  time_t t;
-  SYSTEMTIME st;
-  FILETIME ft;
-
-  GetSystemTime(&st);
-  SystemTimeToFileTime(&st, &ft);
-  t = SYS2UNIX_TIME(ft.dwLowDateTime, ft.dwHighDateTime);
-
-  if (ptime != NULL) {
-    *ptime = t;
-  }
-
-  return t;
-}
-
-static struct tm *localtime(const time_t *ptime, struct tm *ptm) {
-  int64_t t = ((int64_t) *ptime) * RATE_DIFF + EPOCH_DIFF;
-  FILETIME ft, lft;
-  SYSTEMTIME st;
-  TIME_ZONE_INFORMATION tzinfo;
-
-  if (ptm == NULL) {
-    return NULL;
-  }
-
-  * (int64_t *) &ft = t;
-  FileTimeToLocalFileTime(&ft, &lft);
-  FileTimeToSystemTime(&lft, &st);
-  ptm->tm_year = st.wYear - 1900;
-  ptm->tm_mon = st.wMonth - 1;
-  ptm->tm_wday = st.wDayOfWeek;
-  ptm->tm_mday = st.wDay;
-  ptm->tm_hour = st.wHour;
-  ptm->tm_min = st.wMinute;
-  ptm->tm_sec = st.wSecond;
-  ptm->tm_yday = 0; // hope nobody uses this
-  ptm->tm_isdst =
-    GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_DAYLIGHT ? 1 : 0;
-
-  return ptm;
-}
-
-static struct tm *gmtime(const time_t *ptime, struct tm *ptm) {
-  // FIXME(lsm): fix this.
-  return localtime(ptime, ptm);
-}
-
-static size_t strftime(char *dst, size_t dst_size, const char *fmt,
-                       const struct tm *tm) {
-  (void) snprintf(dst, dst_size, "implement strftime() for WinCE");
-  return 0;
-}
-#endif
-
-// Windows happily opens files with some garbage at the end of file name.
-// For example, fopen("a.cgi    ", "r") on Windows successfully opens
-// "a.cgi", despite one would expect an error back.
-// This function returns non-0 if path ends with some garbage.
-static int path_cannot_disclose_cgi(const char *path) {
-  static const char *allowed_last_characters = "_-";
-  int last = path[strlen(path) - 1];
-  return isalnum(last) || strchr(allowed_last_characters, last) != NULL;
-}
-
-static int mg_stat(const char *path, struct file *filep) {
-  wchar_t wbuf[PATH_MAX] = L"\\\\?\\";
-  WIN32_FILE_ATTRIBUTE_DATA info;
-
-  filep->modification_time = 0;
-  to_unicode(path, wbuf + 4, ARRAY_SIZE(wbuf) - 4);
-  if (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0) {
-    filep->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh);
-    filep->modification_time = SYS2UNIX_TIME(
-        info.ftLastWriteTime.dwLowDateTime,
-        info.ftLastWriteTime.dwHighDateTime);
-    filep->is_directory = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
-    // If file name is fishy, reset the file structure and return error.
-    // Note it is important to reset, not just return the error, cause
-    // functions like is_file_opened() check the struct.
-    if (!filep->is_directory && !path_cannot_disclose_cgi(path)) {
-      memset(filep, 0, sizeof(*filep));
-    }
-  }
-
-  return filep->modification_time != 0;
-}
-
-static int mg_remove(const char *path) {
-  wchar_t wbuf[PATH_MAX];
-  to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
-  return DeleteFileW(wbuf) ? 0 : -1;
-}
-
-static int mg_mkdir(const char *path, int mode) {
-  char buf[PATH_MAX];
-  wchar_t wbuf[PATH_MAX];
-
-  (void) mode;
-  mg_strlcpy(buf, path, sizeof(buf));
-  change_slashes_to_backslashes(buf);
-
-  (void) MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, ARRAY_SIZE(wbuf));
-
-  return CreateDirectoryW(wbuf, NULL) ? 0 : -1;
-}
-
-// Implementation of POSIX opendir/closedir/readdir for Windows.
-static DIR * opendir(const char *name) {
-  DIR *dir = NULL;
-  wchar_t wpath[PATH_MAX];
-  DWORD attrs;
-
-  if (name == NULL) {
-    SetLastError(ERROR_BAD_ARGUMENTS);
-  } else if ((dir = (DIR *) malloc(sizeof(*dir))) == NULL) {
-    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-  } else {
-    to_unicode(name, wpath, ARRAY_SIZE(wpath));
-    attrs = GetFileAttributesW(wpath);
-    if (attrs != 0xFFFFFFFF &&
-        ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) {
-      (void) wcscat(wpath, L"\\*");
-      dir->handle = FindFirstFileW(wpath, &dir->info);
-      dir->result.d_name[0] = '\0';
-    } else {
-      free(dir);
-      dir = NULL;
-    }
-  }
-
-  return dir;
-}
-
-static int closedir(DIR *dir) {
-  int result = 0;
-
-  if (dir != NULL) {
-    if (dir->handle != INVALID_HANDLE_VALUE)
-      result = FindClose(dir->handle) ? 0 : -1;
-
-    free(dir);
-  } else {
-    result = -1;
-    SetLastError(ERROR_BAD_ARGUMENTS);
-  }
-
-  return result;
-}
-
-static struct dirent *readdir(DIR *dir) {
-  struct dirent *result = 0;
-
-  if (dir) {
-    if (dir->handle != INVALID_HANDLE_VALUE) {
-      result = &dir->result;
-      (void) WideCharToMultiByte(CP_UTF8, 0,
-          dir->info.cFileName, -1, result->d_name,
-          sizeof(result->d_name), NULL, NULL);
-
-      if (!FindNextFileW(dir->handle, &dir->info)) {
-        (void) FindClose(dir->handle);
-        dir->handle = INVALID_HANDLE_VALUE;
-      }
-
-    } else {
-      SetLastError(ERROR_FILE_NOT_FOUND);
-    }
-  } else {
-    SetLastError(ERROR_BAD_ARGUMENTS);
-  }
-
-  return result;
-}
-
-#ifndef HAVE_POLL
-static int poll(struct pollfd *pfd, int n, int milliseconds) {
-  struct timeval tv;
-  fd_set set;
-  int i, result;
-  SOCKET maxfd = 0;
-
-  tv.tv_sec = milliseconds / 1000;
-  tv.tv_usec = (milliseconds % 1000) * 1000;
-  FD_ZERO(&set);
-
-  for (i = 0; i < n; i++) {
-    FD_SET((SOCKET) pfd[i].fd, &set);
-    pfd[i].revents = 0;
-
-    if (pfd[i].fd > maxfd) {
-        maxfd = pfd[i].fd;
-    }
-  }
-
-  if ((result = select(maxfd + 1, &set, NULL, NULL, &tv)) > 0) {
-    for (i = 0; i < n; i++) {
-      if (FD_ISSET(pfd[i].fd, &set)) {
-        pfd[i].revents = POLLIN;
-      }
-    }
-  }
-
-  return result;
-}
-#endif // HAVE_POLL
-
-static void set_close_on_exec(SOCKET sock) {
-  (void) SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0);
-}
-
-int mg_start_thread(mg_thread_func_t f, void *p) {
-  return (long)_beginthread((void (__cdecl *)(void *)) f, 0, p) == -1L ? -1 : 0;
-}
-
-static HANDLE dlopen(const char *dll_name, int flags) {
-  wchar_t wbuf[PATH_MAX];
-  (void) flags;
-  to_unicode(dll_name, wbuf, ARRAY_SIZE(wbuf));
-  return LoadLibraryW(wbuf);
-}
-
-#if !defined(NO_CGI)
-#define SIGKILL 0
-static int kill(pid_t pid, int sig_num) {
-  (void) TerminateProcess(pid, sig_num);
-  (void) CloseHandle(pid);
-  return 0;
-}
-
-static void trim_trailing_whitespaces(char *s) {
-  char *e = s + strlen(s) - 1;
-  while (e > s && isspace(* (unsigned char *) e)) {
-    *e-- = '\0';
-  }
-}
-
-static pid_t spawn_process(struct mg_connection *conn, const char *prog,
-                           char *envblk, char *envp[], int fdin,
-                           int fdout, const char *dir) {
-  HANDLE me;
-  char *interp, full_interp[PATH_MAX], full_dir[PATH_MAX],
-       cmdline[PATH_MAX], buf[PATH_MAX];
-  FILE *fp;
-  STARTUPINFOA si;
-  PROCESS_INFORMATION pi = { 0 };
-
-  (void) envp;
-
-  memset(&si, 0, sizeof(si));
-  si.cb = sizeof(si);
-
-  // TODO(lsm): redirect CGI errors to the error log file
-  si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
-  si.wShowWindow = SW_HIDE;
-
-  me = GetCurrentProcess();
-  DuplicateHandle(me, (HANDLE) _get_osfhandle(fdin), me,
-                  &si.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS);
-  DuplicateHandle(me, (HANDLE) _get_osfhandle(fdout), me,
-                  &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS);
-
-  // If CGI file is a script, try to read the interpreter line
-  interp = conn->ctx->config[CGI_INTERPRETER];
-  if (interp == NULL) {
-    buf[0] = buf[1] = '\0';
-
-    // Read the first line of the script into the buffer
-    snprintf(cmdline, sizeof(cmdline), "%s%c%s", dir, '/', prog);
-    if ((fp = mg_fopen(cmdline, "r")) != NULL) {
-      fgets(buf, sizeof(buf), fp);
-      fclose(fp);
-      buf[sizeof(buf) - 1] = '\0';
-    }
-
-    if (buf[0] == '#' && buf[1] == '!') {
-      trim_trailing_whitespaces(buf + 2);
-    } else {
-      buf[2] = '\0';
-    }
-    interp = buf + 2;
-  }
-
-  if (interp[0] != '\0') {
-    GetFullPathNameA(interp, sizeof(full_interp), full_interp, NULL);
-    interp = full_interp;
-  }
-  GetFullPathNameA(dir, sizeof(full_dir), full_dir, NULL);
-
-  mg_snprintf(cmdline, sizeof(cmdline), "%s%s\"%s\\%s\"",
-              interp, interp[0] == '\0' ? "" : " ", full_dir, prog);
-
-  DEBUG_TRACE(("Running [%s]", cmdline));
-  if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE,
-        CREATE_NEW_PROCESS_GROUP, envblk, NULL, &si, &pi) == 0) {
-    cry(conn, "%s: CreateProcess(%s): %ld",
-        __func__, cmdline, ERRNO);
-    pi.hProcess = (pid_t) -1;
-  }
-
-  (void) CloseHandle(si.hStdOutput);
-  (void) CloseHandle(si.hStdInput);
-  (void) CloseHandle(pi.hThread);
-
-  return (pid_t) pi.hProcess;
-}
-#endif // !NO_CGI
-
-static int set_non_blocking_mode(SOCKET sock) {
-  unsigned long on = 1;
-  return ioctlsocket(sock, FIONBIO, &on);
-}
-
-#else
-static int mg_stat(const char *path, struct file *filep) {
-  struct stat st;
-
-  filep->modification_time = (time_t) 0;
-  if (stat(path, &st) == 0) {
-    filep->size = st.st_size;
-    filep->modification_time = st.st_mtime;
-    filep->is_directory = S_ISDIR(st.st_mode);
-
-    // See https://github.com/cesanta/mongoose/issues/109
-    // Some filesystems report modification time as 0. Artificially
-    // bump it up to mark mg_stat() success.
-    if (filep->modification_time == (time_t) 0) {
-      filep->modification_time = (time_t) 1;
-    }
-  }
-
-  return filep->modification_time != (time_t) 0;
-}
-
-static void set_close_on_exec(int fd) {
-  fcntl(fd, F_SETFD, FD_CLOEXEC);
-}
-
-int mg_start_thread(mg_thread_func_t func, void *param) {
-  pthread_t thread_id;
-  pthread_attr_t attr;
-  int result;
-
-  (void) pthread_attr_init(&attr);
-  (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-
-#if USE_STACK_SIZE > 1
-  // Compile-time option to control stack size, e.g. -DUSE_STACK_SIZE=16384
-  (void) pthread_attr_setstacksize(&attr, USE_STACK_SIZE);
-#endif
-
-  result = pthread_create(&thread_id, &attr, func, param);
-  pthread_attr_destroy(&attr);
-
-  return result;
-}
-
-#ifndef NO_CGI
-static pid_t spawn_process(struct mg_connection *conn, const char *prog,
-                           char *envblk, char *envp[], int fdin,
-                           int fdout, const char *dir) {
-  pid_t pid;
-  const char *interp;
-
-  (void) envblk;
-
-  if ((pid = fork()) == -1) {
-    // Parent
-    send_http_error(conn, 500, http_500_error, "fork(): %s", strerror(ERRNO));
-  } else if (pid == 0) {
-    // Child
-    if (chdir(dir) != 0) {
-      cry(conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO));
-    } else if (dup2(fdin, 0) == -1) {
-      cry(conn, "%s: dup2(%d, 0): %s", __func__, fdin, strerror(ERRNO));
-    } else if (dup2(fdout, 1) == -1) {
-      cry(conn, "%s: dup2(%d, 1): %s", __func__, fdout, strerror(ERRNO));
-    } else {
-      // Not redirecting stderr to stdout, to avoid output being littered
-      // with the error messages.
-      (void) close(fdin);
-      (void) close(fdout);
-
-      // After exec, all signal handlers are restored to their default values,
-      // with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's
-      // implementation, SIGCHLD's handler will leave unchanged after exec
-      // if it was set to be ignored. Restore it to default action.
-      signal(SIGCHLD, SIG_DFL);
-
-      interp = conn->ctx->config[CGI_INTERPRETER];
-      if (interp == NULL) {
-        (void) execle(prog, prog, NULL, envp);
-        cry(conn, "%s: execle(%s): %s", __func__, prog, strerror(ERRNO));
-      } else {
-        (void) execle(interp, interp, prog, NULL, envp);
-        cry(conn, "%s: execle(%s %s): %s", __func__, interp, prog,
-            strerror(ERRNO));
-      }
-    }
-    exit(EXIT_FAILURE);
-  }
-
-  return pid;
-}
-#endif // !NO_CGI
-
-static int set_non_blocking_mode(SOCKET sock) {
-  int flags;
-
-  flags = fcntl(sock, F_GETFL, 0);
-  (void) fcntl(sock, F_SETFL, flags | O_NONBLOCK);
-
-  return 0;
-}
-#endif // _WIN32
-
 // Write data to the IO channel - opened file descriptor, socket or SSL
 // descriptor. Return number of bytes written.
 static int64_t push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf,
diff --git a/build/src/unix.c b/build/src/unix.c
new file mode 100644
index 000000000..577fc3fc7
--- /dev/null
+++ b/build/src/unix.c
@@ -0,0 +1,105 @@
+#include "internal.h"
+
+#if !defined(_WIN32)
+static int mg_stat(const char *path, struct file *filep) {
+  struct stat st;
+
+  filep->modification_time = (time_t) 0;
+  if (stat(path, &st) == 0) {
+    filep->size = st.st_size;
+    filep->modification_time = st.st_mtime;
+    filep->is_directory = S_ISDIR(st.st_mode);
+
+    // See https://github.com/cesanta/mongoose/issues/109
+    // Some filesystems report modification time as 0. Artificially
+    // bump it up to mark mg_stat() success.
+    if (filep->modification_time == (time_t) 0) {
+      filep->modification_time = (time_t) 1;
+    }
+  }
+
+  return filep->modification_time != (time_t) 0;
+}
+
+static void set_close_on_exec(int fd) {
+  fcntl(fd, F_SETFD, FD_CLOEXEC);
+}
+
+int mg_start_thread(mg_thread_func_t func, void *param) {
+  pthread_t thread_id;
+  pthread_attr_t attr;
+  int result;
+
+  (void) pthread_attr_init(&attr);
+  (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+#if USE_STACK_SIZE > 1
+  // Compile-time option to control stack size, e.g. -DUSE_STACK_SIZE=16384
+  (void) pthread_attr_setstacksize(&attr, USE_STACK_SIZE);
+#endif
+
+  result = pthread_create(&thread_id, &attr, func, param);
+  pthread_attr_destroy(&attr);
+
+  return result;
+}
+
+#ifndef NO_CGI
+static pid_t spawn_process(struct mg_connection *conn, const char *prog,
+                           char *envblk, char *envp[], int fdin,
+                           int fdout, const char *dir) {
+  pid_t pid;
+  const char *interp;
+
+  (void) envblk;
+
+  if ((pid = fork()) == -1) {
+    // Parent
+    send_http_error(conn, 500, http_500_error, "fork(): %s", strerror(ERRNO));
+  } else if (pid == 0) {
+    // Child
+    if (chdir(dir) != 0) {
+      cry(conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO));
+    } else if (dup2(fdin, 0) == -1) {
+      cry(conn, "%s: dup2(%d, 0): %s", __func__, fdin, strerror(ERRNO));
+    } else if (dup2(fdout, 1) == -1) {
+      cry(conn, "%s: dup2(%d, 1): %s", __func__, fdout, strerror(ERRNO));
+    } else {
+      // Not redirecting stderr to stdout, to avoid output being littered
+      // with the error messages.
+      (void) close(fdin);
+      (void) close(fdout);
+
+      // After exec, all signal handlers are restored to their default values,
+      // with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's
+      // implementation, SIGCHLD's handler will leave unchanged after exec
+      // if it was set to be ignored. Restore it to default action.
+      signal(SIGCHLD, SIG_DFL);
+
+      interp = conn->ctx->config[CGI_INTERPRETER];
+      if (interp == NULL) {
+        (void) execle(prog, prog, NULL, envp);
+        cry(conn, "%s: execle(%s): %s", __func__, prog, strerror(ERRNO));
+      } else {
+        (void) execle(interp, interp, prog, NULL, envp);
+        cry(conn, "%s: execle(%s %s): %s", __func__, interp, prog,
+            strerror(ERRNO));
+      }
+    }
+    exit(EXIT_FAILURE);
+  }
+
+  return pid;
+}
+#endif // !NO_CGI
+
+static int set_non_blocking_mode(SOCKET sock) {
+  int flags;
+
+  flags = fcntl(sock, F_GETFL, 0);
+  (void) fcntl(sock, F_SETFL, flags | O_NONBLOCK);
+
+  return 0;
+}
+#endif // _WIN32
+
diff --git a/build/src/win32.c b/build/src/win32.c
new file mode 100644
index 000000000..93c7c9628
--- /dev/null
+++ b/build/src/win32.c
@@ -0,0 +1,401 @@
+#include "internal.h"
+
+#if defined(_WIN32)
+static pthread_t pthread_self(void) {
+  return GetCurrentThreadId();
+}
+
+static int pthread_mutex_init(pthread_mutex_t *mutex, void *unused) {
+  (void) unused;
+  *mutex = CreateMutex(NULL, FALSE, NULL);
+  return *mutex == NULL ? -1 : 0;
+}
+
+static int pthread_mutex_destroy(pthread_mutex_t *mutex) {
+  return CloseHandle(*mutex) == 0 ? -1 : 0;
+}
+
+static int pthread_mutex_lock(pthread_mutex_t *mutex) {
+  return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0? 0 : -1;
+}
+
+static int pthread_mutex_unlock(pthread_mutex_t *mutex) {
+  return ReleaseMutex(*mutex) == 0 ? -1 : 0;
+}
+
+static int pthread_cond_init(pthread_cond_t *cv, const void *unused) {
+  (void) unused;
+  cv->signal = CreateEvent(NULL, FALSE, FALSE, NULL);
+  cv->broadcast = CreateEvent(NULL, TRUE, FALSE, NULL);
+  return cv->signal != NULL && cv->broadcast != NULL ? 0 : -1;
+}
+
+static int pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex) {
+  HANDLE handles[] = {cv->signal, cv->broadcast};
+  ReleaseMutex(*mutex);
+  WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+  return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0? 0 : -1;
+}
+
+static int pthread_cond_signal(pthread_cond_t *cv) {
+  return SetEvent(cv->signal) == 0 ? -1 : 0;
+}
+
+static int pthread_cond_broadcast(pthread_cond_t *cv) {
+  // Implementation with PulseEvent() has race condition, see
+  // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
+  return PulseEvent(cv->broadcast) == 0 ? -1 : 0;
+}
+
+static int pthread_cond_destroy(pthread_cond_t *cv) {
+  return CloseHandle(cv->signal) && CloseHandle(cv->broadcast) ? 0 : -1;
+}
+
+// For Windows, change all slashes to backslashes in path names.
+static void change_slashes_to_backslashes(char *path) {
+  int i;
+
+  for (i = 0; path[i] != '\0'; i++) {
+    if (path[i] == '/')
+      path[i] = '\\';
+    // i > 0 check is to preserve UNC paths, like \\server\file.txt
+    if (path[i] == '\\' && i > 0)
+      while (path[i + 1] == '\\' || path[i + 1] == '/')
+        (void) memmove(path + i + 1,
+            path + i + 2, strlen(path + i + 1));
+  }
+}
+
+// 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_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len) {
+  char buf[PATH_MAX * 2], buf2[PATH_MAX * 2];
+
+  mg_strlcpy(buf, path, sizeof(buf));
+  change_slashes_to_backslashes(buf);
+
+  // Convert to Unicode and back. If doubly-converted string does not
+  // match the original, something is fishy, reject.
+  memset(wbuf, 0, wbuf_len * sizeof(wchar_t));
+  MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len);
+  WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2),
+                      NULL, NULL);
+  if (strcmp(buf, buf2) != 0) {
+    wbuf[0] = L'\0';
+  }
+}
+
+#if defined(_WIN32_WCE)
+static time_t time(time_t *ptime) {
+  time_t t;
+  SYSTEMTIME st;
+  FILETIME ft;
+
+  GetSystemTime(&st);
+  SystemTimeToFileTime(&st, &ft);
+  t = SYS2UNIX_TIME(ft.dwLowDateTime, ft.dwHighDateTime);
+
+  if (ptime != NULL) {
+    *ptime = t;
+  }
+
+  return t;
+}
+
+static struct tm *localtime(const time_t *ptime, struct tm *ptm) {
+  int64_t t = ((int64_t) *ptime) * RATE_DIFF + EPOCH_DIFF;
+  FILETIME ft, lft;
+  SYSTEMTIME st;
+  TIME_ZONE_INFORMATION tzinfo;
+
+  if (ptm == NULL) {
+    return NULL;
+  }
+
+  * (int64_t *) &ft = t;
+  FileTimeToLocalFileTime(&ft, &lft);
+  FileTimeToSystemTime(&lft, &st);
+  ptm->tm_year = st.wYear - 1900;
+  ptm->tm_mon = st.wMonth - 1;
+  ptm->tm_wday = st.wDayOfWeek;
+  ptm->tm_mday = st.wDay;
+  ptm->tm_hour = st.wHour;
+  ptm->tm_min = st.wMinute;
+  ptm->tm_sec = st.wSecond;
+  ptm->tm_yday = 0; // hope nobody uses this
+  ptm->tm_isdst =
+    GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_DAYLIGHT ? 1 : 0;
+
+  return ptm;
+}
+
+static struct tm *gmtime(const time_t *ptime, struct tm *ptm) {
+  // FIXME(lsm): fix this.
+  return localtime(ptime, ptm);
+}
+
+static size_t strftime(char *dst, size_t dst_size, const char *fmt,
+                       const struct tm *tm) {
+  (void) snprintf(dst, dst_size, "implement strftime() for WinCE");
+  return 0;
+}
+#endif
+
+// Windows happily opens files with some garbage at the end of file name.
+// For example, fopen("a.cgi    ", "r") on Windows successfully opens
+// "a.cgi", despite one would expect an error back.
+// This function returns non-0 if path ends with some garbage.
+static int path_cannot_disclose_cgi(const char *path) {
+  static const char *allowed_last_characters = "_-";
+  int last = path[strlen(path) - 1];
+  return isalnum(last) || strchr(allowed_last_characters, last) != NULL;
+}
+
+static int mg_stat(const char *path, struct file *filep) {
+  wchar_t wbuf[PATH_MAX] = L"\\\\?\\";
+  WIN32_FILE_ATTRIBUTE_DATA info;
+
+  filep->modification_time = 0;
+  to_unicode(path, wbuf + 4, ARRAY_SIZE(wbuf) - 4);
+  if (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0) {
+    filep->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh);
+    filep->modification_time = SYS2UNIX_TIME(
+        info.ftLastWriteTime.dwLowDateTime,
+        info.ftLastWriteTime.dwHighDateTime);
+    filep->is_directory = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
+    // If file name is fishy, reset the file structure and return error.
+    // Note it is important to reset, not just return the error, cause
+    // functions like is_file_opened() check the struct.
+    if (!filep->is_directory && !path_cannot_disclose_cgi(path)) {
+      memset(filep, 0, sizeof(*filep));
+    }
+  }
+
+  return filep->modification_time != 0;
+}
+
+static int mg_remove(const char *path) {
+  wchar_t wbuf[PATH_MAX];
+  to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
+  return DeleteFileW(wbuf) ? 0 : -1;
+}
+
+static int mg_mkdir(const char *path, int mode) {
+  char buf[PATH_MAX];
+  wchar_t wbuf[PATH_MAX];
+
+  (void) mode;
+  mg_strlcpy(buf, path, sizeof(buf));
+  change_slashes_to_backslashes(buf);
+
+  (void) MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, ARRAY_SIZE(wbuf));
+
+  return CreateDirectoryW(wbuf, NULL) ? 0 : -1;
+}
+
+// Implementation of POSIX opendir/closedir/readdir for Windows.
+static DIR * opendir(const char *name) {
+  DIR *dir = NULL;
+  wchar_t wpath[PATH_MAX];
+  DWORD attrs;
+
+  if (name == NULL) {
+    SetLastError(ERROR_BAD_ARGUMENTS);
+  } else if ((dir = (DIR *) malloc(sizeof(*dir))) == NULL) {
+    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+  } else {
+    to_unicode(name, wpath, ARRAY_SIZE(wpath));
+    attrs = GetFileAttributesW(wpath);
+    if (attrs != 0xFFFFFFFF &&
+        ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) {
+      (void) wcscat(wpath, L"\\*");
+      dir->handle = FindFirstFileW(wpath, &dir->info);
+      dir->result.d_name[0] = '\0';
+    } else {
+      free(dir);
+      dir = NULL;
+    }
+  }
+
+  return dir;
+}
+
+static int closedir(DIR *dir) {
+  int result = 0;
+
+  if (dir != NULL) {
+    if (dir->handle != INVALID_HANDLE_VALUE)
+      result = FindClose(dir->handle) ? 0 : -1;
+
+    free(dir);
+  } else {
+    result = -1;
+    SetLastError(ERROR_BAD_ARGUMENTS);
+  }
+
+  return result;
+}
+
+static struct dirent *readdir(DIR *dir) {
+  struct dirent *result = 0;
+
+  if (dir) {
+    if (dir->handle != INVALID_HANDLE_VALUE) {
+      result = &dir->result;
+      (void) WideCharToMultiByte(CP_UTF8, 0,
+          dir->info.cFileName, -1, result->d_name,
+          sizeof(result->d_name), NULL, NULL);
+
+      if (!FindNextFileW(dir->handle, &dir->info)) {
+        (void) FindClose(dir->handle);
+        dir->handle = INVALID_HANDLE_VALUE;
+      }
+
+    } else {
+      SetLastError(ERROR_FILE_NOT_FOUND);
+    }
+  } else {
+    SetLastError(ERROR_BAD_ARGUMENTS);
+  }
+
+  return result;
+}
+
+#ifndef HAVE_POLL
+static int poll(struct pollfd *pfd, int n, int milliseconds) {
+  struct timeval tv;
+  fd_set set;
+  int i, result;
+  SOCKET maxfd = 0;
+
+  tv.tv_sec = milliseconds / 1000;
+  tv.tv_usec = (milliseconds % 1000) * 1000;
+  FD_ZERO(&set);
+
+  for (i = 0; i < n; i++) {
+    FD_SET((SOCKET) pfd[i].fd, &set);
+    pfd[i].revents = 0;
+
+    if (pfd[i].fd > maxfd) {
+        maxfd = pfd[i].fd;
+    }
+  }
+
+  if ((result = select(maxfd + 1, &set, NULL, NULL, &tv)) > 0) {
+    for (i = 0; i < n; i++) {
+      if (FD_ISSET(pfd[i].fd, &set)) {
+        pfd[i].revents = POLLIN;
+      }
+    }
+  }
+
+  return result;
+}
+#endif // HAVE_POLL
+
+static void set_close_on_exec(SOCKET sock) {
+  (void) SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0);
+}
+
+int mg_start_thread(mg_thread_func_t f, void *p) {
+  return (long)_beginthread((void (__cdecl *)(void *)) f, 0, p) == -1L ? -1 : 0;
+}
+
+static HANDLE dlopen(const char *dll_name, int flags) {
+  wchar_t wbuf[PATH_MAX];
+  (void) flags;
+  to_unicode(dll_name, wbuf, ARRAY_SIZE(wbuf));
+  return LoadLibraryW(wbuf);
+}
+
+#if !defined(NO_CGI)
+#define SIGKILL 0
+static int kill(pid_t pid, int sig_num) {
+  (void) TerminateProcess(pid, sig_num);
+  (void) CloseHandle(pid);
+  return 0;
+}
+
+static void trim_trailing_whitespaces(char *s) {
+  char *e = s + strlen(s) - 1;
+  while (e > s && isspace(* (unsigned char *) e)) {
+    *e-- = '\0';
+  }
+}
+
+static pid_t spawn_process(struct mg_connection *conn, const char *prog,
+                           char *envblk, char *envp[], int fdin,
+                           int fdout, const char *dir) {
+  HANDLE me;
+  char *interp, full_interp[PATH_MAX], full_dir[PATH_MAX],
+       cmdline[PATH_MAX], buf[PATH_MAX];
+  FILE *fp;
+  STARTUPINFOA si;
+  PROCESS_INFORMATION pi = { 0 };
+
+  (void) envp;
+
+  memset(&si, 0, sizeof(si));
+  si.cb = sizeof(si);
+
+  // TODO(lsm): redirect CGI errors to the error log file
+  si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+  si.wShowWindow = SW_HIDE;
+
+  me = GetCurrentProcess();
+  DuplicateHandle(me, (HANDLE) _get_osfhandle(fdin), me,
+                  &si.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS);
+  DuplicateHandle(me, (HANDLE) _get_osfhandle(fdout), me,
+                  &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS);
+
+  // If CGI file is a script, try to read the interpreter line
+  interp = conn->ctx->config[CGI_INTERPRETER];
+  if (interp == NULL) {
+    buf[0] = buf[1] = '\0';
+
+    // Read the first line of the script into the buffer
+    snprintf(cmdline, sizeof(cmdline), "%s%c%s", dir, '/', prog);
+    if ((fp = mg_fopen(cmdline, "r")) != NULL) {
+      fgets(buf, sizeof(buf), fp);
+      fclose(fp);
+      buf[sizeof(buf) - 1] = '\0';
+    }
+
+    if (buf[0] == '#' && buf[1] == '!') {
+      trim_trailing_whitespaces(buf + 2);
+    } else {
+      buf[2] = '\0';
+    }
+    interp = buf + 2;
+  }
+
+  if (interp[0] != '\0') {
+    GetFullPathNameA(interp, sizeof(full_interp), full_interp, NULL);
+    interp = full_interp;
+  }
+  GetFullPathNameA(dir, sizeof(full_dir), full_dir, NULL);
+
+  mg_snprintf(cmdline, sizeof(cmdline), "%s%s\"%s\\%s\"",
+              interp, interp[0] == '\0' ? "" : " ", full_dir, prog);
+
+  DEBUG_TRACE(("Running [%s]", cmdline));
+  if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE,
+        CREATE_NEW_PROCESS_GROUP, envblk, NULL, &si, &pi) == 0) {
+    cry(conn, "%s: CreateProcess(%s): %ld",
+        __func__, cmdline, ERRNO);
+    pi.hProcess = (pid_t) -1;
+  }
+
+  (void) CloseHandle(si.hStdOutput);
+  (void) CloseHandle(si.hStdInput);
+  (void) CloseHandle(pi.hThread);
+
+  return (pid_t) pi.hProcess;
+}
+#endif // !NO_CGI
+
+static int set_non_blocking_mode(SOCKET sock) {
+  unsigned long on = 1;
+  return ioctlsocket(sock, FIONBIO, &on);
+}
+#endif
diff --git a/mongoose.c b/mongoose.c
index fd647c86c..5bd2d8bc7 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -323,7 +323,7 @@ struct ssl_func {
   void  (*ptr)(void); // Function pointer
 };
 
-static struct ssl_func ssl_sw[];
+static struct ssl_func ssl_sw[30];
 
 #define SSL_free (* (void (*)(SSL *)) ssl_sw[0].ptr)
 #define SSL_accept (* (int (*)(SSL *)) ssl_sw[1].ptr)
@@ -460,6 +460,11 @@ struct de {
 
 static FILE *mg_fopen(const char *path, const char *mode);
 static int mg_stat(const char *path, struct file *filep);
+static void send_http_error(struct mg_connection *, int, const char *,
+                            PRINTF_FORMAT_STRING(const char *fmt), ...)
+                            PRINTF_ARGS(4, 5);
+static void cry(struct mg_connection *conn,
+                PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
 
 #ifdef USE_LUA
 #include "lua_5.2.1.h"
@@ -1311,158 +1316,7 @@ int mg_modify_passwords_file(const char *fname, const char *domain,
   return 1;
 }
 
-// Return number of bytes left to read for this connection
-static int64_t left_to_read(const struct mg_connection *conn) {
-  return conn->content_len + conn->request_len - conn->num_bytes_read;
-}
-
-static int call_user(int type, struct mg_connection *conn, void *p) {
-  if (conn != NULL && conn->ctx != NULL) {
-    conn->event.user_data = conn->ctx->user_data;
-    conn->event.type = type;
-    conn->event.event_param = p;
-    conn->event.request_info = &conn->request_info;
-    conn->event.conn = conn;
-  }
-  return conn == NULL || conn->ctx == NULL || conn->ctx->event_handler == NULL ?
-    0 : conn->ctx->event_handler(&conn->event);
-}
-
-static FILE *mg_fopen(const char *path, const char *mode) {
-#ifdef _WIN32
-  wchar_t wbuf[PATH_MAX], wmode[20];
-  to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
-  MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, ARRAY_SIZE(wmode));
-  return _wfopen(wbuf, wmode);
-#else
-  return fopen(path, mode);
-#endif
-}
-
-static void sockaddr_to_string(char *buf, size_t len,
-                                     const union usa *usa) {
-  buf[0] = '\0';
-#if defined(USE_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
-}
-
-static void cry(struct mg_connection *conn,
-                PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
-
-// Print error message to the opened error log stream.
-static void cry(struct mg_connection *conn, const char *fmt, ...) {
-  char buf[MG_BUF_LEN], src_addr[IP_ADDR_STR_LEN];
-  va_list ap;
-  FILE *fp;
-  time_t timestamp;
-
-  va_start(ap, fmt);
-  (void) vsnprintf(buf, sizeof(buf), fmt, ap);
-  va_end(ap);
-
-  // Do not lock when getting the callback value, here and below.
-  // I suppose this is fine, since function cannot disappear in the
-  // same way string option can.
-  if (call_user(MG_EVENT_LOG, conn, buf) == 0) {
-    fp = conn->ctx == NULL || conn->ctx->config[ERROR_LOG_FILE] == NULL ? NULL :
-      fopen(conn->ctx->config[ERROR_LOG_FILE], "a+");
-
-    if (fp != NULL) {
-      flockfile(fp);
-      timestamp = time(NULL);
-
-      sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
-      fprintf(fp, "[%010lu] [error] [client %s] ", (unsigned long) timestamp,
-              src_addr);
-
-      if (conn->request_info.request_method != NULL) {
-        fprintf(fp, "%s %s: ", conn->request_info.request_method,
-                conn->request_info.uri);
-      }
-
-      fprintf(fp, "%s", buf);
-      fputc('\n', fp);
-      funlockfile(fp);
-      fclose(fp);
-    }
-  }
-}
-
-// Return fake connection structure. Used for logging, if connection
-// is not applicable at the moment of logging.
-static struct mg_connection *fc(struct mg_context *ctx) {
-  static struct mg_connection fake_connection;
-  fake_connection.ctx = ctx;
-  // See https://github.com/cesanta/mongoose/issues/236
-  fake_connection.event.user_data = ctx->user_data;
-  return &fake_connection;
-}
-
-const char *mg_version(void) {
-  return MONGOOSE_VERSION;
-}
-
-// HTTP 1.1 assumes keep alive if "Connection:" header is not set
-// This function must tolerate situations when connection info is not
-// set up, for example if request parsing failed.
-static int should_keep_alive(const struct mg_connection *conn) {
-  const char *http_version = conn->request_info.http_version;
-  const char *header = mg_get_header(conn, "Connection");
-  if (conn->must_close ||
-      conn->status_code == 401 ||
-      mg_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0 ||
-      (header != NULL && mg_strcasecmp(header, "keep-alive") != 0) ||
-      (header == NULL && http_version && strcmp(http_version, "1.1"))) {
-    return 0;
-  }
-  return 1;
-}
-
-static const char *suggest_connection_header(const struct mg_connection *conn) {
-  return should_keep_alive(conn) ? "keep-alive" : "close";
-}
-
-static void send_http_error(struct mg_connection *, int, const char *,
-                            PRINTF_FORMAT_STRING(const char *fmt), ...)
-  PRINTF_ARGS(4, 5);
-
-
-static void send_http_error(struct mg_connection *conn, int status,
-                            const char *reason, const char *fmt, ...) {
-  char buf[MG_BUF_LEN];
-  va_list ap;
-  int len = 0;
-
-  conn->status_code = status;
-  buf[0] = '\0';
-
-  // Errors 1xx, 204 and 304 MUST NOT send a body
-  if (status > 199 && status != 204 && status != 304) {
-    len = mg_snprintf(buf, sizeof(buf), "Error %d: %s", status, reason);
-    buf[len++] = '\n';
-
-    va_start(ap, fmt);
-    len += mg_vsnprintf(buf + len, sizeof(buf) - len, fmt, ap);
-    va_end(ap);
-  }
-  DEBUG_TRACE(("[%s]", buf));
-
-  mg_printf(conn, "HTTP/1.1 %d %s\r\n"
-            "Content-Length: %d\r\n"
-            "Connection: %s\r\n\r\n", status, reason, len,
-            suggest_connection_header(conn));
-  conn->num_bytes_sent += mg_printf(conn, "%s", buf);
-}
-
-#if defined(_WIN32) && !defined(__SYMBIAN32__)
+#if defined(_WIN32)
 static pthread_t pthread_self(void) {
   return GetCurrentThreadId();
 }
@@ -1860,8 +1714,9 @@ static int set_non_blocking_mode(SOCKET sock) {
   unsigned long on = 1;
   return ioctlsocket(sock, FIONBIO, &on);
 }
+#endif
 
-#else
+#if !defined(_WIN32)
 static int mg_stat(const char *path, struct file *filep) {
   struct stat st;
 
@@ -1964,6 +1819,150 @@ static int set_non_blocking_mode(SOCKET sock) {
 }
 #endif // _WIN32
 
+
+// Return number of bytes left to read for this connection
+static int64_t left_to_read(const struct mg_connection *conn) {
+  return conn->content_len + conn->request_len - conn->num_bytes_read;
+}
+
+static int call_user(int type, struct mg_connection *conn, void *p) {
+  if (conn != NULL && conn->ctx != NULL) {
+    conn->event.user_data = conn->ctx->user_data;
+    conn->event.type = type;
+    conn->event.event_param = p;
+    conn->event.request_info = &conn->request_info;
+    conn->event.conn = conn;
+  }
+  return conn == NULL || conn->ctx == NULL || conn->ctx->event_handler == NULL ?
+    0 : conn->ctx->event_handler(&conn->event);
+}
+
+static FILE *mg_fopen(const char *path, const char *mode) {
+#ifdef _WIN32
+  wchar_t wbuf[PATH_MAX], wmode[20];
+  to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
+  MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, ARRAY_SIZE(wmode));
+  return _wfopen(wbuf, wmode);
+#else
+  return fopen(path, mode);
+#endif
+}
+
+static void sockaddr_to_string(char *buf, size_t len,
+                                     const union usa *usa) {
+  buf[0] = '\0';
+#if defined(USE_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
+}
+
+// Print error message to the opened error log stream.
+static void cry(struct mg_connection *conn, const char *fmt, ...) {
+  char buf[MG_BUF_LEN], src_addr[IP_ADDR_STR_LEN];
+  va_list ap;
+  FILE *fp;
+  time_t timestamp;
+
+  va_start(ap, fmt);
+  (void) vsnprintf(buf, sizeof(buf), fmt, ap);
+  va_end(ap);
+
+  // Do not lock when getting the callback value, here and below.
+  // I suppose this is fine, since function cannot disappear in the
+  // same way string option can.
+  if (call_user(MG_EVENT_LOG, conn, buf) == 0) {
+    fp = conn->ctx == NULL || conn->ctx->config[ERROR_LOG_FILE] == NULL ? NULL :
+      fopen(conn->ctx->config[ERROR_LOG_FILE], "a+");
+
+    if (fp != NULL) {
+      flockfile(fp);
+      timestamp = time(NULL);
+
+      sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
+      fprintf(fp, "[%010lu] [error] [client %s] ", (unsigned long) timestamp,
+              src_addr);
+
+      if (conn->request_info.request_method != NULL) {
+        fprintf(fp, "%s %s: ", conn->request_info.request_method,
+                conn->request_info.uri);
+      }
+
+      fprintf(fp, "%s", buf);
+      fputc('\n', fp);
+      funlockfile(fp);
+      fclose(fp);
+    }
+  }
+}
+
+// Return fake connection structure. Used for logging, if connection
+// is not applicable at the moment of logging.
+static struct mg_connection *fc(struct mg_context *ctx) {
+  static struct mg_connection fake_connection;
+  fake_connection.ctx = ctx;
+  // See https://github.com/cesanta/mongoose/issues/236
+  fake_connection.event.user_data = ctx->user_data;
+  return &fake_connection;
+}
+
+const char *mg_version(void) {
+  return MONGOOSE_VERSION;
+}
+
+// HTTP 1.1 assumes keep alive if "Connection:" header is not set
+// This function must tolerate situations when connection info is not
+// set up, for example if request parsing failed.
+static int should_keep_alive(const struct mg_connection *conn) {
+  const char *http_version = conn->request_info.http_version;
+  const char *header = mg_get_header(conn, "Connection");
+  if (conn->must_close ||
+      conn->status_code == 401 ||
+      mg_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0 ||
+      (header != NULL && mg_strcasecmp(header, "keep-alive") != 0) ||
+      (header == NULL && http_version && strcmp(http_version, "1.1"))) {
+    return 0;
+  }
+  return 1;
+}
+
+static const char *suggest_connection_header(const struct mg_connection *conn) {
+  return should_keep_alive(conn) ? "keep-alive" : "close";
+}
+
+static void send_http_error(struct mg_connection *conn, int status,
+                            const char *reason, const char *fmt, ...) {
+  char buf[MG_BUF_LEN];
+  va_list ap;
+  int len = 0;
+
+  conn->status_code = status;
+  buf[0] = '\0';
+
+  // Errors 1xx, 204 and 304 MUST NOT send a body
+  if (status > 199 && status != 204 && status != 304) {
+    len = mg_snprintf(buf, sizeof(buf), "Error %d: %s", status, reason);
+    buf[len++] = '\n';
+
+    va_start(ap, fmt);
+    len += mg_vsnprintf(buf + len, sizeof(buf) - len, fmt, ap);
+    va_end(ap);
+  }
+  DEBUG_TRACE(("[%s]", buf));
+
+  mg_printf(conn, "HTTP/1.1 %d %s\r\n"
+            "Content-Length: %d\r\n"
+            "Connection: %s\r\n\r\n", status, reason, len,
+            suggest_connection_header(conn));
+  conn->num_bytes_sent += mg_printf(conn, "%s", buf);
+}
+
 // Write data to the IO channel - opened file descriptor, socket or SSL
 // descriptor. Return number of bytes written.
 static int64_t push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf,
-- 
GitLab