From 14526a21091b6e6d022d2de4b639e8f02c556b8b Mon Sep 17 00:00:00 2001
From: Sergey Lyubka <valenok@gmail.com>
Date: Tue, 28 Jan 2014 12:42:13 +0000
Subject: [PATCH] mg_add_uri_handler -> mg_set_request_handler()

---
 docs/API.md               | 41 +++++++++++------
 examples/Makefile         |  2 +-
 examples/auth.c           |  2 +-
 examples/hello.c          |  4 +-
 examples/multi_threaded.c |  6 +--
 examples/post.c           |  4 +-
 examples/upload.c         |  4 +-
 examples/websocket.c      | 11 +++--
 examples/websocket.html   | 11 +++--
 mongoose.c                | 97 +++++++++++++++------------------------
 mongoose.h                | 13 +++---
 unit_test.c               | 43 +++++++++--------
 12 files changed, 117 insertions(+), 121 deletions(-)

diff --git a/docs/API.md b/docs/API.md
index c79473974..3abad5314 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -60,12 +60,26 @@ that take `struct mg_server *` parameter. Mongoose does not
 mutex-protect `struct mg_server *`, therefore the best practice is
 to call server management functions from the same thread (an IO thread).
 
-    void mg_add_uri_handler(struct mg_server *, const char *uri, mg_handler_t);
-
-Adds an URI handler. If Mongoose gets a request and request's URI starts
-with `uri`, then specified handler is called to serve the request. Thus, an
-`uri` is a match prefix. For example, if `uri` is "/", then all requests will
-be routed to the handler, because all URIs start with `/` character.
+    void mg_set_auth_handler(struct mg_server *, mg_handler_t handler);
+
+Sets authorization handler. Called by Mongoose on each request, before
+performing any other action. Handler can return either `MG_AUTH_OK` or
+`MG_AUTH_FAIL`. If `handler` returns `MG_AUTH_FAIL`, then Mongoose sends
+digest authorization request to the client. If `handler returns `MG_AUTH_OK`,
+then mongoose proceeds with handling the request. Handler function can use
+`mg_authorize_digest()` function to verify authorization, or implement any other
+custom authorization mechanism.
+
+    void mg_set_request_handler(struct mg_server *, mg_handler_t handler);
+
+Sets a request handler. When set, `handler` will be called for each request.
+Possible return values from a handler function are `MG_REQUEST_NOT_PROCESSED`,
+`MG_REQUEST_PROCESSED` and `MG_REQUEST_CALL_AGAIN`. If handler returns
+`MG_REQUEST_NOT_PROCESSED`, mongoose will proceed with handling the request.
+If handler returns `MG_REQUEST_PROCESSED`, that signals mongoose that handler
+has already processed the connection, and mongoose will skip to the next
+request. `MG_REQUEST_CALL_AGAIN` tells mongoose to call request handler
+again and again on each `mg_poll_server()` iteration.
 
 When mongoose buffers in HTTP request and successfully parses it, it calls
 appropriate URI handler immediately for GET requests. For POST requests,
@@ -82,19 +96,16 @@ whether the request is websocket or not. Also, for websocket requests,
 there is `struct mg_connection::wsbits` field which contains first byte
 of the websocket frame which URI handler can examine. Note that to
 reply to the websocket client, `mg_websocket_write()` should be used.
-To reply to the plain HTTP client, `mg_write_data()` should be used.
-
-An URI handler must return a value.  If URI handler has sent all data,
-it should return `1`.  If URI handler returns `0`, that signals Mongoose
-that URI handler hasn't finished sending data to the client. In this case,
-Mongoose will call URI handler after each successful socket write.
-`struct mg_connection::wsbits` flag will indicate the status of the write,
-`1` means that write has failed and connection will be closed.
+To reply to the plain HTTP client, `mg_write_data()` should be used. Note that
+websocket handler must return either `MG_CLIENT_CLOSE` or `MG_CLIENT_CONTINUE`
+value.
 
     void mg_set_http_error_handler(struct mg_server *, mg_handler_t);
 
 Adds HTTP error handler. An actual HTTP error is passed as
-`struct mg_connection::status_code` parameter. If handler returns 0, it
+`struct mg_connection::status_code` parameter.
+Hanlder should return either `MG_ERROR_PROCESSED` or `MG_ERROR_NOT_PROCESSED`.
+If handler returns `MG_ERROR_NOT_PROCESSED`, it
 means a handler has not processed the connection, and mongoose proceeds
 with sending HTTP error to the client. Otherwise, mongoose does nothing.
 
diff --git a/examples/Makefile b/examples/Makefile
index a4bcc1f59..54f75ac9d 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -1,4 +1,4 @@
-CFLAGS  = -W -Wall -I.. -pthread -g -pipe $(COPT)
+CFLAGS  = -W -Wall -I.. -pthread -g -pipe $(CFLAGS_EXTRA)
 DLL_FLAGS = -DLUA_COMPAT_ALL -I../build
 RM = rm -rf
 
diff --git a/examples/auth.c b/examples/auth.c
index a5ffca420..21d13c642 100644
--- a/examples/auth.c
+++ b/examples/auth.c
@@ -3,7 +3,7 @@
 #include "mongoose.h"
 
 static int auth_handler(struct mg_connection *conn) {
-  int result = 0; // Not authorized
+  int result = MG_AUTH_FAIL; // Not authorized
   FILE *fp;
 
   // To populate passwords file, do
diff --git a/examples/hello.c b/examples/hello.c
index 3481af5b7..4ed9f4356 100644
--- a/examples/hello.c
+++ b/examples/hello.c
@@ -5,7 +5,7 @@
 // This function will be called by mongoose on every new request
 static int index_html(struct mg_connection *conn) {
   mg_printf_data(conn, "Hello! Requested URI is [%s]", conn->uri);
-  return 1;
+  return MG_REQUEST_PROCESSED;
 }
 
 int main(void) {
@@ -14,7 +14,7 @@ int main(void) {
   // Create and configure the server
   server = mg_create_server(NULL);
   mg_set_option(server, "listening_port", "8080");
-  mg_add_uri_handler(server, "/", index_html);
+  mg_set_request_handler(server, index_html);
 
   // Serve request. Hit Ctrl-C to terminate the program
   printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
diff --git a/examples/multi_threaded.c b/examples/multi_threaded.c
index 509694d3b..134d88fab 100644
--- a/examples/multi_threaded.c
+++ b/examples/multi_threaded.c
@@ -6,7 +6,7 @@ static int request_handler(struct mg_connection *conn) {
   mg_send_header(conn, "Content-Type", "text/plain");
   mg_printf_data(conn, "This is a reply from server instance # %s",
                  (char *) conn->server_param);
-  return 0;
+  return MG_REQUEST_PROCESSED;
 }
 
 static void *serve(void *server) {
@@ -20,8 +20,8 @@ int main(void) {
   server1 = mg_create_server((void *) "1");
   server2 = mg_create_server((void *) "2");
 
-  mg_add_uri_handler(server1, "/", request_handler);
-  mg_add_uri_handler(server2, "/", request_handler);
+  mg_set_request_handler(server1, request_handler);
+  mg_set_request_handler(server2, request_handler);
 
   // Make both server1 and server2 listen on the same socket
   mg_set_option(server1, "listening_port", "8080");
diff --git a/examples/post.c b/examples/post.c
index 127160a40..b958b3858 100644
--- a/examples/post.c
+++ b/examples/post.c
@@ -34,13 +34,13 @@ static int handler(struct mg_connection *conn) {
     mg_send_data(conn, html_form, strlen(html_form));
   }
 
-  return 1;
+  return MG_REQUEST_PROCESSED;
 }
 
 int main(void) {
   struct mg_server *server = mg_create_server(NULL);
   mg_set_option(server, "listening_port", "8080");
-  mg_add_uri_handler(server, "/", handler);
+  mg_set_request_handler(server, handler);
   printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
   for (;;) {
     mg_poll_server(server, 1000);
diff --git a/examples/upload.c b/examples/upload.c
index b83693952..468ccc45b 100644
--- a/examples/upload.c
+++ b/examples/upload.c
@@ -30,7 +30,7 @@ static int index_html(struct mg_connection *conn) {
 
   mg_printf_data(conn, "%s", "</body></html>");
 
-  return 1;
+  return MG_REQUEST_PROCESSED;
 }
 
 int main(void) {
@@ -39,7 +39,7 @@ int main(void) {
   // Create and configure the server
   server = mg_create_server(NULL);
   mg_set_option(server, "listening_port", "8080");
-  mg_add_uri_handler(server, "/", index_html);
+  mg_set_request_handler(server, index_html);
 
   // Serve request. Hit Ctrl-C to terminate the program
   printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
diff --git a/examples/websocket.c b/examples/websocket.c
index 50039f00f..5dbe711bd 100644
--- a/examples/websocket.c
+++ b/examples/websocket.c
@@ -9,7 +9,7 @@ static int iterate_callback(struct mg_connection *c) {
     int len = snprintf(buf, sizeof(buf), "%d", * (int *) c->connection_param);
     mg_websocket_write(c, 1, buf, len);
   }
-  return 1;
+  return MG_REQUEST_PROCESSED;
 }
 
 static int index_html(struct mg_connection *conn) {
@@ -21,11 +21,12 @@ static int index_html(struct mg_connection *conn) {
     // times for connection lifetime.
     // Echo websocket data back to the client.
     mg_websocket_write(conn, 1, conn->content, conn->content_len);
-    return conn->content_len == 4 && !memcmp(conn->content, "exit", 4);
+    return conn->content_len == 4 && !memcmp(conn->content, "exit", 4) ?
+      MG_CLIENT_CLOSE : MG_CLIENT_CONTINUE;
   } else {
     mg_send_header(conn, "Content-Type", "text/html");
     mg_send_data(conn, index_html, index_size);
-    return 1;
+    return MG_REQUEST_PROCESSED;
   }
 }
 
@@ -34,12 +35,12 @@ int main(void) {
   unsigned int current_timer = 0, last_timer = 0;
 
   mg_set_option(server, "listening_port", "8080");
-  mg_add_uri_handler(server, "/", index_html);
+  mg_set_request_handler(server, index_html);
 
   printf("Started on port %s\n", mg_get_option(server, "listening_port"));
   for (;;) {
     current_timer = mg_poll_server(server, 100);
-    if (current_timer - last_timer > 1) {
+    if (current_timer - last_timer > 0) {
       last_timer = current_timer;
       mg_iterate_over_connections(server, iterate_callback, &current_timer);
     }
diff --git a/examples/websocket.html b/examples/websocket.html
index aed62c169..7f5b7cc46 100644
--- a/examples/websocket.html
+++ b/examples/websocket.html
@@ -24,10 +24,13 @@
       out('DISCONNECTED');
     };
     websocket.onmessage = function(ev) {
-      if (!ev.data) return; // No data, this is a PING message, ignore it
-      out('<span style="color: blue;">RESPONSE: ' + ev.data + ' </span>');
-      num_messages++;
-      if (num_messages > 100) {
+      if (!ev.data) {
+        out('<span style="color: blue;">PING... </span>');
+      } else {
+        out('<span style="color: blue;">RESPONSE: ' + ev.data + ' </span>');
+        num_messages++;
+      }
+      if (num_messages > 15) {
         websocket.send('exit');
       }
     };
diff --git a/mongoose.c b/mongoose.c
index 1a596f475..67363dd09 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -203,12 +203,6 @@ struct vec {
   int len;
 };
 
-struct uri_handler {
-  struct ll link;
-  char *uri;
-  mg_handler_t handler;
-};
-
 // For directory listing and WevDAV support
 struct dir_entry {
   struct connection *conn;
@@ -275,7 +269,7 @@ struct mg_server {
   sock_t listening_sock;
   union socket_address lsa;   // Listening socket address
   struct ll active_connections;
-  struct ll uri_handlers;
+  mg_handler_t request_handler;
   mg_handler_t error_handler;
   mg_handler_t auth_handler;
   char *config_options[NUM_OPTIONS];
@@ -300,7 +294,6 @@ union endpoint {
   int fd;                   // Opened regular local file
   sock_t cgi_sock;          // CGI socket
   void *ssl;                // SSL descriptor
-  struct uri_handler *uh;   // URI handler user function
 };
 
 enum endpoint_type { EP_NONE, EP_FILE, EP_CGI, EP_USER, EP_PUT, EP_CLIENT };
@@ -338,6 +331,7 @@ struct connection {
 #endif
 };
 
+static void open_local_endpoint(struct connection *conn, int skip_user);
 static void close_local_endpoint(struct connection *conn);
 
 static const struct {
@@ -664,7 +658,7 @@ static void send_http_error(struct connection *conn, int code,
 
   // Invoke error handler if it is set
   if (conn->server->error_handler != NULL &&
-      conn->server->error_handler(&conn->mg_conn)) {
+      conn->server->error_handler(&conn->mg_conn) == MG_ERROR_PROCESSED) {
     close_local_endpoint(conn);
     return;
   }
@@ -1861,7 +1855,7 @@ static int deliver_websocket_frame(struct connection *conn) {
     }
 
     // Call the handler and remove frame from the iobuf
-    if (conn->endpoint.uh->handler(&conn->mg_conn)) {
+    if (conn->server->request_handler(&conn->mg_conn) == MG_CLIENT_CLOSE) {
       conn->flags |= CONN_SPOOL_DONE;
     }
     discard_leading_iobuf_bytes(&conn->local_iobuf, frame_len);
@@ -1934,16 +1928,20 @@ static void write_terminating_chunk(struct connection *conn) {
   mg_write(&conn->mg_conn, "0\r\n\r\n", 5);
 }
 
-static void call_uri_handler(struct connection *conn) {
+static int call_request_handler(struct connection *conn) {
+  int result;
   conn->mg_conn.content = conn->local_iobuf.buf;
-  if (conn->endpoint.uh->handler(&conn->mg_conn)) {
-    if (conn->flags & CONN_HEADERS_SENT) {
-      write_terminating_chunk(conn);
-    }
-    close_local_endpoint(conn);
-  } else {
-    conn->flags |= CONN_LONG_RUNNING;
+  switch ((result = conn->server->request_handler(&conn->mg_conn))) {
+    case MG_REQUEST_CALL_AGAIN: conn->flags |= CONN_LONG_RUNNING; break;
+    case MG_REQUEST_NOT_PROCESSED: break;
+    default:
+      if (conn->flags & CONN_HEADERS_SENT) {
+        write_terminating_chunk(conn);
+      }
+      close_local_endpoint(conn);
+      break;
   }
+  return result;
 }
 
 static void callback_http_client_on_connect(struct connection *conn) {
@@ -2021,19 +2019,6 @@ const char *mg_get_mime_type(const char *path, const char *default_mime_type) {
   return default_mime_type;
 }
 
-static struct uri_handler *find_uri_handler(struct mg_server *server,
-                                            const char *uri) {
-  struct ll *lp, *tmp;
-  struct uri_handler *uh;
-
-  LINKED_LIST_FOREACH(&server->uri_handlers, lp, tmp) {
-    uh = LINKED_LIST_ENTRY(lp, struct uri_handler, link);
-    if (!strncmp(uh->uri, uri, strlen(uh->uri))) return uh;
-  }
-
-  return NULL;
-}
-
 #ifndef MONGOOSE_NO_FILESYSTEM
 // Convert month to the month number. Return -1 on error, or month number
 static int get_month_index(const char *s) {
@@ -2247,7 +2232,7 @@ static void open_file_endpoint(struct connection *conn, const char *path,
 
 #endif  // MONGOOSE_NO_FILESYSTEM
 
-static void call_uri_handler_if_data_is_buffered(struct connection *conn) {
+static void call_request_handler_if_data_is_buffered(struct connection *conn) {
   struct iobuf *loc = &conn->local_iobuf;
   struct mg_connection *c = &conn->mg_conn;
 
@@ -2256,8 +2241,9 @@ static void call_uri_handler_if_data_is_buffered(struct connection *conn) {
     do { } while (deliver_websocket_frame(conn));
   } else
 #endif
-  if ((size_t) loc->len >= c->content_len) {
-    call_uri_handler(conn);
+  if ((size_t) loc->len >= c->content_len &&
+      call_request_handler(conn) == MG_REQUEST_NOT_PROCESSED) {
+    open_local_endpoint(conn, 1);
   }
 }
 
@@ -2984,7 +2970,8 @@ static int check_password(const char *method, const char *ha1, const char *uri,
   mg_md5(expected_response, ha1, ":", nonce, ":", nc,
       ":", cnonce, ":", qop, ":", ha2, NULL);
 
-  return mg_strcasecmp(response, expected_response) == 0;
+  return mg_strcasecmp(response, expected_response) == 0 ?
+    MG_AUTH_OK : MG_AUTH_FAIL;
 }
 
 
@@ -3014,14 +3001,14 @@ int mg_authorize_digest(struct mg_connection *c, FILE *fp) {
       return check_password(c->request_method, ha1, uri,
                             nonce, nc, cnonce, qop, resp);
   }
-  return 0;
+  return MG_AUTH_FAIL;
 }
 
 
 // Return 1 if request is authorised, 0 otherwise.
 static int is_authorized(struct connection *conn, const char *path) {
   FILE *fp;
-  int authorized = 1;
+  int authorized = MG_AUTH_OK;
 
   if ((fp = open_auth_file(conn, path)) != NULL) {
     authorized = mg_authorize_digest(&conn->mg_conn, fp);
@@ -3034,7 +3021,7 @@ static int is_authorized(struct connection *conn, const char *path) {
 static int is_authorized_for_dav(struct connection *conn) {
   const char *auth_file = conn->server->config_options[DAV_AUTH_FILE];
   FILE *fp;
-  int authorized = 0;
+  int authorized = MG_AUTH_FAIL;
 
   if (auth_file != NULL && (fp = fopen(auth_file, "r")) != NULL) {
     authorized = mg_authorize_digest(&conn->mg_conn, fp);
@@ -3359,7 +3346,7 @@ static void handle_lsp_request(struct connection *conn, const char *path,
 }
 #endif // MONGOOSE_USE_LUA
 
-static void open_local_endpoint(struct connection *conn) {
+static void open_local_endpoint(struct connection *conn, int skip_user) {
 #ifndef MONGOOSE_NO_FILESYSTEM
   static const char lua_pat[] = LUA_SCRIPT_PATTERN;
   file_stat_t st;
@@ -3372,17 +3359,15 @@ static void open_local_endpoint(struct connection *conn) {
 #ifndef MONGOOSE_NO_AUTH
   // Call auth handler
   if (conn->server->auth_handler != NULL &&
-      conn->server->auth_handler(&conn->mg_conn) == 0) {
+      conn->server->auth_handler(&conn->mg_conn) == MG_AUTH_FAIL) {
     mg_send_digest_auth_request(&conn->mg_conn);
     return;
   }
 #endif
 
   // Call URI handler if one is registered for this URI
-  conn->endpoint.uh = find_uri_handler(conn->server, conn->mg_conn.uri);
-  if (conn->endpoint.uh != NULL) {
+  if (skip_user == 0 && conn->server->request_handler != NULL) {
     conn->endpoint_type = EP_USER;
-    conn->mg_conn.content = conn->local_iobuf.buf;
 #if MONGOOSE_USE_POST_SIZE_LIMIT > 1
     {
       const char *cl = mg_get_header(&conn->mg_conn, "Content-Length");
@@ -3525,7 +3510,7 @@ static void process_request(struct connection *conn) {
     send_websocket_handshake_if_requested(&conn->mg_conn);
 #endif
     send_continue_if_expected(conn);
-    open_local_endpoint(conn);
+    open_local_endpoint(conn, 0);
   }
 
 #ifndef MONGOOSE_NO_CGI
@@ -3534,7 +3519,7 @@ static void process_request(struct connection *conn) {
   }
 #endif
   if (conn->endpoint_type == EP_USER) {
-    call_uri_handler_if_data_is_buffered(conn);
+    call_request_handler_if_data_is_buffered(conn);
   }
 #ifndef MONGOOSE_NO_DAV
   if (conn->endpoint_type == EP_PUT && io->len > 0) {
@@ -3859,7 +3844,9 @@ unsigned int mg_poll_server(struct mg_server *server, int milliseconds) {
     }
     if (conn->flags & CONN_LONG_RUNNING) {
       conn->mg_conn.wsbits = conn->flags & CONN_CLOSE ? 1 : 0;
-      call_uri_handler(conn);
+      if (call_request_handler(conn) == MG_REQUEST_PROCESSED) {
+        conn->flags |= CONN_CLOSE;
+      }
     }
     if (conn->flags & CONN_CLOSE || conn->last_activity_time < expire_time) {
       close_conn(conn);
@@ -3883,10 +3870,6 @@ void mg_destroy_server(struct mg_server **server) {
     LINKED_LIST_FOREACH(&s->active_connections, lp, tmp) {
       close_conn(LINKED_LIST_ENTRY(lp, struct connection, link));
     }
-    LINKED_LIST_FOREACH(&s->uri_handlers, lp, tmp) {
-      free(LINKED_LIST_ENTRY(lp, struct uri_handler, link)->uri);
-      free(LINKED_LIST_ENTRY(lp, struct uri_handler, link));
-    }
     for (i = 0; i < (int) ARRAY_SIZE(s->config_options); i++) {
       free(s->config_options[i]);  // It is OK to free(NULL)
     }
@@ -3909,16 +3892,6 @@ void mg_iterate_over_connections(struct mg_server *server, mg_handler_t handler,
   send(server->ctl[0], (void *) msg, sizeof(msg), 0);
 }
 
-void mg_add_uri_handler(struct mg_server *server, const char *uri,
-                        mg_handler_t handler) {
-  struct uri_handler *p = (struct uri_handler *) malloc(sizeof(*p));
-  if (p != NULL) {
-    LINKED_LIST_ADD_TO_TAIL(&server->uri_handlers, &p->link);
-    p->uri = mg_strdup(uri);
-    p->handler = handler;
-  }
-}
-
 static int get_var(const char *data, size_t data_len, const char *name,
                    char *dst, size_t dst_len) {
   const char *p, *e, *s;
@@ -4145,6 +4118,9 @@ const char *mg_set_option(struct mg_server *server, const char *name,
   return error_msg;
 }
 
+void mg_set_request_handler(struct mg_server *server, mg_handler_t handler) {
+  server->request_handler = handler;
+}
 
 void mg_set_http_error_handler(struct mg_server *server, mg_handler_t handler) {
   server->error_handler = handler;
@@ -4184,7 +4160,6 @@ struct mg_server *mg_create_server(void *server_data) {
 #endif
 
   LINKED_LIST_INIT(&server->active_connections);
-  LINKED_LIST_INIT(&server->uri_handlers);
 
   // Create control socket pair. Do it in a loop to protect from
   // interrupted syscalls in mg_socketpair().
diff --git a/mongoose.h b/mongoose.h
index 9ba5afe9f..5822b8153 100644
--- a/mongoose.h
+++ b/mongoose.h
@@ -65,7 +65,7 @@ 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);
 unsigned int mg_poll_server(struct mg_server *, int milliseconds);
-void mg_add_uri_handler(struct mg_server *, const char *uri, mg_handler_t);
+void mg_set_request_handler(struct mg_server *, mg_handler_t);
 void mg_set_http_error_handler(struct mg_server *, mg_handler_t);
 void mg_set_auth_handler(struct mg_server *, mg_handler_t);
 const char **mg_get_valid_option_names(void);
@@ -87,9 +87,8 @@ int mg_websocket_write(struct mg_connection *, int opcode,
 int mg_write(struct mg_connection *, const void *buf, int len);
 int mg_printf(struct mg_connection *conn, const char *fmt, ...);
 
-
 const char *mg_get_header(const struct mg_connection *, const char *name);
-const char *mg_get_mime_type(const char *file_name, const char *default_mime_type);
+const char *mg_get_mime_type(const char *name, const char *default_mime_type);
 int mg_get_var(const struct mg_connection *conn, const char *var_name,
                char *buf, size_t buf_len);
 int mg_parse_header(const char *hdr, const char *var_name, char *buf, size_t);
@@ -102,10 +101,12 @@ int mg_parse_multipart(const char *buf, int buf_len,
 void *mg_start_thread(void *(*func)(void *), void *param);
 char *mg_md5(char buf[33], ...);
 int mg_authorize_digest(struct mg_connection *c, FILE *fp);
-void mg_send_digest_auth_request(struct mg_connection *conn);
 
-// Callback return codes
-enum { MG_REPLY_TO_BE_CONTINUED, MG_REPLY_COMPLETED };
+// Callback function return codes
+enum { MG_REQUEST_NOT_PROCESSED, MG_REQUEST_PROCESSED, MG_REQUEST_CALL_AGAIN };
+enum { MG_AUTH_FAIL, MG_AUTH_OK };
+enum { MG_ERROR_NOT_PROCESSED, MG_ERROR_PROCESSED };
+enum { MG_CLIENT_CONTINUE, MG_CLIENT_CLOSE };
 
 // HTTP client events
 enum {
diff --git a/unit_test.c b/unit_test.c
index 03b7b43f2..b9cf34a88 100644
--- a/unit_test.c
+++ b/unit_test.c
@@ -364,39 +364,41 @@ static const char *test_next_option(void) {
 }
 
 static int cb1(struct mg_connection *conn) {
-  // We're not sending HTTP headers here, to make testing easier
-  //mg_printf(conn, "HTTP/1.0 200 OK\r\n\r\n%s %s %s",
-  mg_printf(conn, "%s %s %s",
-           conn->server_param == NULL ? "?" : (char *) conn->server_param,
-           conn->connection_param == NULL ? "?" : "!", conn->remote_ip);
-  return 1;
+  int result = MG_REQUEST_NOT_PROCESSED;
+  if (!strcmp(conn->uri, "/cb1")) {
+    mg_printf(conn, "%s %s %s",
+              conn->server_param == NULL ? "?" : (char *) conn->server_param,
+              conn->connection_param == NULL ? "?" : "!", conn->remote_ip);
+    result = MG_REQUEST_PROCESSED;
+  }
+  return result;
 }
 
 static int error_handler(struct mg_connection *conn) {
   mg_printf(conn, "HTTP/1.0 404 NF\r\n\r\nERR: %d", conn->status_code);
-  return 1;
+  return MG_ERROR_PROCESSED;
 }
 
 static int ts1(struct mg_connection *conn) {
   if (conn->status_code == MG_CONNECT_SUCCESS) {
     mg_printf(conn, "%s", "GET /cb1 HTTP/1.0\r\n\r\n");
-    return 0;
+    return MG_CLIENT_CONTINUE;
   } else if (conn->status_code == MG_DOWNLOAD_SUCCESS) {
     sprintf((char *) conn->connection_param, "%.*s",
             (int) conn->content_len, conn->content);
   }
-  return 1;
+  return MG_CLIENT_CLOSE;
 }
 
 static int ts2(struct mg_connection *conn) {
   if (conn->status_code == MG_CONNECT_SUCCESS) {
     mg_printf(conn, "%s", "GET /non_exist HTTP/1.0\r\n\r\n");
-    return 0;
+    return MG_CLIENT_CONTINUE;
   } else if (conn->status_code == MG_DOWNLOAD_SUCCESS) {
     sprintf((char *) conn->connection_param, "%s %.*s",
             conn->uri, (int) conn->content_len, conn->content);
   }
-  return 1;
+  return MG_CLIENT_CLOSE;
 }
 
 static const char *test_server(void) {
@@ -406,7 +408,7 @@ static const char *test_server(void) {
   ASSERT(server != NULL);
   ASSERT(mg_set_option(server, "listening_port", LISTENING_ADDR) == NULL);
   ASSERT(mg_set_option(server, "document_root", ".") == NULL);
-  mg_add_uri_handler(server, "/cb1", cb1);
+  mg_set_request_handler(server, cb1);
   mg_set_http_error_handler(server, error_handler);
 
   ASSERT(mg_connect(server, "127.0.0.1", atoi(HTTP_PORT),  0, ts1, buf1) == 1);
@@ -460,25 +462,28 @@ static int cb2(struct mg_connection *conn) {
 }
 
 static int cb4h(struct mg_connection *conn) {
-  mg_send_data(conn, ":-)", 3);
-  return MG_REPLY_COMPLETED;
+  int result = MG_REQUEST_NOT_PROCESSED;
+  if (!strcmp(conn->uri, "/x")) {
+    mg_send_data(conn, ":-)", 3);
+    result = MG_REQUEST_PROCESSED;
+  }
+  return result;
 }
 
 static int cb4(struct mg_connection *conn) {
   if (conn->status_code == MG_CONNECT_SUCCESS) {
     mg_printf(conn, "%s", "POST /x HTTP/1.0\r\nContent-Length: 999999\r\n\r\n");
-    return MG_REPLY_TO_BE_CONTINUED;
+    return MG_CLIENT_CONTINUE;
   } else {
     sprintf((char *) conn->connection_param, "%.*s",
             (int) conn->content_len, conn->content);
   }
-  return MG_REPLY_COMPLETED;
+  return MG_CLIENT_CLOSE;
 }
 
 static int cb3(struct mg_connection *conn) {
-  fflush(stdout);
   sprintf((char *) conn->connection_param, "%d", conn->status_code);
-  return 1;
+  return MG_CLIENT_CLOSE;
 }
 
 static const char *test_mg_connect(void) {
@@ -487,7 +492,7 @@ static const char *test_mg_connect(void) {
 
   ASSERT(mg_set_option(server, "listening_port", LISTENING_ADDR) == NULL);
   ASSERT(mg_set_option(server, "document_root", ".") == NULL);
-  mg_add_uri_handler(server, "/x", cb4h);
+  mg_set_request_handler(server, cb4h);
   ASSERT(mg_connect(server, "", 0, 0, NULL, NULL) == 0);
   ASSERT(mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0, cb2, buf2) == 1);
   ASSERT(mg_connect(server, "127.0.0.1", 29, 0, cb3, buf3) == 1);
-- 
GitLab