ソースを参照

modules/websocket: Added new event_route[websocket:closed] which is run when a WebSocket connection is closed

- Also changed ws_handle_handshake() to return 1 on success (still 0 on all
  errors as they are handled - and the correct responses sent - within the
  function).
Peter Dunkley 13 年 前
コミット
6f928a54d1

+ 31 - 3
modules/websocket/README

@@ -43,6 +43,10 @@ Peter Dunkley
               6.5. ws.disable
               6.5. ws.disable
               6.6. ws.enable
               6.6. ws.enable
 
 
+        7. Event routes
+
+              7.1. websocket:closed
+
    List of Examples
    List of Examples
 
 
    1.1. event_route[xhttp:request]
    1.1. event_route[xhttp:request]
@@ -90,6 +94,10 @@ Chapter 1. Admin Guide
         6.5. ws.disable
         6.5. ws.disable
         6.6. ws.enable
         6.6. ws.enable
 
 
+   7. Event routes
+
+        7.1. websocket:closed
+
 1. Overview
 1. Overview
 
 
    This module implements a WebSocket (RFC 6455) server and provides
    This module implements a WebSocket (RFC 6455) server and provides
@@ -153,7 +161,12 @@ event_route[xhttp:request] {
 
 
                 # ws_handle_handshake() exits (no further configuration file
                 # ws_handle_handshake() exits (no further configuration file
                 # processing of the request) when complete.
                 # processing of the request) when complete.
-                ws_handle_handshake();
+                if (ws_handle_handshake())
+                {
+                        # Optional... cache some information about the
+                        # successful connection
+                        exit;
+                }
         }
         }
 
 
         xhttp_reply("404", "Not found", "", "");
         xhttp_reply("404", "Not found", "", "");
@@ -356,8 +369,8 @@ modparam("websocket", "ping_application_data", "WebSockets rock")
 
 
 Note
 Note
 
 
-   This function always returns 0, stopping all further processing of the
-   request.
+   This function returns 0, stopping all further processing of the
+   request, when there is a problem.
 
 
    Example 1.8. ws_handle_handshake usage
    Example 1.8. ws_handle_handshake usage
 ...
 ...
@@ -460,3 +473,18 @@ Note
    MI FIFO Command Format:
    MI FIFO Command Format:
                         :ws.enable:fifo_reply
                         :ws.enable:fifo_reply
                         _empty_line_
                         _empty_line_
+
+7. Event routes
+
+   7.1. websocket:closed
+
+7.1.  websocket:closed
+
+   When defined, the module calls event_route[websocket:closed] when a
+   connection closes. The connection may be identified using the the $si
+   and $sp pseudo-variables.
+...
+event_route[websocket:closed] {
+        xlog("L_INFO", "WebSocket connection from $si:$sp has closed\n");
+}
+...

+ 30 - 3
modules/websocket/doc/websocket_admin.xml

@@ -77,7 +77,12 @@ event_route[xhttp:request] {
 
 
                 # ws_handle_handshake() exits (no further configuration file
                 # ws_handle_handshake() exits (no further configuration file
                 # processing of the request) when complete.
                 # processing of the request) when complete.
-                ws_handle_handshake();
+                if (ws_handle_handshake())
+		{
+			# Optional... cache some information about the
+			# successful connection
+			exit;
+		}
         }
         }
 
 
         xhttp_reply("404", "Not found", "", "");
         xhttp_reply("404", "Not found", "", "");
@@ -343,8 +348,8 @@ modparam("websocket", "ping_application_data", "WebSockets rock")
 		from HTTP to WebSocket.</para>
 		from HTTP to WebSocket.</para>
 		<para>This function can be used from ANY_ROUTE (but will only
 		<para>This function can be used from ANY_ROUTE (but will only
 		work in <emphasis>event_route[xhttp:request]</emphasis>).</para>
 		work in <emphasis>event_route[xhttp:request]</emphasis>).</para>
