فهرست منبع

Handler for websocket connection (Step 1/?)

Add an interface to register handler for websocket connections.
See enhancement #30 and question #101
bel 10 سال پیش
والد
کامیت
0016b36204
4فایلهای تغییر یافته به همراه156 افزوده شده و 17 حذف شده
  1. 2 2
      VS2012/ex_embedded_c/ex_embedded_c.vcxproj
  2. 76 7
      examples/embedded_c/embedded_c.c
  3. 14 0
      include/civetweb.h
  4. 64 8
      src/civetweb.c

+ 2 - 2
VS2012/ex_embedded_c/ex_embedded_c.vcxproj

@@ -95,7 +95,7 @@
       </PrecompiledHeader>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>USE_WEBSOCKET;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
@@ -125,7 +125,7 @@
       <Optimization>MaxSpeed</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>USE_WEBSOCKET;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>

+ 76 - 7
examples/embedded_c/embedded_c.c

@@ -1,4 +1,5 @@
 /*
+* Copyright (c) 2013-2015 the CivetWeb developers
 * Copyright (c) 2013 No Face Press, LLC
 * License http://opensource.org/licenses/mit-license.php MIT License
 */
@@ -14,13 +15,13 @@
 #include "civetweb.h"
 
 
-
 #define DOCUMENT_ROOT "."
 #define PORT "8888"
 #define EXAMPLE_URI "/example"
 #define EXIT_URI "/exit"
 int exitNow = 0;
 
+
 int ExampleHandler(struct mg_connection *conn, void *cbdata)
 {
     mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n");
@@ -29,12 +30,14 @@ int ExampleHandler(struct mg_connection *conn, void *cbdata)
     mg_printf(conn, "<p>To see a page from the A handler <a href=\"A\">click here</a></p>");
     mg_printf(conn, "<p>To see a page from the A/B handler <a href=\"A/B\">click here</a></p>");
     mg_printf(conn, "<p>To see a page from the *.foo handler <a href=\"xy.foo\">click here</a></p>");
+    mg_printf(conn, "<p>To test websocket handler <a href=\"/websocket\">click here</a></p>");
     mg_printf(conn, "<p>To exit <a href=\"%s\">click here</a></p>",
               EXIT_URI);
     mg_printf(conn, "</body></html>\n");
     return 1;
 }
 
+
 int ExitHandler(struct mg_connection *conn, void *cbdata)
 {
     mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n");
@@ -43,6 +46,7 @@ int ExitHandler(struct mg_connection *conn, void *cbdata)
     return 1;
 }
 
+
 int AHandler(struct mg_connection *conn, void *cbdata)
 {
     mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n");
@@ -52,6 +56,7 @@ int AHandler(struct mg_connection *conn, void *cbdata)
     return 1;
 }
 
+
 int ABHandler(struct mg_connection *conn, void *cbdata)
 {
     mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n");
@@ -61,6 +66,7 @@ int ABHandler(struct mg_connection *conn, void *cbdata)
     return 1;
 }
 
+
 int FooHandler(struct mg_connection *conn, void *cbdata)
 {
     /* Handler may access the request info using mg_get_request_info */
@@ -76,9 +82,70 @@ int FooHandler(struct mg_connection *conn, void *cbdata)
 }
 
 
