From 0016b36204690f8261fc494c5c4e4936064924d2 Mon Sep 17 00:00:00 2001 From: bel Date: Sat, 18 Apr 2015 18:14:20 +0200 Subject: [PATCH] Handler for websocket connection (Step 1/?) Add an interface to register handler for websocket connections. See enhancement #30 and question #101 --- VS2012/ex_embedded_c/ex_embedded_c.vcxproj | 4 +- examples/embedded_c/embedded_c.c | 83 ++++++++++++++++++++-- include/civetweb.h | 14 ++++ src/civetweb.c | 72 ++++++++++++++++--- 4 files changed, 156 insertions(+), 17 deletions(-) diff --git a/VS2012/ex_embedded_c/ex_embedded_c.vcxproj b/VS2012/ex_embedded_c/ex_embedded_c.vcxproj index 3c1f407f..fc232977 100644 --- a/VS2012/ex_embedded_c/ex_embedded_c.vcxproj +++ b/VS2012/ex_embedded_c/ex_embedded_c.vcxproj @@ -95,7 +95,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + USE_WEBSOCKET;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) $(ProjectDir)..\..\include;%(AdditionalIncludeDirectories) @@ -125,7 +125,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + USE_WEBSOCKET;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) $(ProjectDir)..\..\include;%(AdditionalIncludeDirectories) diff --git a/examples/embedded_c/embedded_c.c b/examples/embedded_c/embedded_c.c index 44e3bf51..519f5f55 100644 --- a/examples/embedded_c/embedded_c.c +++ b/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, "

To see a page from the A handler click here

"); mg_printf(conn, "

To see a page from the A/B handler click here

"); mg_printf(conn, "

To see a page from the *.foo handler click here

"); + mg_printf(conn, "

To test websocket handler click here

"); mg_printf(conn, "

To exit click here

", EXIT_URI); mg_printf(conn, "\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 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, "\n"); + mg_printf(conn, "\n\n"); + mg_printf(conn, "\n"); + mg_printf(conn, "Embedded websocket example\n"); +#ifdef USE_WEBSOCKET + /* mg_printf(conn, "\n"); ... xhtml style */ + mg_printf(conn, "\n"); + mg_printf(conn, "\n\n"); + mg_printf(conn, "
No websocket connection yet
\n"); +#else + mg_printf(conn, "\n\n"); + mg_printf(conn, "Example not compiled with USE_WEBSOCKET\n"); +#endif + mg_printf(conn, "\n\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); diff --git a/include/civetweb.h b/include/civetweb.h index 72b89e98..3edbe9a3 100644 --- a/include/civetweb.h +++ b/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 diff --git a/src/civetweb.c b/src/civetweb.c index a1985968..a41ed22c 100755 --- a/src/civetweb.c +++ b/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)