-		<note><para>This function always returns 0, stopping all further
-		processing of the request.</para></note>
+		<note><para>This function returns 0, stopping all further
+		processing of the request, when there is a problem.</para></note>
 		<example>
 		<example>
 		<title><function>ws_handle_handshake</function> usage</title>
 		<title><function>ws_handle_handshake</function> usage</title>
 		<programlisting format="linespecific">
 		<programlisting format="linespecific">
@@ -466,7 +471,29 @@ ws_handle_handshake();
 </programlisting>
 </programlisting>
 	</section>
 	</section>
 
 
+	</section>
 
 
+	<section>
+	<title>Event routes</title>
+	<section>
+		<title>
+		<function moreinfo="none">websocket:closed</function>
+		</title>
+		<para>
+			When defined, the module calls
+			event_route[websocket:closed] when a connection
+			closes.  The connection may be identified using the
+			the $si and $sp pseudo-variables.
+		</para>
+		<programlisting format="linespecific">
+...
+event_route[websocket:closed] {
+	xlog("L_INFO", "WebSocket connection from $si:$sp has closed\n");
+}
+...
+		</programlisting>
 	</section>
 	</section>
+
+	</section>	
 </chapter>
 </chapter>
 
 

+ 10 - 1
modules/websocket/example/kamailio.cfg

@@ -333,9 +333,18 @@ event_route[xhttp:request] {
 
 
 		# ws_handle_handshake() exits (no further configuration file
 		# ws_handle_handshake() exits (no further configuration file
 		# processing of the request) when complete.
 		# processing of the request) when complete.
-		ws_handle_handshake();
+		if (ws_handle_handshake())
+		{
+			# Optional... cache some information abou the
+			# successful connection
+			exit;
+		}
 	}
 	}
 
 
 	xhttp_reply("404", "Not found", "", "");
 	xhttp_reply("404", "Not found", "", "");
 }
 }
+
+event_route[websocket:closed] {
+	xlog("L_INFO", "WebSocket connection from $si:$sp has closed\n");
+}
 #!endif
 #!endif

+ 39 - 3
modules/websocket/ws_conn.c

@@ -24,6 +24,7 @@
 #include "../../locking.h"
 #include "../../locking.h"
 #include "../../str.h"
 #include "../../str.h"
 #include "../../tcp_conn.h"
 #include "../../tcp_conn.h"
+#include "../../lib/kcore/faked_msg.h"
 #include "../../lib/kcore/kstats_wrapper.h"
 #include "../../lib/kcore/kstats_wrapper.h"
 #include "../../lib/kmi/tree.h"
 #include "../../lib/kmi/tree.h"
 #include "../../mem/mem.h"
 #include "../../mem/mem.h"
@@ -175,9 +176,10 @@ void wsconn_destroy(void)
 	}
 	}
 }
 }
 
 
-int wsconn_add(int id)
+int wsconn_add(struct receive_info rcv)
 {
 {
 	int cur_cons, max_cons;
 	int cur_cons, max_cons;
+	int id = rcv.proto_reserved1;
 	int id_hash = tcp_id_hash(id);
 	int id_hash = tcp_id_hash(id);
 	ws_connection_t *wsc;
 	ws_connection_t *wsc;
 
 
@@ -192,6 +194,7 @@ int wsconn_add(int id)
 	wsc->id = id;
 	wsc->id = id;
 	wsc->id_hash = id_hash;
 	wsc->id_hash = id_hash;
 	wsc->state = WS_S_OPEN;
 	wsc->state = WS_S_OPEN;
+	wsc->rcv = rcv;
 
 
 	WSCONN_LOCK;
 	WSCONN_LOCK;
 	/* Add to WebSocket connection table */
 	/* Add to WebSocket connection table */
@@ -221,7 +224,37 @@ int wsconn_add(int id)
 	return 0;
 	return 0;
 }
 }
 
 
