diff --git a/docs/c-api/http.h/mg_connect_ws.md b/docs/c-api/http.h/mg_connect_ws.md
index b0f38d74895f3a5c6222457a6189a10ac345c160..7a5b517d34f0064642b5abe094d5f526906a0f63 100644
--- a/docs/c-api/http.h/mg_connect_ws.md
+++ b/docs/c-api/http.h/mg_connect_ws.md
@@ -4,9 +4,8 @@ decl_name: "mg_connect_ws"
 symbol_kind: "func"
 signature: |
   struct mg_connection *mg_connect_ws(struct mg_mgr *mgr,
-                                      mg_event_handler_t event_handler,
-                                      const char *url, const char *protocol,
-                                      const char *extra_headers);
+                                      MG_CB(mg_event_handler_t event_handler,
+                                            void *user_data);
 ---
 
 Helper function that creates an outbound WebSocket connection.
diff --git a/docs/c-api/http.h/mg_connect_ws_opt.md b/docs/c-api/http.h/mg_connect_ws_opt.md
index 57956ddbd30fbb22ef7582589574f9b40240ce8a..e2203cff69636a5e9c837b87c74f719eb1df899c 100644
--- a/docs/c-api/http.h/mg_connect_ws_opt.md
+++ b/docs/c-api/http.h/mg_connect_ws_opt.md
@@ -3,11 +3,8 @@ title: "mg_connect_ws_opt()"
 decl_name: "mg_connect_ws_opt"
 symbol_kind: "func"
 signature: |
-  struct mg_connection *mg_connect_ws_opt(struct mg_mgr *mgr,
-                                          mg_event_handler_t ev_handler,
-                                          struct mg_connect_opts opts,
-                                          const char *url, const char *protocol,
-                                          const char *extra_headers);
+  struct mg_connection *mg_connect_ws_opt(
+      struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data);
 ---
 
 Helper function that creates an outbound WebSocket connection
diff --git a/docs/c-api/http_client.h/mg_connect_http.md b/docs/c-api/http_client.h/mg_connect_http.md
index fcf5f04687585192eade1234648ae79dd0217480..4664fcd9040d75cf8c167b3135e6c87417c68660 100644
--- a/docs/c-api/http_client.h/mg_connect_http.md
+++ b/docs/c-api/http_client.h/mg_connect_http.md
@@ -3,11 +3,9 @@ title: "mg_connect_http()"
 decl_name: "mg_connect_http"
 symbol_kind: "func"
 signature: |
-  struct mg_connection *mg_connect_http(struct mg_mgr *mgr,
-                                        mg_event_handler_t event_handler,
-                                        const char *url,
-                                        const char *extra_headers,
-                                        const char *post_data);
+  struct mg_connection *mg_connect_http(
+      struct mg_mgr *mgr,
+      MG_CB(mg_event_handler_t event_handler, void *user_data);
 ---
 
 Helper function that creates an outbound HTTP connection.
diff --git a/docs/c-api/http_client.h/mg_connect_http_opt.md b/docs/c-api/http_client.h/mg_connect_http_opt.md
index 2454cb10ee06712997b4a9e6c088d12ff7b5e3f4..29df1d753db7e0ac5e9565a7266b2444379c8d34 100644
--- a/docs/c-api/http_client.h/mg_connect_http_opt.md
+++ b/docs/c-api/http_client.h/mg_connect_http_opt.md
@@ -3,12 +3,8 @@ title: "mg_connect_http_opt()"
 decl_name: "mg_connect_http_opt"
 symbol_kind: "func"
 signature: |
-  struct mg_connection *mg_connect_http_opt(struct mg_mgr *mgr,
-                                            mg_event_handler_t ev_handler,
-                                            struct mg_connect_opts opts,
-                                            const char *url,
-                                            const char *extra_headers,
-                                            const char *post_data);
+  struct mg_connection *mg_connect_http_opt(
+      struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data);
 ---
 
 Helper function that creates an outbound HTTP connection.
diff --git a/docs/c-api/http_server.h/mg_file_upload_handler.md b/docs/c-api/http_server.h/mg_file_upload_handler.md
index 444c106ff2b4a6713099ff455e961575da006f6b..a2c9e016f6ea34beb257c9dacfb271cfb813bb4d 100644
--- a/docs/c-api/http_server.h/mg_file_upload_handler.md
+++ b/docs/c-api/http_server.h/mg_file_upload_handler.md
@@ -4,7 +4,8 @@ decl_name: "mg_file_upload_handler"
 symbol_kind: "func"
 signature: |
   void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data,
-                              mg_fu_fname_fn local_name_fn);
+                              mg_fu_fname_fn local_name_fn
+                                  MG_UD_ARG(void *user_data);
 ---
 
 File upload handler.
diff --git a/docs/c-api/net.h/mg_add_sock.md b/docs/c-api/net.h/mg_add_sock.md
index 511ae84100b786819ba93153a00e67e9b8de98ef..e63ce2e48212c987cca7ae576bd97ca769b42c58 100644
--- a/docs/c-api/net.h/mg_add_sock.md
+++ b/docs/c-api/net.h/mg_add_sock.md
@@ -3,7 +3,9 @@ title: "mg_add_sock()"
 decl_name: "mg_add_sock"
 symbol_kind: "func"
 signature: |
