From 8a65611a8cd35e8148ac63a977460c7c6a511f3d Mon Sep 17 00:00:00 2001
From: Sergey Lyubka <valenok@gmail.com>
Date: Mon, 2 Dec 2013 22:22:22 +0000
Subject: [PATCH] Using core.h and modified API

---
 build/src/core.c | 281 ++++++++++++++++++++++++++++++++---------------
 build/src/core.h |  89 +++++++++++++++
 2 files changed, 279 insertions(+), 91 deletions(-)
 create mode 100644 build/src/core.h

diff --git a/build/src/core.c b/build/src/core.c
index dd929c18a..2dc75333a 100644
--- a/build/src/core.c
+++ b/build/src/core.c
@@ -69,6 +69,7 @@ typedef CRITICAL_SECTION mutex_t;
 #define mutex_lock(x) EnterCriticalSection(x)
 #define mutex_unlock(x) LeaveCriticalSection(x)
 #define S_ISDIR(x) ((x) & _S_IFDIR)
+#define sleep(x) Sleep((x) * 1000)
 #ifndef va_copy
 #define va_copy(x,y) x = y
 #endif // MINGW #defines va_copy
@@ -93,7 +94,8 @@ typedef pthread_mutex_t mutex_t;
 #define INT64_FMT PRId64
 #endif
 
-#include "mongoose.h"
+//#include "mongoose.h"
+#include "core.h"
 
 struct linked_list_link { struct linked_list_link *prev, *next; };
 #define LINKED_LIST_INIT(N)  ((N)->next = (N)->prev = (N))
@@ -139,6 +141,12 @@ struct vec {
   int len;
 };
 