-int wsconn_rm(ws_connection_t *wsc)
+static void wsconn_run_route(ws_connection_t *wsc)
+{
+	int rt, backup_rt;
+	struct run_act_ctx ctx;
+	sip_msg_t *fmsg;
+
+	LM_DBG("wsconn_run_route event_route[websocket:closed]\n");
+
+	rt = route_get(&event_rt, "websocket:closed");
+	if (rt < 0 || event_rt.rlist[rt] == NULL)
+	{
+		LM_DBG("route does not exist");
+		return;
+	}
+
+	if (faked_msg_init() < 0)
+	{
+		LM_ERR("faked_msg_init() failed\n");
+		return;
+	}
+	fmsg = faked_msg_next();
+	fmsg->rcv = wsc->rcv;
+
+	backup_rt = get_route_type();
+	set_route_type(REQUEST_ROUTE);
+	init_run_actions_ctx(&ctx);
+	run_top_route(event_rt.rlist[rt], fmsg, 0);
+	set_route_type(backup_rt);
+}
+
+int wsconn_rm(ws_connection_t *wsc, ws_conn_eventroute_t run_event_route)
 {
 {
 	if (!wsc)
 	if (!wsc)
 	{
 	{
@@ -229,6 +262,9 @@ int wsconn_rm(ws_connection_t *wsc)
 		return -1;
 		return -1;
 	}
 	}
 
 
+	if (run_event_route == WSCONN_EVENTROUTE_YES)
+		wsconn_run_route(wsc);
+
 	WSCONN_LOCK;
 	WSCONN_LOCK;
 	/* Remove from the WebSocket used list */
 	/* Remove from the WebSocket used list */
 	if (wsconn_used_list->head == wsc)
 	if (wsconn_used_list->head == wsc)
@@ -289,7 +325,7 @@ void wsconn_close_now(ws_connection_t *wsc)
 	con->state = S_CONN_BAD;
 	con->state = S_CONN_BAD;
 	con->timeout = get_ticks_raw();
 	con->timeout = get_ticks_raw();
 
 
-	if (wsconn_rm(wsc) < 0)
+	if (wsconn_rm(wsc, WSCONN_EVENTROUTE_YES) < 0)
 		LM_ERR("removing WebSocket connection\n");
 		LM_ERR("removing WebSocket connection\n");
 }
 }
 
 

+ 10 - 2
modules/websocket/ws_conn.h

@@ -48,6 +48,8 @@ typedef struct ws_connection
 	unsigned id_hash;	/* for the corresponding TCP/TLS connection */
 	unsigned id_hash;	/* for the corresponding TCP/TLS connection */
 	struct ws_connection *id_prev;
 	struct ws_connection *id_prev;
 	struct ws_connection *id_next;
 	struct ws_connection *id_next;
+
+	struct receive_info rcv;
 } ws_connection_t;
 } ws_connection_t;
 
 
 typedef struct
 typedef struct
@@ -56,6 +58,12 @@ typedef struct
 	ws_connection_t *tail;
 	ws_connection_t *tail;
 } ws_connection_used_list_t;
 } ws_connection_used_list_t;
 
 
+typedef enum
+{
+	WSCONN_EVENTROUTE_NO = 0,
+	WSCONN_EVENTROUTE_YES
+} ws_conn_eventroute_t;
+
 extern ws_connection_used_list_t *wsconn_used_list;
 extern ws_connection_used_list_t *wsconn_used_list;
 
 
 extern char *wsconn_state_str[];
 extern char *wsconn_state_str[];
@@ -65,8 +73,8 @@ extern stat_var *ws_max_concurrent_connections;
 
 
 int wsconn_init(void);
 int wsconn_init(void);
 void wsconn_destroy(void);
 void wsconn_destroy(void);
-int wsconn_add(int id);
-int wsconn_rm(ws_connection_t *wsc);
+int wsconn_add(struct receive_info rcv);
+int wsconn_rm(ws_connection_t *wsc, ws_conn_eventroute_t run_event_route);
 int wsconn_update(ws_connection_t *wsc);
 int wsconn_update(ws_connection_t *wsc);
 void wsconn_close_now(ws_connection_t *wsc);
 void wsconn_close_now(ws_connection_t *wsc);
 ws_connection_t *wsconn_get(int id);
 ws_connection_t *wsconn_get(int id);

+ 3 - 3
modules/websocket/ws_frame.c