-int main(int argc, char *argv[])
+int WebSocketStartHandler(struct mg_connection *conn, void *cbdata)
 {
+    mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n");
 
+    mg_printf(conn, "<!DOCTYPE html>\n");
+    mg_printf(conn, "<html>\n<head>\n");
+    mg_printf(conn, "<meta charset=\"UTF-8\">\n");
+    mg_printf(conn, "<title>Embedded websocket example</title>\n");
+#ifdef USE_WEBSOCKET
+    /* mg_printf(conn, "<script type=\"text/javascript\"><![CDATA[\n"); ... xhtml style */
+    mg_printf(conn, "<script>\n");
+    mg_printf(conn,
+    "function load() {\n"
+    "  var wsproto = (location.protocol === 'https:') ? 'wss:' : 'ws:';\n"
+    "  connection = new WebSocket(wsproto + '//' + window.location.host + '/websocket.lua');\n"
+    "  websock_text_field = document.getElementById('websock_text_field');\n"
+    "  connection.onmessage = function (e) {\n"
+    "    websock_text_field.innerHtml=e.data;\n"
+    "  }\n"
+    "  connection.onerror = function (error) {\n"
+    "    alert('WebSocket error');\n"
+    "    connection.close();\n"
+    "  }\n"
+    "}\n"
+    );
+    /* mg_printf(conn, "]]></script>\n"); ... xhtml style */
+    mg_printf(conn, "</script>\n");
+    mg_printf(conn, "</head>\n<body onload=\"load()\">\n");
+    mg_printf(conn, "<div id='websock_text_field'>No websocket connection yet</div>\n");
+#else
+    mg_printf(conn, "</head>\n<body>\n");
+    mg_printf(conn, "Example not compiled with USE_WEBSOCKET\n");
+#endif
+    mg_printf(conn, "</body>\n</html>\n");
+    return 1;
+}
+
+
+#ifdef USE_WEBSOCKET
+int WebSocketConnectHandler(const struct mg_connection * conn, void *cbdata)
+{
+    return 0;
+}
+
+void WebSocketReadyHandler(const struct mg_connection * conn, void *cbdata)
+{
+    const char * text = "Hello from the websocket ready handler";
+    mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, text, strlen(text));
+}
+
+int WebsocketDataHandler(const struct mg_connection * conn, int bits, char * data, size_t len, void *cbdata)
+{
+    return 1;
+}
+
+void WebSocketCloseHandler(const struct mg_connection * conn, void *cbdata)
+{
+}
+
+#endif
+
+
+int main(int argc, char *argv[])
+{
     const char * options[] = { "document_root", DOCUMENT_ROOT,
                                "listening_ports", PORT, 0
                              };
@@ -88,11 +155,13 @@ int main(int argc, char *argv[])
     memset(&callbacks, 0, sizeof(callbacks));
     ctx = mg_start(&callbacks, 0, options);
 
-    mg_set_request_handler(ctx,EXAMPLE_URI, ExampleHandler,0);
-    mg_set_request_handler(ctx,EXIT_URI, ExitHandler,0);
-    mg_set_request_handler(ctx,"/a", AHandler,0);
-    mg_set_request_handler(ctx,"/a/b", ABHandler,0);
-    mg_set_request_handler(ctx,"**.foo$", FooHandler,0);
+    mg_set_request_handler(ctx, EXAMPLE_URI, ExampleHandler, 0);
+    mg_set_request_handler(ctx, EXIT_URI, ExitHandler, 0);
+    mg_set_request_handler(ctx, "/a", AHandler, 0);
+    mg_set_request_handler(ctx, "/a/b", ABHandler, 0);
+    mg_set_request_handler(ctx, "**.foo$", FooHandler, 0);
+    mg_set_request_handler(ctx, "/websocket", WebSocketStartHandler, 0);
+    mg_set_websocket_handler(ctx, "/websocket", WebSocketConnectHandler, WebSocketReadyHandler, WebsocketDataHandler, WebSocketCloseHandler, 0);
 
     printf("Browse files at http://localhost:%s/\n", PORT);
     printf("Run example at http://localhost:%s%s\n", PORT, EXAMPLE_URI);

+ 14 - 0
include/civetweb.h

@@ -264,6 +264,20 @@ typedef int (* mg_request_handler)(struct mg_connection *conn, void *cbdata);
       cbdata: the callback data to give to the handler when it is called. */
 CIVETWEB_API void mg_set_request_handler(struct mg_context *ctx, const char *uri, mg_request_handler handler, void *cbdata);
 
+typedef int  (*mg_websocket_connect_handler)(const struct mg_connection *, void *);
+typedef void (*mg_websocket_ready_handler)(struct mg_connection *, void *);
+typedef int  (*mg_websocket_data_handler)(struct mg_connection *, int, char *, size_t, void *);
+typedef void (*mg_connection_close_handler)(struct mg_connection *, void *);
+
+
+CIVETWEB_API void mg_set_websocket_handler(struct mg_context *ctx,
+                                           const char *uri,
+                                           mg_websocket_connect_handler connect_handler,
+                                           mg_websocket_ready_handler ready_handler,
+                                           mg_websocket_data_handler data_handler,
+                                           mg_connection_close_handler close_handler,
+                                           void *cbdata
+                                           );
 
 /* Get the value of particular configuration parameter.
    The value returned is read-only. Civetweb does not allow changing

+ 64 - 8
src/civetweb.c

@@ -821,9 +821,21 @@ static struct mg_option config_options[] = {
 struct mg_request_handler_info {
     char *uri;
     size_t uri_len;
-    mg_request_handler handler;
+
     int is_websocket_handler;
 
+    union {
+        struct {
+            mg_request_handler handler;
+        };
+        struct {
+            mg_websocket_connect_handler connect_handler;
+            mg_websocket_ready_handler ready_handler;
+            mg_websocket_data_handler data_handler;
+            mg_connection_close_handler close_handler;
+        };
+    };
+
     void *cbdata;
     struct mg_request_handler_info *next;
 };
@@ -5933,7 +5945,7 @@ static void handle_websocket_request(struct mg_connection *conn, const char *pat
 #ifdef USE_LUA
     int lua_websock = 0;
 #endif
-    
+
 #ifndef USE_LUA
     (void)path;
     (void)is_script_resource;
@@ -6249,11 +6261,28 @@ static void redirect_to_https_port(struct mg_connection *conn, int ssl_index)
 }
 
 
-static void mg_set_request_handler_type(struct mg_context *ctx, const char *uri, mg_request_handler handler, void *cbdata, int is_websocket_handler)
+static void mg_set_request_handler_type(struct mg_context *ctx,
+                                        const char *uri,
+                                        int is_websocket_handler,
+                                        int is_delete_request,
+                                        mg_request_handler handler,
+                                        mg_websocket_connect_handler connect_handler,
+                                        mg_websocket_ready_handler ready_handler,
+                                        mg_websocket_data_handler data_handler,
+                                        mg_connection_close_handler close_handler,
+                                        void *cbdata)
 {
     struct mg_request_handler_info *tmp_rh, **lastref;
     size_t urilen = strlen(uri);
 
+    if (is_websocket_handler) {
+        assert(handler == NULL);
+        assert(is_delete_request || connect_handler!=NULL || ready_handler!=NULL || data_handler!=NULL || close_handler!=NULL);
+    } else {
+        assert(connect_handler==NULL && ready_handler==NULL && data_handler==NULL && close_handler==NULL);
+        assert(is_delete_request || (handler!=NULL));
+    }
+
     mg_lock_context(ctx);
 
     /* first try to find an existing handler */
@@ -6261,9 +6290,16 @@ static void mg_set_request_handler_type(struct mg_context *ctx, const char *uri,
     for (tmp_rh = ctx->request_handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) {
         if (tmp_rh->is_websocket_handler == is_websocket_handler) {
             if (urilen == tmp_rh->uri_len && !strcmp(tmp_rh->uri,uri)) {
-                if (handler != NULL) {
+                if (!is_delete_request) {
                     /* update existing handler */
-                    tmp_rh->handler = handler;
+                    if (!is_websocket_handler) {
+                        tmp_rh->handler = handler;
+                    } else {
+                        tmp_rh->connect_handler = connect_handler;
+                        tmp_rh->ready_handler = ready_handler;
+                        tmp_rh->data_handler = data_handler;
+                        tmp_rh->close_handler = close_handler;
+                    }
                     tmp_rh->cbdata = cbdata;
                 } else {
                     /* remove existing handler */
@@ -6278,7 +6314,7 @@ static void mg_set_request_handler_type(struct mg_context *ctx, const char *uri,
         lastref = &(tmp_rh->next);
     }
 
-    if (handler == NULL) {
+    if (is_delete_request) {
         /* no handler to set, this was a remove request to a non-existing handler */
         mg_unlock_context(ctx);
         return;
@@ -6298,7 +6334,14 @@ static void mg_set_request_handler_type(struct mg_context *ctx, const char *uri,
         return;
     }
     tmp_rh->uri_len = urilen;
-    tmp_rh->handler = handler;
+    if (!is_websocket_handler) {
+        tmp_rh->handler = handler;
+    } else {
+        tmp_rh->connect_handler = connect_handler;
+        tmp_rh->ready_handler = ready_handler;
+        tmp_rh->data_handler = data_handler;
+        tmp_rh->close_handler = close_handler;
+    }
     tmp_rh->cbdata = cbdata;
     tmp_rh->is_websocket_handler = is_websocket_handler;
     tmp_rh->next = NULL;
@@ -6309,7 +6352,20 @@ static void mg_set_request_handler_type(struct mg_context *ctx, const char *uri,
 
 void mg_set_request_handler(struct mg_context *ctx, const char *uri, mg_request_handler handler, void *cbdata)
 {
-    mg_set_request_handler_type(ctx, uri, handler, cbdata, 0);
+    mg_set_request_handler_type(ctx, uri, 0, handler==NULL, handler, NULL, NULL, NULL, NULL, cbdata);
+}
+
+void mg_set_websocket_handler(struct mg_context *ctx,
+                              const char *uri,
+                              mg_websocket_connect_handler connect_handler,
+                              mg_websocket_ready_handler ready_handler,
+                              mg_websocket_data_handler data_handler,
+                              mg_connection_close_handler close_handler,
+                              void *cbdata
+                              )
+{
+    int is_delete_request = (connect_handler!=NULL) || (ready_handler!=NULL) || (data_handler!=NULL) || (close_handler!=NULL);
+    mg_set_request_handler_type(ctx, uri, 1, is_delete_request, NULL, connect_handler, ready_handler, data_handler, close_handler, cbdata);
 }
 
 static int get_request_handler(struct mg_connection *conn, int is_websocket_request, mg_request_handler *handler, void **cbdata)