+struct uri_handler {
+  struct linked_list_link link;
+  char *uri;
+  mg_uri_handler_t handler;
+};
+
 // NOTE(lsm): this enum shoulds be in sync with the config_options.
 enum {
   ACCESS_CONTROL_LIST, ACCESS_LOG_FILE, AUTH_DOMAIN, CGI_INTERPRETER,
@@ -152,9 +160,9 @@ struct mg_server {
   sock_t listening_sock;
   union socket_address lsa;   // Listening socket address
   struct linked_list_link active_connections;
+  struct linked_list_link uri_handlers;
   char *config_options[NUM_OPTIONS];
-  mg_event_handler_t event_handler;
-  void *user_data;
+  void *server_data;
   sock_t ctl[2];  // Control socketpair. Used to wake up from select() call
 };
 
@@ -170,13 +178,13 @@ union endpoint {
   void *ssl;    // SSL descriptor
 };
 
-enum endpoint_type { EP_NONE, EP_FILE, EP_DIR, EP_CGI, EP_SSL };
+enum endpoint_type { EP_NONE, EP_FILE, EP_DIR, EP_CGI, EP_SSL, EP_USER };
 enum connection_flags { CONN_CLOSE = 1, CONN_SPOOL_DONE = 2 };
 
-struct mg_connection {
+struct connection {
+  struct mg_connection mg_conn;   // XXX: Must be first
   struct linked_list_link link;   // Linkage to server->active_connections
   struct mg_server *server;
-  struct mg_event event;      // NOTE(lsm): this has conn_data attribute
   sock_t client_sock;         // Connected client
   union socket_address csa;   // Client's socket address
   struct iobuf local_iobuf;
@@ -184,7 +192,6 @@ struct mg_connection {
   union endpoint endpoint;
   enum endpoint_type endpoint_type;
   time_t expire_time;
-  struct mg_request_info request_info;
   char *path_info;
   int request_len;
   int flags;
@@ -206,7 +213,7 @@ static const char *static_config_options[] = {
   "hide_files_patterns", NULL,
   "idle_timeout_ms", "30000",
   "index_files","index.html,index.htm,index.cgi,index.shtml,index.php,index.lp",
-  "listening_port", "8080",
+  "listening_port", NULL,
   "num_threads", "50",
   "put_delete_auth_file", NULL,
   "run_as_user", NULL,
@@ -287,13 +294,13 @@ static int get_option_index(const char *name) {
   return -1;
 }
 
-const char *mg_get_option2(const struct mg_server *srv, const char *name) {
+const char *mg_get_option(const struct mg_server *srv, const char *name) {
   int i = get_option_index(name);
   return i == -1 ? NULL : srv->config_options[i] == NULL ? "" :
     srv->config_options[i];
 }
 
-int mg_start_thread(mg_thread_func_t f, void *p) {
+int mg_start_thread(void *(*f)(void *), void *p) {
 #ifdef _WIN32
   return (long)_beginthread((void (__cdecl *)(void *)) f, 0, p) == -1L ? -1 : 0;
 #else
@@ -316,6 +323,7 @@ int mg_start_thread(mg_thread_func_t f, void *p) {
 #endif
 }
 
+#if 0
 static int call_user(int event_type, struct mg_connection *conn, void *p) {
   if (conn != NULL && conn->server != NULL) {
     conn->event.user_data = conn->server->user_data;
@@ -327,6 +335,7 @@ static int call_user(int event_type, struct mg_connection *conn, void *p) {
   return !conn || !conn->server || !conn->server->event_handler ? 0 :
     conn->server->event_handler(&conn->event);
 }
+#endif
 
 static void set_close_on_exec(int fd) {
 #ifdef _WIN32
@@ -547,11 +556,11 @@ static int check_acl(const char *acl, uint32_t remote_ip) {
   return allowed == '+';
 }
 
-static struct mg_connection *accept_new_connection(struct mg_server *server) {
+static struct connection *accept_new_connection(struct mg_server *server) {
   union socket_address sa;
   socklen_t len = sizeof(sa);
   sock_t sock = INVALID_SOCKET;
-  struct mg_connection *conn = NULL;
+  struct connection *conn = NULL;
 
   if ((sock = accept(server->listening_sock, &sa.sa, &len)) == INVALID_SOCKET) {
 #if 0
@@ -562,7 +571,7 @@ static struct mg_connection *accept_new_connection(struct mg_server *server) {
   } else if (!check_acl(server->config_options[ACCESS_CONTROL_LIST],
                         ntohl(* (uint32_t *) &sa.sin.sin_addr))) {
     closesocket(sock);
-  } else if ((conn = (struct mg_connection *)
+  } else if ((conn = (struct connection *)
               calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE)) == NULL) {
     closesocket(sock);
   } else {
@@ -584,7 +593,7 @@ static struct mg_connection *accept_new_connection(struct mg_server *server) {
   return conn;
 }
 
-static void close_conn(struct mg_connection *conn) {
+static void close_conn(struct connection *conn) {
   DBG(("closing %p", conn));
   LINKED_LIST_REMOVE(&conn->link);
   closesocket(conn->client_sock);
@@ -638,7 +647,7 @@ static char *skip(char **buf, const char *delimiters) {
 
 // Parse HTTP headers from the given buffer, advance buffer to the point
 // where parsing stopped.
-static void parse_http_headers(char **buf, struct mg_request_info *ri) {
+static void parse_http_headers(char **buf, struct mg_connection *ri) {
   size_t i;
 
   for (i = 0; i < ARRAY_SIZE(ri->http_headers); i++) {
@@ -658,16 +667,15 @@ static int is_valid_http_method(const char *method) {
     || !strcmp(method, "MKCOL");
 }
 
-// Parse HTTP request, fill in mg_request_info structure.
+// Parse HTTP request, fill in mg_request structure.
 // This function modifies the buffer by NUL-terminating
 // HTTP request components, header names and header values.
-static int parse_http_message(char *buf, int len, struct mg_request_info *ri) {
+static int parse_http_message(char *buf, int len, struct mg_connection *ri) {
   int is_request, request_len = get_request_len((unsigned char *) buf, len);
   if (request_len > 0) {
     // Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_port
-    ri->remote_user = ri->request_method = ri->uri = ri->http_version = NULL;
+    ri->request_method = ri->uri = ri->http_version = NULL;
     ri->num_headers = 0;
-
     buf[request_len - 1] = '\0';
 
     // RFC says that all initial whitespaces should be ingored
@@ -804,7 +812,7 @@ int mg_url_decode(const char *src, int src_len, char *dst,
 }
 
 // Return HTTP header value, or NULL if not found.
-static const char *get_header(const struct mg_request_info *ri, const char *s) {
+const char *mg_get_header(const struct mg_connection *ri, const char *s) {
   int i;
 
   for (i = 0; i < ri->num_headers; i++)
@@ -814,18 +822,14 @@ static const char *get_header(const struct mg_request_info *ri, const char *s) {
   return NULL;
 }
 
-const char *mg_get_header(const struct mg_connection *conn, const char *name) {
-  return get_header(&conn->request_info, name);
-}
-
 // Return 1 if real file has been found, 0 otherwise
-static int convert_uri_to_file_name(struct mg_connection *conn, char *buf,
+static int convert_uri_to_file_name(struct connection *conn, char *buf,
                                     size_t buf_len, struct stat *st) {
   struct vec a, b;
   const char *rewrites = conn->server->config_options[URL_REWRITES],
         *root = conn->server->config_options[DOCUMENT_ROOT],
         *cgi_pat = conn->server->config_options[CGI_PATTERN],
-        *uri = conn->request_info.uri;
+        *uri = conn->mg_conn.uri;
   char *p;
   int match_len;
 
@@ -896,7 +900,8 @@ static int vspool(struct iobuf *io, const char *fmt, va_list ap) {
   return len;
 }
 
-int mg_printf(struct mg_connection *conn, const char *fmt, ...) {
+int mg_printf(struct mg_connection *c, const char *fmt, ...) {
+  struct connection *conn = (struct connection *) c;
   va_list ap;
   int ret;
   va_start(ap, fmt);
@@ -908,7 +913,8 @@ int mg_printf(struct mg_connection *conn, const char *fmt, ...) {
   return ret;
 }
 
-int mg_write(struct mg_connection *conn, const void *buf, int len) {
+int mg_write(struct mg_connection *c, const void *buf, int len) {
+  struct connection *conn = (struct connection *) c;
   int ret;
   mutex_lock(&conn->mutex);
   ret = spool(&conn->remote_iobuf, buf, len);
@@ -921,7 +927,7 @@ static int is_error(int n) {
   return n == 0 || (n < 0 && errno != EINTR && errno != EAGAIN);
 }
 
-static void write_to_client(struct mg_connection *conn) {
+static void write_to_client(struct connection *conn) {
   struct iobuf *io = &conn->remote_iobuf;
   int n = send(conn->client_sock, io->buf, io->len, 0);
 
@@ -938,7 +944,7 @@ static void write_to_client(struct mg_connection *conn) {
   }
 }
 
-const char *mg_get_builtin_mime_type(const char *path) {
+const char *mg_get_mime_type(const char *path) {
   const char *ext;
   size_t i, path_len;
 
@@ -977,7 +983,7 @@ static void get_mime_type(const struct mg_server *server, const char *path,
     }
   }
 
-  vec->ptr = mg_get_builtin_mime_type(path);
+  vec->ptr = mg_get_mime_type(path);
   vec->len = strlen(vec->ptr);
 }
 
@@ -994,7 +1000,7 @@ static void construct_etag(char *buf, size_t buf_len, const struct stat *st) {
            (unsigned long) st->st_mtime, (int64_t) st->st_size);
 }
 
-static void open_file_endpoint(struct mg_connection *conn, const char *path,
+static void open_file_endpoint(struct connection *conn, const char *path,
                                struct stat *st) {
   char date[64], lm[64], etag[64], range[64];
   const char *msg = "OK", *hdr;
@@ -1013,7 +1019,7 @@ static void open_file_endpoint(struct mg_connection *conn, const char *path,
 
   // If Range: header specified, act accordingly
   r1 = r2 = 0;
-  hdr = mg_get_header(conn, "Range");
+  hdr = mg_get_header(&conn->mg_conn, "Range");
   if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0 &&
       r1 >= 0 && r2 >= 0) {
     conn->status_code = 206;
@@ -1031,7 +1037,7 @@ static void open_file_endpoint(struct mg_connection *conn, const char *path,
   gmt_time_string(lm, sizeof(lm), &st->st_mtime);
   construct_etag(etag, sizeof(etag), st);
 
-  mg_printf(conn,
+  mg_printf(&conn->mg_conn,
             "HTTP/1.1 %d %s\r\n"
             "Date: %s\r\n"
             "Last-Modified: %s\r\n"
@@ -1044,46 +1050,67 @@ static void open_file_endpoint(struct mg_connection *conn, const char *path,
             conn->status_code, msg, date, lm, etag, (int) mime_vec.len,
             mime_vec.ptr, cl, "keep-alive", range, EXTRA_HTTP_HEADERS);
 
-  if (!strcmp(conn->request_info.request_method, "HEAD")) {
+  if (!strcmp(conn->mg_conn.request_method, "HEAD")) {
     lseek(conn->endpoint.fd, 0, SEEK_END);
   }
 }
 
-static void open_local_endpoint(struct mg_connection *conn) {
-  struct mg_request_info *ri = &conn->request_info;
+static struct uri_handler *find_uri_handler(struct mg_server *server,
+                                            const char *uri) {
+  struct linked_list_link *lp, *tmp;
+  struct uri_handler *uh;
+
+  LINKED_LIST_FOREACH(&server->uri_handlers, lp, tmp) {
+    uh = LINKED_LIST_ENTRY(lp, struct uri_handler, link);
+    if (!strcmp(uh->uri, uri)) return uh;
+  }
+
+  return NULL;
+}
+
+static void open_local_endpoint(struct connection *conn) {
   char path[MAX_PATH_SIZE] = {'\0'};
   struct stat st;
   int uri_len, exists = 0, is_directory = 0;
+  struct uri_handler *uh;
 
-  if ((conn->request_info.query_string = strchr(ri->uri, '?')) != NULL) {
-    * ((char *) conn->request_info.query_string++) = '\0';
+  if ((conn->mg_conn.query_string = strchr(conn->mg_conn.uri, '?')) != NULL) {
+    * ((char *) conn->mg_conn.query_string++) = '\0';
   }
-  uri_len = (int) strlen(ri->uri);
-  mg_url_decode(ri->uri, uri_len, (char *) ri->uri, uri_len + 1, 0);
-  remove_double_dots_and_double_slashes((char *) ri->uri);
+  uri_len = (int) strlen(conn->mg_conn.uri);
+  mg_url_decode(conn->mg_conn.uri, uri_len, (char *) conn->mg_conn.uri,
+                uri_len + 1, 0);
+  remove_double_dots_and_double_slashes((char *) conn->mg_conn.uri);
 
+#if 0
   if (call_user(MG_REQUEST_BEGIN, conn, NULL) != 0) {
     conn->flags |= CONN_SPOOL_DONE;
     return;
   }
+#endif
+
+  if ((uh = find_uri_handler(conn->server, conn->mg_conn.uri)) != NULL) {
+    conn->endpoint_type = EP_USER;
+  }
 
   exists = convert_uri_to_file_name(conn, path, sizeof(path), &st);
   is_directory = S_ISDIR(st.st_mode);
 
   if (!exists) {
-    mg_printf(conn, "%s", "HTTP/1.0 404 Not Found\r\n\r\n");
+    mg_printf(&conn->mg_conn, "%s", "HTTP/1.0 404 Not Found\r\n\r\n");
     conn->flags |= CONN_SPOOL_DONE;
-  } else if (is_directory && ri->uri[uri_len - 1] != '/') {
-    mg_printf(conn, "HTTP/1.1 301 Moved Permanently\r\n"
-              "Location: %s/\r\n\r\n", ri->uri);
+  } else if (is_directory && conn->mg_conn.uri[uri_len - 1] != '/') {
+    mg_printf(&conn->mg_conn, "HTTP/1.1 301 Moved Permanently\r\n"
+              "Location: %s/\r\n\r\n", conn->mg_conn.uri);
     conn->flags |= CONN_SPOOL_DONE;
   } else if (is_directory) {
-    mg_printf(conn, "%s", "HTTP/1.0 403 Directory Listing Denied\r\n\r\n");
+    mg_printf(&conn->mg_conn, "%s",
+              "HTTP/1.0 403 Directory Listing Denied\r\n\r\n");
     conn->flags |= CONN_SPOOL_DONE;
   } else if ((conn->endpoint.fd = open(path, O_RDONLY)) != -1) {
     open_file_endpoint(conn, path, &st);
   } else {
-    mg_printf(conn, "%s", "HTTP/1.0 404 Not Found\r\n\r\n");
+    mg_printf(&conn->mg_conn, "%s", "HTTP/1.0 404 Not Found\r\n\r\n");
     conn->flags |= CONN_SPOOL_DONE;
   }
 }
@@ -1092,13 +1119,12 @@ static int io_space(const struct iobuf *io) {
   return io->size - io->len;
 }
 
-static void process_request(struct mg_connection *conn) {
+static void process_request(struct connection *conn) {
   struct iobuf *io = &conn->local_iobuf;
 
   //DBG(("parse_http_message(%d [%.*s])", io->len, io->len, io->buf));
   if (conn->request_len == 0) {
-    conn->request_len = parse_http_message(io->buf, io->len,
-                                           &conn->request_info);
+    conn->request_len = parse_http_message(io->buf, io->len, &conn->mg_conn);
   }
   DBG(("parse_http_message() -> %d", conn->request_len));
 
@@ -1111,7 +1137,7 @@ static void process_request(struct mg_connection *conn) {
   }
 }
 
-static void read_from_client(struct mg_connection *conn) {
+static void read_from_client(struct connection *conn) {
   struct iobuf *io = &conn->local_iobuf;
   int n = recv(conn->client_sock, io->buf + io->len, io->size - io->len, 0);
 
@@ -1127,21 +1153,19 @@ static void read_from_client(struct mg_connection *conn) {
   }
 }
 
-static int should_keep_alive(const struct mg_connection *conn) {
-  const char *method = conn->request_info.request_method;
-  const char *http_version = conn->request_info.http_version;
-  const char *header = mg_get_header(conn, "Connection");
+static int should_keep_alive(const struct connection *conn) {
+  const char *method = conn->mg_conn.request_method;
+  const char *http_version = conn->mg_conn.http_version;
+  const char *header = mg_get_header(&conn->mg_conn, "Connection");
   return method != NULL && !strcmp(method, "GET") &&
     ((header != NULL && !strcmp(header, "keep-alive")) ||
      (header == NULL && http_version && !strcmp(http_version, "1.1")));
 }
 
-static void close_local_endpoint(struct mg_connection *conn) {
+static void close_local_endpoint(struct connection *conn) {
   struct iobuf *io = &conn->local_iobuf;
   int keep_alive = should_keep_alive(conn);  // Must be done before memmove
 
-  call_user(MG_REQUEST_END, conn, (void *) (long) conn->status_code);
-
   // Close file descriptor
   switch (conn->endpoint_type) {
     case EP_FILE: close(conn->endpoint.fd); break;
@@ -1162,7 +1186,7 @@ static void close_local_endpoint(struct mg_connection *conn) {
   }
 }
 
-static void transfer_file_data(struct mg_connection *conn) {
+static void transfer_file_data(struct connection *conn) {
   struct iobuf *io = &conn->remote_iobuf;
   int n, rem_space = io_space(io);
 
@@ -1183,22 +1207,24 @@ void add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) {
   }
 }
 
-void mg_poll_server(struct mg_server *server, unsigned int milliseconds) {
+void mg_poll_server(struct mg_server *server, int milliseconds) {
   struct linked_list_link *lp, *tmp;
-  struct mg_connection *conn;
+  struct connection *conn;
   struct timeval tv;
   fd_set read_set, write_set;
   sock_t max_fd = -1;
   time_t current_time = time(NULL), expire_time = current_time +
     atoi(server->config_options[IDLE_TIMEOUT_MS]) / 1000;
 
+  if (server->listening_sock == INVALID_SOCKET) return;
+
   FD_ZERO(&read_set);
   FD_ZERO(&write_set);
   add_to_set(server->listening_sock, &read_set, &max_fd);
   add_to_set(server->ctl[0], &read_set, &max_fd);
 
   LINKED_LIST_FOREACH(&server->active_connections, lp, tmp) {
-    conn = LINKED_LIST_ENTRY(lp, struct mg_connection, link);
+    conn = LINKED_LIST_ENTRY(lp, struct connection, link);
     add_to_set(conn->client_sock, &read_set, &max_fd);
     if (conn->endpoint_type == EP_FILE) {
       transfer_file_data(conn);
@@ -1230,7 +1256,7 @@ void mg_poll_server(struct mg_server *server, unsigned int milliseconds) {
 
     // Read/write from clients
     LINKED_LIST_FOREACH(&server->active_connections, lp, tmp) {
-      conn = LINKED_LIST_ENTRY(lp, struct mg_connection, link);
+      conn = LINKED_LIST_ENTRY(lp, struct connection, link);
       if (FD_ISSET(conn->client_sock, &read_set)) {
         conn->expire_time = expire_time;
         read_from_client(conn);
@@ -1244,7 +1270,7 @@ void mg_poll_server(struct mg_server *server, unsigned int milliseconds) {
 
   // Close expired connections and those that need to be closed
   LINKED_LIST_FOREACH(&server->active_connections, lp, tmp) {
-    conn = LINKED_LIST_ENTRY(lp, struct mg_connection, link);
+    conn = LINKED_LIST_ENTRY(lp, struct connection, link);
     if (conn->flags & CONN_CLOSE || current_time > conn->expire_time) {
       close_conn(conn);
     }
@@ -1253,27 +1279,85 @@ void mg_poll_server(struct mg_server *server, unsigned int milliseconds) {
 
 void mg_destroy_server(struct mg_server **server) {
   struct linked_list_link *lp, *tmp;
-  struct mg_connection *conn;
 
   if (server != NULL && *server != NULL) {
     closesocket((*server)->listening_sock);
     closesocket((*server)->ctl[0]);
     closesocket((*server)->ctl[1]);
     LINKED_LIST_FOREACH(&(*server)->active_connections, lp, tmp) {
-      conn = LINKED_LIST_ENTRY(lp, struct mg_connection, link);
-      LINKED_LIST_REMOVE(&conn->link);
-      free(conn);
+      free(LINKED_LIST_ENTRY(lp, struct connection, link));
+    }
+    LINKED_LIST_FOREACH(&(*server)->uri_handlers, lp, tmp) {
+      free(LINKED_LIST_ENTRY(lp, struct uri_handler, link));
     }
     free(*server);
     *server = NULL;
   }
 }
 
-struct mg_server *mg_create_server(const char *opts[], mg_event_handler_t func,
-                                   void *user_data) {
+// Apply function to all active connections. Return number of active
+// connections. Function could be NULL.
+static int mg_iterate_over_connections(struct mg_server *server,
+                                void (*func)(struct mg_connection *, void *),
+                                void *param) {
+  struct linked_list_link *lp, *tmp;
+  struct connection *conn;
+  int num_connections = 0;
+
+  LINKED_LIST_FOREACH(&server->active_connections, lp, tmp) {
+    num_connections++;
+    conn = LINKED_LIST_ENTRY(lp, struct connection, link);
+    if (conn->endpoint_type == EP_USER && func != NULL) {
+      func((struct mg_connection *) conn, param);
+    }
+  }
+
+  return num_connections;
+}
+
+void mg_add_uri_handler(struct mg_server *server, const char *uri,
+                        mg_uri_handler_t handler) {
+  struct uri_handler *p = (struct uri_handler *) malloc(sizeof(*p));
+  if (p != NULL) {
+    LINKED_LIST_ADD_TO_FRONT(&server->uri_handlers, &p->link);
+    p->uri = mg_strdup(uri);
+    p->handler = handler;
+  }
+}
+
+const char *mg_set_option(struct mg_server *server, const char *name,
+                          const char *value) {
+  int ind = get_option_index(name);
+  const char *error_msg = NULL;
+
+  if (ind < 0) {
+    error_msg = "No such option";
+  } else {
+    if (server->config_options[ind] != NULL) {
+      free(server->config_options[ind]);
+    }
+    server->config_options[ind] = mg_strdup(value);
+
+    if (ind == LISTENING_PORT) {
+      if (server->listening_sock != INVALID_SOCKET) {
+        closesocket(server->listening_sock);
+      }
+      parse_port_string(server->config_options[LISTENING_PORT], &server->lsa);
+      server->listening_sock = open_listening_socket(&server->lsa);
+      if (server->listening_sock == INVALID_SOCKET) {
+        error_msg = "Cannot bind to port";
+      } else {
+        set_non_blocking_mode(server->listening_sock);
+      }
+    }
+  }
+
+  return error_msg;
+}
+
+struct mg_server *mg_create_server(void *server_data) {
   struct mg_server *server = (struct mg_server *) calloc(1, sizeof(*server));
-  const char *name, *value;
-  char error_msg[100] = {'\0'};
+  const char *value;
   int i;
 
 #ifdef _WIN32
@@ -1281,11 +1365,13 @@ struct mg_server *mg_create_server(const char *opts[], mg_event_handler_t func,
   WSAStartup(MAKEWORD(2, 2), &data);
 #endif
 
-  server->event_handler = func;
-  server->user_data = user_data;
   LINKED_LIST_INIT(&server->active_connections);
+  LINKED_LIST_INIT(&server->uri_handlers);
   mg_socketpair(server->ctl);
+  server->server_data = server_data;
+  server->listening_sock = INVALID_SOCKET;
 
+#if 0
   while (opts != NULL && (name = *opts++) != NULL) {
     if ((i = get_option_index(name)) == -1) {
       snprintf(error_msg, sizeof(error_msg), "Invalid option: [%s]", name);
@@ -1299,43 +1385,56 @@ struct mg_server *mg_create_server(const char *opts[], mg_event_handler_t func,
       DBG(("[%s] -> [%s]", name, value));
     }
   }
+#endif
 
-  // Set default values
+  // Set default options values
   for (i = 0; static_config_options[i * 2] != NULL; i++) {
     value = static_config_options[i * 2 + 1];
     if (server->config_options[i] == NULL && value != NULL) {
       server->config_options[i] = mg_strdup(value);
     }
   }
-  parse_port_string(server->config_options[LISTENING_PORT], &server->lsa);
-  server->listening_sock = open_listening_socket(&server->lsa);
-  set_non_blocking_mode(server->listening_sock);
-
-  if (error_msg[0] != '\0') {
-    mg_destroy_server(&server);
-  }
 
   return server;
 }
 
 // End of library, start of the application code
 
-static int event_handler(struct mg_event *ev) {
-  if (ev->type == MG_REQUEST_BEGIN &&
-      !strcmp(ev->request_info->uri, "/x")) {
-    mg_printf(ev->conn, "%s", "HTTP/1.0 200 OK\r\n\r\n:-)\n");
-    return 1;
+static void iterate_callback(struct mg_connection *c, void *param) {
+  mg_write(c, "%d", * (int *) param);
+}
+
+static void *timer_thread(void *param) {
+  struct mg_server *server = (struct mg_server *) param;
+  int i;
+
+  for (i = 0; i < 1000; i++) {
+    sleep(1);
+    mg_iterate_over_connections(server, iterate_callback, &i);
   }
-  return 0;
+
+  return NULL;
+}
+
+static int websocket_handler(struct mg_connection *conn) {
+  mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n\r\n:-)\n");
+  return 1;
 }
 
+
 int main(void) {
-  const char *options[] = {"document_root", ".", "listening_port", "8080", 0};
-  struct mg_server *server = mg_create_server(options, event_handler, NULL);
+  struct mg_server *server = mg_create_server(NULL);
+
+  mg_set_option(server, "listening_port", "8080");
+  mg_set_option(server, "document_root", ".");
+  mg_add_uri_handler(server, "/ws", websocket_handler);
+  mg_start_thread(timer_thread, server);
 
+  printf("Started on port %s\n", mg_get_option(server, "listening_port"));
   for (;;) {
     mg_poll_server(server, 1000);
   }
+
   mg_destroy_server(&server);
   return 0;
 }
diff --git a/build/src/core.h b/build/src/core.h
new file mode 100644
index 000000000..621bd30c0
--- /dev/null
+++ b/build/src/core.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
+// Copyright (c) 2013 Cesanta Software Limited
+// All rights reserved
+//
+// This library is dual-licensed: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation. For the terms of this
+// license, see <http://www.gnu.org/licenses/>.
+//
+// You are free to use this library under the terms of the GNU General
+// Public License, but WITHOUT ANY WARRANTY; without even the implied
+// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// Alternatively, you can license this library under a commercial
+// license, as set out in <http://cesanta.com/products.html>.
+//
+// NOTE: Detailed API documentation is at http://cesanta.com/docs.html
+
+#ifndef MONGOOSE_HEADER_INCLUDED
+#define  MONGOOSE_HEADER_INCLUDED
+
+#include <stdio.h>      // required for FILE
+#include <stddef.h>     // required for size_t
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+// This structure contains information about HTTP request.
+struct mg_connection {
+  const char *request_method; // "GET", "POST", etc
+  const char *uri;            // URL-decoded URI
+  const char *http_version;   // E.g. "1.0", "1.1"
+  const char *query_string;   // URL part after '?', not including '?', or NULL
+  long remote_ip;             // Client's IP address
+  int remote_port;            // Client's port
+  int is_ssl;                 // 1 if SSL-ed, 0 if not
+  int num_headers;            // Number of HTTP headers
+  struct mg_header {
+    const char *name;         // HTTP header name
+    const char *value;        // HTTP header value
+  } http_headers[64];         // Maximum 64 headers
+
+  void *server_param;         // Parameter passed to mg_add_uri_handler()
+  void *connection_param;     // Placeholder for connection-specific data
+};
+
+struct mg_server; // Opaque structure describing server instance
+typedef int (*mg_uri_handler_t)(struct mg_connection *);
+typedef int (*mg_error_handler_t)(struct mg_connection *, int http_error_code);
+
+// Server management functions
+struct mg_server *mg_create_server(void *server_param);
+void mg_destroy_server(struct mg_server **);
+const char *mg_set_option(struct mg_server *, const char *opt, const char *val);
+void mg_poll_server(struct mg_server *, int milliseconds);
+void mg_add_uri_handler(struct mg_server *, const char *uri, mg_uri_handler_t);
+void mg_set_error_handler(struct mg_server *, mg_error_handler_t);
+void mg_set_log_handler(struct mg_server*, int (*)(struct mg_connection*, int));
+const char **mg_get_valid_option_names(void);
+const char *mg_get_option(const struct mg_server *server, const char *name);
+
+// Websocket functions
+void mg_websocket_handshake(struct mg_connection *);
+int mg_websocket_read(struct mg_connection *, int *bits, char **data);
+int mg_websocket_write(struct mg_connection* conn, int opcode,
+                       const char *data, size_t data_len);
+
+// Connection management functions
+int mg_write(struct mg_connection *, const void *buf, int len);
+int mg_printf(struct mg_connection *, const char *fmt, ...);
+void mg_send_file(struct mg_connection *, const char *path);
+int mg_read(struct mg_connection *, void *buf, int len);
+const char *mg_get_header(const struct mg_connection *, const char *name);
+int mg_get_var(const char *data, size_t data_len,
+               const char *var_name, char *dst, size_t dst_len);
+int mg_get_cookie(const char *cookie, const char *var_name,
+                  char *buf, size_t buf_len);
+const char *mg_get_mime_type(const char *file_name);
+
+// Utility functions
+int mg_start_thread(void *(*func)(void *), void *param);
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif // MONGOOSE_HEADER_INCLUDED
-- 
GitLab