@@ -216,7 +216,7 @@ static int encode_and_send_ws_frame(ws_frame_t *frame, conn_close_t conn_close)
 	if ((con = tcpconn_get(frame->wsc->id, 0, 0, 0, 0)) == NULL)
 	if ((con = tcpconn_get(frame->wsc->id, 0, 0, 0, 0)) == NULL)
 	{
 	{
 		LM_WARN("TCP/TLS connection get failed\n");
 		LM_WARN("TCP/TLS connection get failed\n");
-		if (wsconn_rm(frame->wsc) < 0)
+		if (wsconn_rm(frame->wsc, WSCONN_EVENTROUTE_YES) < 0)
 			LM_ERR("removing WebSocket connection\n");
 			LM_ERR("removing WebSocket connection\n");
 		return -1;
 		return -1;
 	}
 	}
@@ -224,7 +224,7 @@ static int encode_and_send_ws_frame(ws_frame_t *frame, conn_close_t conn_close)
 	if (conn_close == CONN_CLOSE_DO)
 	if (conn_close == CONN_CLOSE_DO)
 	{
 	{
 		dst.send_flags.f |= SND_F_CON_CLOSE;
 		dst.send_flags.f |= SND_F_CON_CLOSE;
-		if (wsconn_rm(frame->wsc) < 0)
+		if (wsconn_rm(frame->wsc, WSCONN_EVENTROUTE_YES) < 0)
 		{
 		{
 			LM_ERR("removing WebSocket connection\n");
 			LM_ERR("removing WebSocket connection\n");
 			return -1;
 			return -1;
@@ -271,7 +271,7 @@ static int encode_and_send_ws_frame(ws_frame_t *frame, conn_close_t conn_close)
 		LM_ERR("sending WebSocket frame\n");
 		LM_ERR("sending WebSocket frame\n");
 		pkg_free(send_buf);
 		pkg_free(send_buf);
 		update_stat(ws_failed_connections, 1);
 		update_stat(ws_failed_connections, 1);
-		if (wsconn_rm(frame->wsc) < 0)
+		if (wsconn_rm(frame->wsc, WSCONN_EVENTROUTE_YES) < 0)
 			LM_ERR("removing WebSocket connection\n");
 			LM_ERR("removing WebSocket connection\n");
 		return -1;
 		return -1;
 	}
 	}

+ 7 - 3
modules/websocket/ws_handshake.c

@@ -292,7 +292,7 @@ int ws_handle_handshake(struct sip_msg *msg)
 				(unsigned char *) reply_key.s, KEY_BUF_LEN);
 				(unsigned char *) reply_key.s, KEY_BUF_LEN);
 
 
 	/* Add the connection to the WebSocket connection table */
 	/* Add the connection to the WebSocket connection table */
-	wsconn_add(msg->rcv.proto_reserved1);
+	wsconn_add(msg->rcv);
 
 
 	/* Make sure Kamailio core sends future messages on this connection
 	/* Make sure Kamailio core sends future messages on this connection
 	   directly to this module */
 	   directly to this module */
@@ -321,10 +321,14 @@ int ws_handle_handshake(struct sip_msg *msg)
 	msg->rpl_send_flags.f &= ~SND_F_CON_CLOSE;
 	msg->rpl_send_flags.f &= ~SND_F_CON_CLOSE;
 	if (ws_send_reply(msg, 101,
 	if (ws_send_reply(msg, 101,
 				&str_status_switching_protocols, &headers) < 0)
 				&str_status_switching_protocols, &headers) < 0)
+	{
 		if ((wsc = wsconn_get(msg->rcv.proto_reserved1)) != NULL)
 		if ((wsc = wsconn_get(msg->rcv.proto_reserved1)) != NULL)
-			wsconn_rm(wsc);
+			wsconn_rm(wsc, WSCONN_EVENTROUTE_NO);
 
 
-	return 0;
+		return 0;
+	}
+
+	return 1;
 }
 }
 
 
 struct mi_root *ws_mi_disable(struct mi_root *cmd, void *param)
 struct mi_root *ws_mi_disable(struct mi_root *cmd, void *param)