-  struct mg_connection *mg_add_sock(struct mg_mgr *, sock_t, mg_event_handler_t);
+  struct mg_connection *mg_add_sock(struct mg_mgr *mgr, sock_t sock,
+                                    MG_CB(mg_event_handler_t handler,
+                                          void *user_data);
 ---
 
 Creates a connection, associates it with the given socket and event handler
diff --git a/docs/c-api/net.h/mg_add_sock_opt.md b/docs/c-api/net.h/mg_add_sock_opt.md
index 678a91cbb8fdfa55a295f88b11d84f6ef4ce6ac5..de2a22e9ffd766b45206cf28f707a63d9b17160c 100644
--- a/docs/c-api/net.h/mg_add_sock_opt.md
+++ b/docs/c-api/net.h/mg_add_sock_opt.md
@@ -3,9 +3,9 @@ title: "mg_add_sock_opt()"
 decl_name: "mg_add_sock_opt"
 symbol_kind: "func"
 signature: |
-  struct mg_connection *mg_add_sock_opt(struct mg_mgr *, sock_t,
-                                        mg_event_handler_t,
-                                        struct mg_add_sock_opts);
+  struct mg_connection *mg_add_sock_opt(struct mg_mgr *mgr, sock_t sock,
+                                        MG_CB(mg_event_handler_t handler,
+                                              void *user_data);
 ---
 
 Creates a connection, associates it with the given socket and event handler
diff --git a/docs/c-api/net.h/mg_bind.md b/docs/c-api/net.h/mg_bind.md
index 800bba9547f2bb7d7341a64d9fd455bbeacb6f5b..9507d51c585e828db6fee531ac9c9cf0ed326be5 100644
--- a/docs/c-api/net.h/mg_bind.md
+++ b/docs/c-api/net.h/mg_bind.md
@@ -3,8 +3,9 @@ title: "mg_bind()"
 decl_name: "mg_bind"
 symbol_kind: "func"
 signature: |
-  struct mg_connection *mg_bind(struct mg_mgr *, const char *,
-                                mg_event_handler_t);
+  struct mg_connection *mg_bind(struct mg_mgr *mgr, const char *address,
+                                MG_CB(mg_event_handler_t handler,
+                                      void *user_data);
 ---
 
 Creates a listening connection.
diff --git a/docs/c-api/net.h/mg_bind_opt.md b/docs/c-api/net.h/mg_bind_opt.md
index 5fc46054071f6a5b46ed6aec73cc701854241843..823dd0c0aef50fb18bcc8ab48673c1d376e3f2ba 100644
--- a/docs/c-api/net.h/mg_bind_opt.md
+++ b/docs/c-api/net.h/mg_bind_opt.md
@@ -4,8 +4,8 @@ decl_name: "mg_bind_opt"
 symbol_kind: "func"
 signature: |
   struct mg_connection *mg_bind_opt(struct mg_mgr *mgr, const char *address,
-                                    mg_event_handler_t handler,
-                                    struct mg_bind_opts opts);
+                                    MG_CB(mg_event_handler_t handler,
+                                          void *user_data);
 ---
 
 Creates a listening connection.
diff --git a/docs/c-api/net.h/mg_broadcast.md b/docs/c-api/net.h/mg_broadcast.md
index 4eda7f3cd833e4049017d0524e99dee84444a361..cef0b379ceacac570d2167b475d1a5d81957af7c 100644
--- a/docs/c-api/net.h/mg_broadcast.md
+++ b/docs/c-api/net.h/mg_broadcast.md
@@ -3,7 +3,8 @@ title: "mg_broadcast()"
 decl_name: "mg_broadcast"
 symbol_kind: "func"
 signature: |
-  void mg_broadcast(struct mg_mgr *, mg_event_handler_t func, void *, size_t);
+  void mg_broadcast(struct mg_mgr *mgr, mg_event_handler_t cb, void *data,
+                    size_t len);
 ---
 
 Passes a message of a given length to all connections.
diff --git a/docs/c-api/net.h/mg_connect.md b/docs/c-api/net.h/mg_connect.md
index bbacd8f4c9faac91271151c4a9a0bfbf859733f0..788be84e663c6f86bea11ea248d7fa46098aeed4 100644
--- a/docs/c-api/net.h/mg_connect.md
+++ b/docs/c-api/net.h/mg_connect.md
@@ -4,7 +4,8 @@ decl_name: "mg_connect"
 symbol_kind: "func"
 signature: |
   struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *address,
-                                   mg_event_handler_t handler);
+                                   MG_CB(mg_event_handler_t handler,
+                                         void *user_data);
 ---
 
 Connects to a remote host.
diff --git a/docs/c-api/net.h/mg_connect_opt.md b/docs/c-api/net.h/mg_connect_opt.md
index de7e71392fdc279bea3001376481b2faf2863a30..3ae818a4436731c7d48eabc38f3a4c136cd5870e 100644
--- a/docs/c-api/net.h/mg_connect_opt.md
+++ b/docs/c-api/net.h/mg_connect_opt.md
@@ -4,8 +4,8 @@ decl_name: "mg_connect_opt"
 symbol_kind: "func"
 signature: |
   struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address,
-                                       mg_event_handler_t handler,
-                                       struct mg_connect_opts opts);
+                                       MG_CB(mg_event_handler_t handler,
+                                             void *user_data);
 ---
 
 Connects to a remote host.
diff --git a/docs/c-api/net.h/mg_event_handler_t.md b/docs/c-api/net.h/mg_event_handler_t.md
index 191805c8f74dba67a81ae4881d8b05b2fea0ca02..5a4822246d2475a84ac186e4554681b26025a82d 100644
--- a/docs/c-api/net.h/mg_event_handler_t.md
+++ b/docs/c-api/net.h/mg_event_handler_t.md
@@ -4,7 +4,7 @@ decl_name: "mg_event_handler_t"
 symbol_kind: "typedef"
 signature: |
   typedef void (*mg_event_handler_t)(struct mg_connection *nc, int ev,
-                                     void *ev_data);
+                                     void *ev_data MG_UD_ARG(void *user_data));
 ---
 
 Callback function (event handler) prototype. Must be defined by the user.
diff --git a/docs/c-api/net.h/mg_next.md b/docs/c-api/net.h/mg_next.md
index fcd5e1d17b92348206810fcbaba11c444dbdaa92..423af6fdbc591de9b59c4806ecaf56055bdb06dd 100644
--- a/docs/c-api/net.h/mg_next.md
+++ b/docs/c-api/net.h/mg_next.md
@@ -3,7 +3,7 @@ title: "mg_next()"
 decl_name: "mg_next"
 symbol_kind: "func"
 signature: |
-  struct mg_connection *mg_next(struct mg_mgr *, struct mg_connection *);
+  struct mg_connection *mg_next(struct mg_mgr *mgr, struct mg_connection *c);
 ---
 
 Iterates over all active connections.
diff --git a/mongoose.c b/mongoose.c
index 926fd574d11f7376019e699f08542012c65dbd8d..a694cd51070abe0b1a34799217fe6cb19f12031e 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -155,7 +155,8 @@ MG_INTERNAL void mg_handle_put(struct mg_connection *nc, const char *path,
                                struct http_message *hm);
 #endif
 #if MG_ENABLE_HTTP_WEBSOCKET
-MG_INTERNAL void mg_ws_handler(struct mg_connection *nc, int ev, void *ev_data);
+MG_INTERNAL void mg_ws_handler(struct mg_connection *nc, int ev,
+                               void *ev_data MG_UD_ARG(void *user_data));
 MG_INTERNAL void mg_ws_handshake(struct mg_connection *nc,
                                  const struct mg_str *key);
 #endif
@@ -1934,7 +1935,8 @@ extern "C" {
 
 struct mg_connection *mg_tun_bind_opt(struct mg_mgr *mgr,
                                       const char *dispatcher,
-                                      mg_event_handler_t handler,
+                                      MG_CB(mg_event_handler_t handler,
+                                            void *user_data),
                                       struct mg_bind_opts opts);
 
 int mg_tun_parse_frame(void *data, size_t len, struct mg_tun_frame *frame);
@@ -2049,7 +2051,7 @@ MG_INTERNAL void mg_call(struct mg_connection *nc,
   if (ev_handler != NULL) {
     unsigned long flags_before = nc->flags;
     size_t recv_mbuf_before = nc->recv_mbuf.len, recved;
-    ev_handler(nc, ev, ev_data);
+    ev_handler(nc, ev, ev_data MG_UD_ARG(nc->user_data));
     recved = (recv_mbuf_before - nc->recv_mbuf.len);
     /* Prevent user handler from fiddling with system flags. */
     if (ev_handler == nc->handler && nc->flags != flags_before) {
@@ -2643,14 +2645,16 @@ static void resolve_cb(struct mg_dns_message *msg, void *data,
 #endif
 
 struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *address,
-                                 mg_event_handler_t callback) {
+                                 MG_CB(mg_event_handler_t callback,
+                                       void *user_data)) {
   struct mg_connect_opts opts;
   memset(&opts, 0, sizeof(opts));
-  return mg_connect_opt(mgr, address, callback, opts);
+  return mg_connect_opt(mgr, address, MG_CB(callback, user_data), opts);
 }
 
 struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address,
-                                     mg_event_handler_t callback,
+                                     MG_CB(mg_event_handler_t callback,
+                                           void *user_data),
                                      struct mg_connect_opts opts) {
   struct mg_connection *nc = NULL;
   int proto, rc;
@@ -2673,7 +2677,11 @@ struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address,
 
   nc->flags |= opts.flags & _MG_ALLOWED_CONNECT_FLAGS_MASK;
   nc->flags |= (proto == SOCK_DGRAM) ? MG_F_UDP : 0;
+#if MG_ENABLE_CALLBACK_USERDATA
+  nc->user_data = user_data;
+#else
   nc->user_data = opts.user_data;
+#endif
 
 #if MG_ENABLE_SSL
   DBG(("%p %s %s,%s,%s", nc, address, (opts.ssl_cert ? opts.ssl_cert : "-"),
@@ -2745,14 +2753,16 @@ struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address,
 }
 
 struct mg_connection *mg_bind(struct mg_mgr *srv, const char *address,
-                              mg_event_handler_t event_handler) {
+                              MG_CB(mg_event_handler_t event_handler,
+                                    void *user_data)) {
   struct mg_bind_opts opts;
   memset(&opts, 0, sizeof(opts));
-  return mg_bind_opt(srv, address, event_handler, opts);
+  return mg_bind_opt(srv, address, MG_CB(event_handler, user_data), opts);
 }
 
 struct mg_connection *mg_bind_opt(struct mg_mgr *mgr, const char *address,
-                                  mg_event_handler_t callback,
+                                  MG_CB(mg_event_handler_t callback,
+                                        void *user_data),
                                   struct mg_bind_opts opts) {
   union socket_address sa;
   struct mg_connection *nc = NULL;
@@ -2765,7 +2775,7 @@ struct mg_connection *mg_bind_opt(struct mg_mgr *mgr, const char *address,
 #if MG_ENABLE_TUN
   if (mg_strncmp(mg_mk_str(address), mg_mk_str("ws://"), 5) == 0 ||
       mg_strncmp(mg_mk_str(address), mg_mk_str("wss://"), 6) == 0) {
-    return mg_tun_bind_opt(mgr, address, callback, opts);
+    return mg_tun_bind_opt(mgr, address, MG_CB(callback, user_data), opts);
   }
 #endif
 
@@ -2823,6 +2833,9 @@ struct mg_connection *mg_bind_opt(struct mg_mgr *mgr, const char *address,
   }
   mg_add_conn(nc->mgr, nc);
 
+#if MG_ENABLE_CALLBACK_USERDATA
+  (void) user_data;
+#endif
   return nc;
 }
 
@@ -2934,8 +2947,13 @@ void mg_if_get_conn_addr(struct mg_connection *nc, int remote,
 }
 
 struct mg_connection *mg_add_sock_opt(struct mg_mgr *s, sock_t sock,
-                                      mg_event_handler_t callback,
+                                      MG_CB(mg_event_handler_t callback,
+                                            void *user_data),
                                       struct mg_add_sock_opts opts) {
+#if MG_ENABLE_CALLBACK_USERDATA
+  opts.user_data = user_data;
+#endif
+
   struct mg_connection *nc = mg_create_connection_base(s, callback, opts);
   if (nc != NULL) {
     mg_sock_set(nc, sock);
@@ -2945,10 +2963,11 @@ struct mg_connection *mg_add_sock_opt(struct mg_mgr *s, sock_t sock,
 }
 
 struct mg_connection *mg_add_sock(struct mg_mgr *s, sock_t sock,
-                                  mg_event_handler_t callback) {
+                                  MG_CB(mg_event_handler_t callback,
+                                        void *user_data)) {
   struct mg_add_sock_opts opts;
   memset(&opts, 0, sizeof(opts));
-  return mg_add_sock_opt(s, sock, callback, opts);
+  return mg_add_sock_opt(s, sock, MG_CB(callback, user_data), opts);
 }
 
 double mg_time(void) {
@@ -3525,7 +3544,8 @@ static void mg_mgr_handle_ctl_sock(struct mg_mgr *mgr) {
   if (len >= (int) sizeof(ctl_msg.callback) && ctl_msg.callback != NULL) {
     struct mg_connection *nc;
     for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) {
-      ctl_msg.callback(nc, MG_EV_POLL, ctl_msg.message);
+      ctl_msg.callback(nc, MG_EV_POLL,
+                       ctl_msg.message MG_UD_ARG(nc->user_data));
     }
   }
 }
@@ -5046,7 +5066,7 @@ struct mg_http_proto_data {
 
 static void mg_http_conn_destructor(void *proto_data);
 struct mg_connection *mg_connect_http_base(
-    struct mg_mgr *mgr, mg_event_handler_t ev_handler,
+    struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
     struct mg_connect_opts opts, const char *schema, const char *schema_ssl,
     const char *url, const char **path, char **user, char **pass, char **addr);
 
@@ -5561,18 +5581,22 @@ static void mg_http_multipart_begin(struct mg_connection *nc,
  * even bigger (round up to 4k, from 700 bytes of actual size).
  */
 #ifdef __xtensa__
-static void mg_http_handler2(struct mg_connection *nc, int ev, void *ev_data,
+static void mg_http_handler2(struct mg_connection *nc, int ev,
+                             void *ev_data MG_UD_ARG(void *user_data),
                              struct http_message *hm) __attribute__((noinline));
 
-void mg_http_handler(struct mg_connection *nc, int ev, void *ev_data) {
+void mg_http_handler(struct mg_connection *nc, int ev,
+                     void *ev_data MG_UD_ARG(void *user_data)) {
   struct http_message hm;
-  mg_http_handler2(nc, ev, ev_data, &hm);
+  mg_http_handler2(nc, ev, ev_data MG_UD_ARG(user_data), &hm);
 }
 
-static void mg_http_handler2(struct mg_connection *nc, int ev, void *ev_data,
+static void mg_http_handler2(struct mg_connection *nc, int ev,
+                             void *ev_data MG_UD_ARG(void *user_data),
                              struct http_message *hm) {
 #else  /* !__XTENSA__ */
-void mg_http_handler(struct mg_connection *nc, int ev, void *ev_data) {
+void mg_http_handler(struct mg_connection *nc, int ev,
+                     void *ev_data MG_UD_ARG(void *user_data)) {
   struct http_message shm;
   struct http_message *hm = &shm;
 #endif /* __XTENSA__ */
@@ -5674,7 +5698,7 @@ void mg_http_handler(struct mg_connection *nc, int ev, void *ev_data) {
       nc->proto_handler = mg_ws_handler;
       nc->flags |= MG_F_IS_WEBSOCKET;
       mg_call(nc, nc->handler, MG_EV_WEBSOCKET_HANDSHAKE_DONE, NULL);
-      mg_ws_handler(nc, MG_EV_RECV, ev_data);
+      mg_ws_handler(nc, MG_EV_RECV, ev_data MG_UD_ARG(user_data));
     } else if (nc->listener != NULL &&
                (vec = mg_get_http_header(hm, "Sec-WebSocket-Key")) != NULL) {
       mg_event_handler_t handler;
@@ -5701,7 +5725,7 @@ void mg_http_handler(struct mg_connection *nc, int ev, void *ev_data) {
           mg_ws_handshake(nc, vec);
         }
         mg_call(nc, nc->handler, MG_EV_WEBSOCKET_HANDSHAKE_DONE, NULL);
-        mg_ws_handler(nc, MG_EV_RECV, ev_data);
+        mg_ws_handler(nc, MG_EV_RECV, ev_data MG_UD_ARG(user_data));
       }
     }
 #endif /* MG_ENABLE_HTTP_WEBSOCKET */
@@ -6950,7 +6974,7 @@ static int mg_http_send_port_based_redirect(
 }
 
 static void mg_reverse_proxy_handler(struct mg_connection *nc, int ev,
-                                     void *ev_data) {
+                                     void *ev_data MG_UD_ARG(void *user_data)) {
   struct http_message *hm = (struct http_message *) ev_data;
   struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
 
@@ -6976,6 +7000,10 @@ static void mg_reverse_proxy_handler(struct mg_connection *nc, int ev,
       pd->reverse_proxy_data.linked_conn->flags |= MG_F_SEND_AND_CLOSE;
       break;
   }
+
+#if MG_ENABLE_CALLBACK_USERDATA
+  (void) user_data;
+#endif
 }
 
 void mg_http_reverse_proxy(struct mg_connection *nc,
@@ -6994,9 +7022,9 @@ void mg_http_reverse_proxy(struct mg_connection *nc,
   mg_asprintf(&purl, sizeof(burl), "%.*s%.*s", (int) upstream.len, upstream.p,
               (int) (hm->uri.len - mount.len), hm->uri.p + mount.len);
 
-  be = mg_connect_http_base(nc->mgr, mg_reverse_proxy_handler, opts, "http://",
-                            "https://", purl, &path, NULL /* user */,
-                            NULL /* pass */, &addr);
+  be = mg_connect_http_base(nc->mgr, MG_CB(mg_reverse_proxy_handler, NULL),
+                            opts, "http://", "https://", purl, &path,
+                            NULL /* user */, NULL /* pass */, &addr);
   LOG(LL_DEBUG, ("Proxying %.*s to %s (rule: %.*s)", (int) hm->uri.len,
                  hm->uri.p, purl, (int) mount.len, mount.p));
 
@@ -7474,7 +7502,8 @@ void mg_serve_http(struct mg_connection *nc, struct http_message *hm,
 
 #if MG_ENABLE_HTTP_STREAMING_MULTIPART
 void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data,
-                            mg_fu_fname_fn local_name_fn) {
+                            mg_fu_fname_fn local_name_fn
+                                MG_UD_ARG(void *user_data)) {
   switch (ev) {
     case MG_EV_HTTP_PART_BEGIN: {
       struct mg_http_multipart_part *mp =
@@ -7586,6 +7615,10 @@ void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data,
       break;
     }
   }
+
+#if MG_ENABLE_CALLBACK_USERDATA
+  (void) user_data;
+#endif
 }
 
 #endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */
@@ -7674,7 +7707,7 @@ cleanup:
 }
 
 struct mg_connection *mg_connect_http_base(
-    struct mg_mgr *mgr, mg_event_handler_t ev_handler,
+    struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
     struct mg_connect_opts opts, const char *schema, const char *schema_ssl,
     const char *url, const char **path, char **user, char **pass, char **addr) {
   struct mg_connection *nc = NULL;
@@ -7707,7 +7740,8 @@ struct mg_connection *mg_connect_http_base(
 #endif
   }
 
-  if ((nc = mg_connect_opt(mgr, *addr, ev_handler, opts)) != NULL) {
+  if ((nc = mg_connect_opt(mgr, *addr, MG_CB(ev_handler, user_data), opts)) !=
+      NULL) {
     mg_set_protocol_http_websocket(nc);
     /* If the port was addred by us, restore the original host. */
     if (port_i >= 0) (*addr)[port_i] = '\0';
@@ -7716,18 +7750,16 @@ struct mg_connection *mg_connect_http_base(
   return nc;
 }
 
-struct mg_connection *mg_connect_http_opt(struct mg_mgr *mgr,
-                                          mg_event_handler_t ev_handler,
-                                          struct mg_connect_opts opts,
-                                          const char *url,
-                                          const char *extra_headers,
-                                          const char *post_data) {
+struct mg_connection *mg_connect_http_opt(
+    struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
+    struct mg_connect_opts opts, const char *url, const char *extra_headers,
+    const char *post_data) {
   char *user = NULL, *pass = NULL, *addr = NULL;
   const char *path = NULL;
   struct mbuf auth;
   struct mg_connection *nc =
-      mg_connect_http_base(mgr, ev_handler, opts, "http://", "https://", url,
-                           &path, &user, &pass, &addr);
+      mg_connect_http_base(mgr, MG_CB(ev_handler, user_data), opts, "http://",
+                           "https://", url, &path, &user, &pass, &addr);
 
   if (nc == NULL) {
     return NULL;
@@ -7754,15 +7786,13 @@ struct mg_connection *mg_connect_http_opt(struct mg_mgr *mgr,
   return nc;
 }
 
-struct mg_connection *mg_connect_http(struct mg_mgr *mgr,
-                                      mg_event_handler_t ev_handler,
-                                      const char *url,
-                                      const char *extra_headers,
-                                      const char *post_data) {
+struct mg_connection *mg_connect_http(
+    struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
+    const char *url, const char *extra_headers, const char *post_data) {
   struct mg_connect_opts opts;
   memset(&opts, 0, sizeof(opts));
-  return mg_connect_http_opt(mgr, ev_handler, opts, url, extra_headers,
-                             post_data);
+  return mg_connect_http_opt(mgr, MG_CB(ev_handler, user_data), opts, url,
+                             extra_headers, post_data);
 }
 
 size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name,
@@ -8196,8 +8226,11 @@ static void mg_prepare_cgi_environment(struct mg_connection *nc,
 }
 
 static void mg_cgi_ev_handler(struct mg_connection *cgi_nc, int ev,
-                              void *ev_data) {
-  struct mg_connection *nc = (struct mg_connection *) cgi_nc->user_data;
+                              void *ev_data MG_UD_ARG(void *user_data)) {
+#if !MG_ENABLE_CALLBACK_USERDATA
+  void *user_data = cgi_nc->user_data;
+#endif
+  struct mg_connection *nc = (struct mg_connection *) user_data;
   (void) ev_data;
 
   if (nc == NULL) {
@@ -8289,10 +8322,12 @@ MG_INTERNAL void mg_handle_cgi(struct mg_connection *nc, const char *prog,
                        fds[1]) != 0) {
     size_t n = nc->recv_mbuf.len - (hm->message.len - hm->body.len);
     struct mg_connection *cgi_nc =
-        mg_add_sock(nc->mgr, fds[0], mg_cgi_ev_handler);
+        mg_add_sock(nc->mgr, fds[0], mg_cgi_ev_handler MG_UD_ARG(nc));
     struct mg_http_proto_data *cgi_pd = mg_http_get_proto_data(nc);
     cgi_pd->cgi.cgi_nc = cgi_nc;
+#if !MG_ENABLE_CALLBACK_USERDATA
     cgi_pd->cgi.cgi_nc->user_data = nc;
+#endif
     nc->flags |= MG_F_USER_1;
     /* Push POST data to the CGI */
     if (n > 0 && n < nc->recv_mbuf.len) {
@@ -9035,7 +9070,7 @@ void mg_printf_websocket_frame(struct mg_connection *nc, int op,
 }
 
 MG_INTERNAL void mg_ws_handler(struct mg_connection *nc, int ev,
-                               void *ev_data) {
+                               void *ev_data MG_UD_ARG(void *user_data)) {
   mg_call(nc, nc->handler, ev, ev_data);
 
   switch (ev) {
@@ -9056,6 +9091,9 @@ MG_INTERNAL void mg_ws_handler(struct mg_connection *nc, int ev,
     default:
       break;
   }
+#if MG_ENABLE_CALLBACK_USERDATA
+  (void) user_data;
+#endif
 }
 
 #ifndef MG_EXT_SHA1
@@ -9154,16 +9192,15 @@ void mg_send_websocket_handshake(struct mg_connection *nc, const char *path,
                                extra_headers);
 }
 
-struct mg_connection *mg_connect_ws_opt(struct mg_mgr *mgr,
-                                        mg_event_handler_t ev_handler,
-                                        struct mg_connect_opts opts,
-                                        const char *url, const char *protocol,
-                                        const char *extra_headers) {
+struct mg_connection *mg_connect_ws_opt(
+    struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
+    struct mg_connect_opts opts, const char *url, const char *protocol,
+    const char *extra_headers) {
   char *user = NULL, *pass = NULL, *addr = NULL;
   const char *path = NULL;
   struct mg_connection *nc =
-      mg_connect_http_base(mgr, ev_handler, opts, "ws://", "wss://", url, &path,
-                           &user, &pass, &addr);
+      mg_connect_http_base(mgr, MG_CB(ev_handler, user_data), opts, "ws://",
+                           "wss://", url, &path, &user, &pass, &addr);
 
   if (nc != NULL) {
     mg_send_websocket_handshake3(nc, path, addr, protocol, extra_headers, user,
@@ -9176,13 +9213,13 @@ struct mg_connection *mg_connect_ws_opt(struct mg_mgr *mgr,
   return nc;
 }
 
-struct mg_connection *mg_connect_ws(struct mg_mgr *mgr,
-                                    mg_event_handler_t ev_handler,
-                                    const char *url, const char *protocol,
-                                    const char *extra_headers) {
+struct mg_connection *mg_connect_ws(
+    struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
+    const char *url, const char *protocol, const char *extra_headers) {
   struct mg_connect_opts opts;
   memset(&opts, 0, sizeof(opts));
-  return mg_connect_ws_opt(mgr, ev_handler, opts, url, protocol, extra_headers);
+  return mg_connect_ws_opt(mgr, MG_CB(ev_handler, user_data), opts, url,
+                           protocol, extra_headers);
 }
 #endif /* MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBSOCKET */
 #ifdef MG_MODULE_LINES
@@ -9694,19 +9731,20 @@ MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) {
   return end - io->buf;
 }
 
-static void mqtt_handler(struct mg_connection *nc, int ev, void *ev_data) {
+static void mqtt_handler(struct mg_connection *nc, int ev,
+                         void *ev_data MG_UD_ARG(void *user_data)) {
   int len;
   struct mbuf *io = &nc->recv_mbuf;
   struct mg_mqtt_message mm;
   memset(&mm, 0, sizeof(mm));
 
-  nc->handler(nc, ev, ev_data);
+  nc->handler(nc, ev, ev_data MG_UD_ARG(user_data));
 
   switch (ev) {
     case MG_EV_RECV:
       len = parse_mqtt(io, &mm);
       if (len == -1) break; /* not fully buffered */
-      nc->handler(nc, MG_MQTT_EVENT_BASE + mm.cmd, &mm);
+      nc->handler(nc, MG_MQTT_EVENT_BASE + mm.cmd, &mm MG_UD_ARG(user_data));
       mbuf_remove(io, len);
       break;
   }
@@ -10479,12 +10517,13 @@ size_t mg_dns_uncompress_name(struct mg_dns_message *msg, struct mg_str *name,
   return dst - old_dst;
 }
 
-static void dns_handler(struct mg_connection *nc, int ev, void *ev_data) {
+static void dns_handler(struct mg_connection *nc, int ev,
+                        void *ev_data MG_UD_ARG(void *user_data)) {
   struct mbuf *io = &nc->recv_mbuf;
   struct mg_dns_message msg;
 
   /* Pass low-level events to the user handler */
-  nc->handler(nc, ev, ev_data);
+  nc->handler(nc, ev, ev_data MG_UD_ARG(user_data));
 
   switch (ev) {
     case MG_EV_RECV:
@@ -10503,7 +10542,7 @@ static void dns_handler(struct mg_connection *nc, int ev, void *ev_data) {
         mg_send(nc, io->buf, io->len);
       } else {
         /* Call user handler with parsed message */
-        nc->handler(nc, MG_DNS_MESSAGE, &msg);
+        nc->handler(nc, MG_DNS_MESSAGE, &msg MG_UD_ARG(user_data));
       }
       mbuf_remove(io, io->len);
       break;
@@ -10741,15 +10780,19 @@ int mg_resolve_from_hosts_file(const char *name, union socket_address *usa) {
   return -1;
 }
 
-static void mg_resolve_async_eh(struct mg_connection *nc, int ev, void *data) {
+static void mg_resolve_async_eh(struct mg_connection *nc, int ev,
+                                void *data MG_UD_ARG(void *user_data)) {
   time_t now = (time_t) mg_time();
   struct mg_resolve_async_request *req;
   struct mg_dns_message *msg;
   int first = 0;
+#if !MG_ENABLE_CALLBACK_USERDATA
+  void *user_data = nc->user_data;
+#endif
 
-  if (ev != MG_EV_POLL) DBG(("ev=%d user_data=%p", ev, nc->user_data));
+  if (ev != MG_EV_POLL) DBG(("ev=%d user_data=%p", ev, user_data));
 
-  req = (struct mg_resolve_async_request *) nc->user_data;
+  req = (struct mg_resolve_async_request *) user_data;
 
   if (req == NULL) {
     return;
@@ -10849,7 +10892,7 @@ int mg_resolve_async_opt(struct mg_mgr *mgr, const char *name, int query,
     nameserver = mg_dns_server;
   }
 
-  dns_nc = mg_connect(mgr, nameserver, mg_resolve_async_eh);
+  dns_nc = mg_connect(mgr, nameserver, MG_CB(mg_resolve_async_eh, NULL));
   if (dns_nc == NULL) {
     free(req);
     return -1;
@@ -11523,8 +11566,13 @@ static void mg_tun_close_all(struct mg_tun_client *client) {
 }
 
 static void mg_tun_client_handler(struct mg_connection *nc, int ev,
-                                  void *ev_data) {
-  struct mg_tun_client *client = (struct mg_tun_client *) nc->user_data;
+                                  void *ev_data MG_UD_ARG(void *user_data)) {
+#if !MG_ENABLE_CALLBACK_USERDATA
+  void *user_data = nc->user_data;
+#else
+  (void) nc;
+#endif
+  struct mg_tun_client *client = (struct mg_tun_client *) user_data;
 
   switch (ev) {
     case MG_EV_CONNECT: {
@@ -11611,21 +11659,28 @@ static void mg_tun_do_reconnect(struct mg_tun_client *client) {
   opts.ssl_ca_cert = client->ssl.ssl_ca_cert;
 #endif
   /* HTTP/Websocket listener */
-  if ((dc = mg_connect_ws_opt(client->mgr, mg_tun_client_handler, opts,
-                              client->disp_url, MG_TUN_PROTO_NAME, NULL)) ==
-      NULL) {
+  if ((dc = mg_connect_ws_opt(client->mgr, MG_CB(mg_tun_client_handler, client),
+                              opts, client->disp_url, MG_TUN_PROTO_NAME,
+                              NULL)) == NULL) {
     LOG(LL_ERROR,
         ("Cannot connect to WS server on addr [%s]\n", client->disp_url));
     return;
   }
 
   client->disp = dc;
+#if !MG_ENABLE_CALLBACK_USERDATA
   dc->user_data = client;
+#endif
 }
 
 void mg_tun_reconnect_ev_handler(struct mg_connection *nc, int ev,
-                                 void *ev_data) {
-  struct mg_tun_client *client = (struct mg_tun_client *) nc->user_data;
+                                 void *ev_data MG_UD_ARG(void *user_data)) {
+#if !MG_ENABLE_CALLBACK_USERDATA
+  void *user_data = nc->user_data;
+#else
+  (void) nc;
+#endif
+  struct mg_tun_client *client = (struct mg_tun_client *) user_data;
   (void) ev_data;
 
   switch (ev) {
@@ -11642,9 +11697,11 @@ void mg_tun_reconnect_ev_handler(struct mg_connection *nc, int ev,
 
 static void mg_tun_reconnect(struct mg_tun_client *client, int timeout) {
   if (client->reconnect == NULL) {
-    client->reconnect =
-        mg_add_sock(client->mgr, INVALID_SOCKET, mg_tun_reconnect_ev_handler);
+    client->reconnect = mg_add_sock(client->mgr, INVALID_SOCKET,
+                                    MG_CB(mg_tun_reconnect_ev_handler, client));
+#if !MG_ENABLE_CALLBACK_USERDATA
     client->reconnect->user_data = client;
+#endif
   }
   client->reconnect->ev_timer_time = mg_time() + timeout;
 }
@@ -11700,18 +11757,21 @@ void mg_tun_destroy_client(struct mg_tun_client *client) {
 }
 
 static struct mg_connection *mg_tun_do_bind(struct mg_tun_client *client,
-                                            mg_event_handler_t handler,
+                                            MG_CB(mg_event_handler_t handler,
+                                                  void *user_data),
                                             struct mg_bind_opts opts) {
   struct mg_connection *lc;
   opts.iface = client->iface;
-  lc = mg_bind_opt(client->mgr, ":1234" /* dummy port */, handler, opts);
+  lc = mg_bind_opt(client->mgr, ":1234" /* dummy port */,
+                   MG_CB(handler, user_data), opts);
   client->listener = lc;
   return lc;
 }
 
 struct mg_connection *mg_tun_bind_opt(struct mg_mgr *mgr,
                                       const char *dispatcher,
-                                      mg_event_handler_t handler,
+                                      MG_CB(mg_event_handler_t handler,
+                                            void *user_data),
                                       struct mg_bind_opts opts) {
 #if MG_ENABLE_SSL
   struct mg_tun_ssl_opts ssl = {opts.ssl_cert, opts.ssl_key, opts.ssl_ca_cert};
@@ -11728,7 +11788,7 @@ struct mg_connection *mg_tun_bind_opt(struct mg_mgr *mgr,
   opts.ssl_key = NULL;
   opts.ssl_ca_cert = NULL;
 #endif
-  return mg_tun_do_bind(client, handler, opts);
+  return mg_tun_do_bind(client, MG_CB(handler, user_data), opts);
 }
 
 int mg_tun_parse_frame(void *data, size_t len, struct mg_tun_frame *frame) {
@@ -11909,19 +11969,20 @@ MG_INTERNAL int mg_sntp_parse_reply(const char *buf, int len,
   return 0;
 }
 
-static void mg_sntp_handler(struct mg_connection *c, int ev, void *ev_data) {
+static void mg_sntp_handler(struct mg_connection *c, int ev,
+                            void *ev_data MG_UD_ARG(void *user_data)) {
   struct mbuf *io = &c->recv_mbuf;
   struct mg_sntp_message msg;
 
-  c->handler(c, ev, ev_data);
+  c->handler(c, ev, ev_data MG_UD_ARG(user_data));
 
   switch (ev) {
     case MG_EV_RECV: {
       if (mg_sntp_parse_reply(io->buf, io->len, &msg) < 0) {
         DBG(("Invalid SNTP packet received (%d)", (int) io->len));
-        c->handler(c, MG_SNTP_MALFORMED_REPLY, NULL);
+        c->handler(c, MG_SNTP_MALFORMED_REPLY, NULL MG_UD_ARG(user_data));
       } else {
-        c->handler(c, MG_SNTP_REPLY, (void *) &msg);
+        c->handler(c, MG_SNTP_REPLY, (void *) &msg MG_UD_ARG(user_data));
       }
 
       mbuf_remove(io, io->len);
@@ -11941,7 +12002,8 @@ int mg_set_protocol_sntp(struct mg_connection *c) {
 }
 
 struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr,
-                                      mg_event_handler_t event_handler,
+                                      MG_CB(mg_event_handler_t event_handler,
+                                            void *user_data),
                                       const char *sntp_server_name) {
   struct mg_connection *c = NULL;
   char url[100], *p_url = url;
@@ -11964,7 +12026,7 @@ struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr,
 
   mg_asprintf(&p_url, sizeof(url), "%s%s%s", proto, sntp_server_name, port);
 
-  c = mg_connect(mgr, p_url, event_handler);
+  c = mg_connect(mgr, p_url, event_handler MG_UD_ARG(user_data));
 
   if (c == NULL) {
     goto cleanup;
@@ -11986,8 +12048,11 @@ struct sntp_data {
 };
 
 static void mg_sntp_util_ev_handler(struct mg_connection *c, int ev,
-                                    void *ev_data) {
-  struct sntp_data *sd = (struct sntp_data *) c->user_data;
+                                    void *ev_data MG_UD_ARG(void *user_data)) {
+#if !MG_ENABLE_CALLBACK_USERDATA
+  void *user_data = c->user_data;
+#endif
+  struct sntp_data *sd = (struct sntp_data *) user_data;
 
   switch (ev) {
     case MG_EV_CONNECT:
@@ -12015,7 +12080,7 @@ static void mg_sntp_util_ev_handler(struct mg_connection *c, int ev,
       c->flags |= MG_F_CLOSE_IMMEDIATELY;
       break;
     case MG_EV_CLOSE:
-      MG_FREE(c->user_data);
+      MG_FREE(user_data);
       c->user_data = NULL;
       break;
   }
@@ -12030,14 +12095,17 @@ struct mg_connection *mg_sntp_get_time(struct mg_mgr *mgr,
     return NULL;
   }
 
-  c = mg_sntp_connect(mgr, mg_sntp_util_ev_handler, sntp_server_name);
+  c = mg_sntp_connect(mgr, MG_CB(mg_sntp_util_ev_handler, sd),
+                      sntp_server_name);
   if (c == NULL) {
     MG_FREE(sd);
     return NULL;
   }
 
   sd->hander = event_handler;
+#if !MG_ENABLE_CALLBACK_USERDATA
   c->user_data = sd;
+#endif
 
   return c;
 }
diff --git a/mongoose.h b/mongoose.h
index 0812f9a5fdbcfd78faba8a1d5db383e93d033e93..6f697185fc4b601f4919377ab6a2ad23f3f0ea4f 100644
--- a/mongoose.h
+++ b/mongoose.h
@@ -2963,6 +2963,18 @@ struct {								\
 #define MG_ENABLE_EXTRA_ERRORS_DESC 0
 #endif
 
+#ifndef MG_ENABLE_CALLBACK_USERDATA
+#define MG_ENABLE_CALLBACK_USERDATA 0
+#endif
+
+#if MG_ENABLE_CALLBACK_USERDATA
+#define MG_UD_ARG(ud) , ud
+#define MG_CB(cb, ud) cb, ud
+#else
+#define MG_UD_ARG(ud)
+#define MG_CB(cb, ud) cb
+#endif
+
 #endif /* CS_MONGOOSE_SRC_FEATURES_H_ */
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/net_if.h"
@@ -3229,7 +3241,7 @@ struct mg_connection;
  * Mongoose calls the event handler, passing the events defined below.
  */
 typedef void (*mg_event_handler_t)(struct mg_connection *nc, int ev,
-                                   void *ev_data);
+                                   void *ev_data MG_UD_ARG(void *user_data));
 
 /* Events. Meaning of event parameter (evp) is given in the comment. */
 #define MG_EV_POLL 0    /* Sent to each connection on each mg_mgr_poll() call */
@@ -3392,7 +3404,8 @@ time_t mg_mgr_poll(struct mg_mgr *, int milli);
  * be passed as the `ev_data` pointer. Maximum message size is capped
  * by `MG_CTL_MSG_MESSAGE_SIZE` which is set to 8192 bytes.
  */
-void mg_broadcast(struct mg_mgr *, mg_event_handler_t func, void *, size_t);
+void mg_broadcast(struct mg_mgr *mgr, mg_event_handler_t cb, void *data,
+                  size_t len);
 #endif
 
 /*
@@ -3408,7 +3421,7 @@ void mg_broadcast(struct mg_mgr *, mg_event_handler_t func, void *, size_t);
  * }
  * ```
  */
-struct mg_connection *mg_next(struct mg_mgr *, struct mg_connection *);
+struct mg_connection *mg_next(struct mg_mgr *mgr, struct mg_connection *c);
 
 /*
  * Optional parameters to `mg_add_sock_opt()`.
@@ -3429,7 +3442,9 @@ struct mg_add_sock_opts {
  *
  * For more options see the `mg_add_sock_opt` variant.
  */
-struct mg_connection *mg_add_sock(struct mg_mgr *, sock_t, mg_event_handler_t);
+struct mg_connection *mg_add_sock(struct mg_mgr *mgr, sock_t sock,
+                                  MG_CB(mg_event_handler_t handler,
+                                        void *user_data));
 
 /*
  * Creates a connection, associates it with the given socket and event handler
@@ -3437,9 +3452,10 @@ struct mg_connection *mg_add_sock(struct mg_mgr *, sock_t, mg_event_handler_t);
  *
  * See the `mg_add_sock_opts` structure for a description of the options.
  */
-struct mg_connection *mg_add_sock_opt(struct mg_mgr *, sock_t,
-                                      mg_event_handler_t,
-                                      struct mg_add_sock_opts);
+struct mg_connection *mg_add_sock_opt(struct mg_mgr *mgr, sock_t sock,
+                                      MG_CB(mg_event_handler_t handler,
+                                            void *user_data),
+                                      struct mg_add_sock_opts opts);
 
 /*
  * Optional parameters to `mg_bind_opt()`.
@@ -3485,8 +3501,9 @@ struct mg_bind_opts {
  *
  * See `mg_bind_opt` for full documentation.
  */
-struct mg_connection *mg_bind(struct mg_mgr *, const char *,
-                              mg_event_handler_t);
+struct mg_connection *mg_bind(struct mg_mgr *mgr, const char *address,
+                              MG_CB(mg_event_handler_t handler,
+                                    void *user_data));
 /*
  * Creates a listening connection.
  *
@@ -3506,7 +3523,8 @@ struct mg_connection *mg_bind(struct mg_mgr *, const char *,
  * NOTE: The connection remains owned by the manager, do not free().
  */
 struct mg_connection *mg_bind_opt(struct mg_mgr *mgr, const char *address,
-                                  mg_event_handler_t handler,
+                                  MG_CB(mg_event_handler_t handler,
+                                        void *user_data),
                                   struct mg_bind_opts opts);
 
 /* Optional parameters to `mg_connect_opt()` */
@@ -3569,7 +3587,8 @@ struct mg_connect_opts {
  * See `mg_connect_opt()` for full documentation.
  */
 struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *address,
-                                 mg_event_handler_t handler);
+                                 MG_CB(mg_event_handler_t handler,
+                                       void *user_data));
 
 /*
  * Connects to a remote host.
@@ -3620,7 +3639,8 @@ struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *address,
  * ```
  */
 struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address,
-                                     mg_event_handler_t handler,
+                                     MG_CB(mg_event_handler_t handler,
+                                           void *user_data),
                                      struct mg_connect_opts opts);
 
 #if MG_ENABLE_SSL && MG_NET_IF != MG_NET_IF_SIMPLELINK
@@ -4274,7 +4294,8 @@ void mg_send_websocket_handshake3(struct mg_connection *nc, const char *path,
  * ```
  */
 struct mg_connection *mg_connect_ws(struct mg_mgr *mgr,
-                                    mg_event_handler_t event_handler,
+                                    MG_CB(mg_event_handler_t event_handler,
+                                          void *user_data),
                                     const char *url, const char *protocol,
                                     const char *extra_headers);
 
@@ -4284,11 +4305,10 @@ struct mg_connection *mg_connect_ws(struct mg_mgr *mgr,
  * Mostly identical to `mg_connect_ws`, but allows to provide extra parameters
  * (for example, SSL parameters)
  */
-struct mg_connection *mg_connect_ws_opt(struct mg_mgr *mgr,
-                                        mg_event_handler_t ev_handler,
-                                        struct mg_connect_opts opts,
-                                        const char *url, const char *protocol,
-                                        const char *extra_headers);
+struct mg_connection *mg_connect_ws_opt(
+    struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
+    struct mg_connect_opts opts, const char *url, const char *protocol,
+    const char *extra_headers);
 
 /*
  * Send WebSocket frame to the remote end.
@@ -4739,7 +4759,8 @@ typedef struct mg_str (*mg_fu_fname_fn)(struct mg_connection *nc,
  * ```
  */
 void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data,
-                            mg_fu_fname_fn local_name_fn);
+                            mg_fu_fname_fn local_name_fn
+                                MG_UD_ARG(void *user_data));
 #endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */
 #endif /* MG_ENABLE_FILESYSTEM */
 
@@ -4926,11 +4947,10 @@ extern "C" {
  *       "var_1=value_1&var_2=value_2");
  * ```
  */
-struct mg_connection *mg_connect_http(struct mg_mgr *mgr,
-                                      mg_event_handler_t event_handler,
-                                      const char *url,
-                                      const char *extra_headers,
-                                      const char *post_data);
+struct mg_connection *mg_connect_http(
+    struct mg_mgr *mgr,
+    MG_CB(mg_event_handler_t event_handler, void *user_data), const char *url,
+    const char *extra_headers, const char *post_data);
 
 /*
  * Helper function that creates an outbound HTTP connection.
@@ -4939,12 +4959,10 @@ struct mg_connection *mg_connect_http(struct mg_mgr *mgr,
  *parameters
  * (for example, SSL parameters)
  */
-struct mg_connection *mg_connect_http_opt(struct mg_mgr *mgr,
-                                          mg_event_handler_t ev_handler,
-                                          struct mg_connect_opts opts,
-                                          const char *url,
-                                          const char *extra_headers,
-                                          const char *post_data);
+struct mg_connection *mg_connect_http_opt(
+    struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
+    struct mg_connect_opts opts, const char *url, const char *extra_headers,
+    const char *post_data);
 
 /* Creates digest authentication header for a client request. */
 int mg_http_create_digest_auth_header(char *buf, size_t buf_len,
@@ -5837,7 +5855,8 @@ struct mg_sntp_message {
 
 /* Establishes connection to given sntp server */
 struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr,
-                                      mg_event_handler_t event_handler,
+                                      MG_CB(mg_event_handler_t event_handler,
+                                            void *user_data),
                                       const char *sntp_server_name);
 
 /* Sends time request to given connection */