Browse code

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 authored on 07/08/2012 22:38:18
Showing 7 changed files
... ...
@@ -43,6 +43,10 @@ Peter Dunkley
43 43
               6.5. ws.disable
44 44
               6.6. ws.enable
45 45
 
46
+        7. Event routes
47
+
48
+              7.1. websocket:closed
49
+
46 50
    List of Examples
47 51
 
48 52
    1.1. event_route[xhttp:request]
... ...
@@ -90,6 +94,10 @@ Chapter 1. Admin Guide
90 94
         6.5. ws.disable
91 95
         6.6. ws.enable
92 96
 
97
+   7. Event routes
98
+
99
+        7.1. websocket:closed
100
+
93 101
 1. Overview
94 102
 
95 103
    This module implements a WebSocket (RFC 6455) server and provides
... ...
@@ -153,7 +161,12 @@ event_route[xhttp:request] {
153 161
 
154 162
                 # ws_handle_handshake() exits (no further configuration file
155 163
                 # processing of the request) when complete.
156
-                ws_handle_handshake();
164
+                if (ws_handle_handshake())
165
+                {
166
+                        # Optional... cache some information about the
167
+                        # successful connection
168
+                        exit;
169
+                }
157 170
         }
158 171
 
159 172
         xhttp_reply("404", "Not found", "", "");
... ...
@@ -356,8 +369,8 @@ modparam("websocket", "ping_application_data", "WebSockets rock")
356 369
 
357 370
 Note
358 371
 
359
-   This function always returns 0, stopping all further processing of the
360
-   request.
372
+   This function returns 0, stopping all further processing of the
373
+   request, when there is a problem.
361 374
 
362 375
    Example 1.8. ws_handle_handshake usage
363 376
 ...
... ...
@@ -460,3 +473,18 @@ Note
460 473
    MI FIFO Command Format:
461 474
                         :ws.enable:fifo_reply
462 475
                         _empty_line_
476
+
477
+7. Event routes
478
+
479
+   7.1. websocket:closed
480
+
481
+7.1.  websocket:closed
482
+
483
+   When defined, the module calls event_route[websocket:closed] when a
484
+   connection closes. The connection may be identified using the the $si
485
+   and $sp pseudo-variables.
486
+...
487
+event_route[websocket:closed] {
488
+        xlog("L_INFO", "WebSocket connection from $si:$sp has closed\n");
489
+}
490
+...
... ...
@@ -77,7 +77,12 @@ event_route[xhttp:request] {
77 77
 
78 78
                 # ws_handle_handshake() exits (no further configuration file
79 79
                 # processing of the request) when complete.
80
-                ws_handle_handshake();
80
+                if (ws_handle_handshake())
81
+		{
82
+			# Optional... cache some information about the
83
+			# successful connection
84
+			exit;
85
+		}
81 86
         }
82 87
 
83 88
         xhttp_reply("404", "Not found", "", "");
... ...
@@ -343,8 +348,8 @@ modparam("websocket", "ping_application_data", "WebSockets rock")
343 348
 		from HTTP to WebSocket.</para>
344 349
 		<para>This function can be used from ANY_ROUTE (but will only
345 350
 		work in <emphasis>event_route[xhttp:request]</emphasis>).</para>
346
-		<note><para>This function always returns 0, stopping all further
347
-		processing of the request.</para></note>
351
+		<note><para>This function returns 0, stopping all further
352
+		processing of the request, when there is a problem.</para></note>
348 353
 		<example>
349 354
 		<title><function>ws_handle_handshake</function> usage</title>
350 355
 		<programlisting format="linespecific">
... ...
@@ -466,7 +471,29 @@ ws_handle_handshake();
466 471
 </programlisting>
467 472
 	</section>
468 473
 
474
+	</section>
469 475
 
476
+	<section>
477
+	<title>Event routes</title>
478
+	<section>
479
+		<title>
480
+		<function moreinfo="none">websocket:closed</function>
481
+		</title>
482
+		<para>
483
+			When defined, the module calls
484
+			event_route[websocket:closed] when a connection
485
+			closes.  The connection may be identified using the
486
+			the $si and $sp pseudo-variables.
487
+		</para>
488
+		<programlisting format="linespecific">
489
+...
490
+event_route[websocket:closed] {
491
+	xlog("L_INFO", "WebSocket connection from $si:$sp has closed\n");
492
+}
493
+...
494
+		</programlisting>
470 495
 	</section>
496
+
497
+	</section>	
471 498
 </chapter>
472 499
 
... ...
@@ -333,9 +333,18 @@ event_route[xhttp:request] {
333 333
 
334 334
 		# ws_handle_handshake() exits (no further configuration file
335 335
 		# processing of the request) when complete.
336
-		ws_handle_handshake();
336
+		if (ws_handle_handshake())
337
+		{
338
+			# Optional... cache some information abou the
339
+			# successful connection
340
+			exit;
341
+		}
337 342
 	}
338 343
 
339 344
 	xhttp_reply("404", "Not found", "", "");
340 345
 }
346
+
347
+event_route[websocket:closed] {
348
+	xlog("L_INFO", "WebSocket connection from $si:$sp has closed\n");
349
+}
341 350
 #!endif
... ...
@@ -24,6 +24,7 @@
24 24
 #include "../../locking.h"
25 25
 #include "../../str.h"
26 26
 #include "../../tcp_conn.h"
27
+#include "../../lib/kcore/faked_msg.h"
27 28
 #include "../../lib/kcore/kstats_wrapper.h"
28 29
 #include "../../lib/kmi/tree.h"
29 30
 #include "../../mem/mem.h"
... ...
@@ -175,9 +176,10 @@ void wsconn_destroy(void)
175 176
 	}
176 177
 }
177 178
 
178
-int wsconn_add(int id)
179
+int wsconn_add(struct receive_info rcv)
179 180
 {
180 181
 	int cur_cons, max_cons;
182
+	int id = rcv.proto_reserved1;
181 183
 	int id_hash = tcp_id_hash(id);
182 184
 	ws_connection_t *wsc;
183 185
 
... ...
@@ -192,6 +194,7 @@ int wsconn_add(int id)
192 194
 	wsc->id = id;
193 195
 	wsc->id_hash = id_hash;
194 196
 	wsc->state = WS_S_OPEN;
197
+	wsc->rcv = rcv;
195 198
 
196 199
 	WSCONN_LOCK;
197 200
 	/* Add to WebSocket connection table */
... ...
@@ -221,7 +224,37 @@ int wsconn_add(int id)
221 224
 	return 0;
222 225
 }
223 226
 
224
-int wsconn_rm(ws_connection_t *wsc)
227
+static void wsconn_run_route(ws_connection_t *wsc)
228
+{
229
+	int rt, backup_rt;
230
+	struct run_act_ctx ctx;
231
+	sip_msg_t *fmsg;
232
+
233
+	LM_DBG("wsconn_run_route event_route[websocket:closed]\n");
234
+
235
+	rt = route_get(&event_rt, "websocket:closed");
236
+	if (rt < 0 || event_rt.rlist[rt] == NULL)
237
+	{
238
+		LM_DBG("route does not exist");
239
+		return;
240
+	}
241
+
242
+	if (faked_msg_init() < 0)
243
+	{
244
+		LM_ERR("faked_msg_init() failed\n");
245
+		return;
246
+	}
247
+	fmsg = faked_msg_next();
248
+	fmsg->rcv = wsc->rcv;
249
+
250
+	backup_rt = get_route_type();
251
+	set_route_type(REQUEST_ROUTE);
252
+	init_run_actions_ctx(&ctx);
253
+	run_top_route(event_rt.rlist[rt], fmsg, 0);
254
+	set_route_type(backup_rt);
255
+}
256
+
257
+int wsconn_rm(ws_connection_t *wsc, ws_conn_eventroute_t run_event_route)
225 258
 {
226 259
 	if (!wsc)
227 260
 	{
... ...
@@ -229,6 +262,9 @@ int wsconn_rm(ws_connection_t *wsc)
229 262
 		return -1;
230 263
 	}
231 264
 
265
+	if (run_event_route == WSCONN_EVENTROUTE_YES)
266
+		wsconn_run_route(wsc);
267
+
232 268
 	WSCONN_LOCK;
233 269
 	/* Remove from the WebSocket used list */
234 270
 	if (wsconn_used_list->head == wsc)
... ...
@@ -289,7 +325,7 @@ void wsconn_close_now(ws_connection_t *wsc)
289 325
 	con->state = S_CONN_BAD;
290 326
 	con->timeout = get_ticks_raw();
291 327
 
292
-	if (wsconn_rm(wsc) < 0)
328
+	if (wsconn_rm(wsc, WSCONN_EVENTROUTE_YES) < 0)
293 329
 		LM_ERR("removing WebSocket connection\n");
294 330
 }
295 331
 
... ...
@@ -48,6 +48,8 @@ typedef struct ws_connection
48 48
 	unsigned id_hash;	/* for the corresponding TCP/TLS connection */
49 49
 	struct ws_connection *id_prev;
50 50
 	struct ws_connection *id_next;
51
+
52
+	struct receive_info rcv;
51 53
 } ws_connection_t;
52 54
 
53 55
 typedef struct
... ...
@@ -56,6 +58,12 @@ typedef struct
56 58
 	ws_connection_t *tail;
57 59
 } ws_connection_used_list_t;
58 60
 
61
+typedef enum
62
+{
63
+	WSCONN_EVENTROUTE_NO = 0,
64
+	WSCONN_EVENTROUTE_YES
65
+} ws_conn_eventroute_t;
66
+
59 67
 extern ws_connection_used_list_t *wsconn_used_list;
60 68
 
61 69
 extern char *wsconn_state_str[];
... ...
@@ -65,8 +73,8 @@ extern stat_var *ws_max_concurrent_connections;
65 73
 
66 74
 int wsconn_init(void);
67 75
 void wsconn_destroy(void);
68
-int wsconn_add(int id);
69
-int wsconn_rm(ws_connection_t *wsc);
76
+int wsconn_add(struct receive_info rcv);
77
+int wsconn_rm(ws_connection_t *wsc, ws_conn_eventroute_t run_event_route);
70 78
 int wsconn_update(ws_connection_t *wsc);
71 79
 void wsconn_close_now(ws_connection_t *wsc);
72 80
 ws_connection_t *wsconn_get(int id);
... ...
@@ -216,7 +216,7 @@ static int encode_and_send_ws_frame(ws_frame_t *frame, conn_close_t conn_close)
216 216
 	if ((con = tcpconn_get(frame->wsc->id, 0, 0, 0, 0)) == NULL)
217 217
 	{
218 218
 		LM_WARN("TCP/TLS connection get failed\n");
219
-		if (wsconn_rm(frame->wsc) < 0)
219
+		if (wsconn_rm(frame->wsc, WSCONN_EVENTROUTE_YES) < 0)
220 220
 			LM_ERR("removing WebSocket connection\n");
221 221
 		return -1;
222 222
 	}
... ...
@@ -224,7 +224,7 @@ static int encode_and_send_ws_frame(ws_frame_t *frame, conn_close_t conn_close)
224 224
 	if (conn_close == CONN_CLOSE_DO)
225 225
 	{
226 226
 		dst.send_flags.f |= SND_F_CON_CLOSE;
227
-		if (wsconn_rm(frame->wsc) < 0)
227
+		if (wsconn_rm(frame->wsc, WSCONN_EVENTROUTE_YES) < 0)
228 228
 		{
229 229
 			LM_ERR("removing WebSocket connection\n");
230 230
 			return -1;
... ...
@@ -271,7 +271,7 @@ static int encode_and_send_ws_frame(ws_frame_t *frame, conn_close_t conn_close)
271 271
 		LM_ERR("sending WebSocket frame\n");
272 272
 		pkg_free(send_buf);
273 273
 		update_stat(ws_failed_connections, 1);
274
-		if (wsconn_rm(frame->wsc) < 0)
274
+		if (wsconn_rm(frame->wsc, WSCONN_EVENTROUTE_YES) < 0)
275 275
 			LM_ERR("removing WebSocket connection\n");
276 276
 		return -1;
277 277
 	}
... ...
@@ -292,7 +292,7 @@ int ws_handle_handshake(struct sip_msg *msg)
292 292
 				(unsigned char *) reply_key.s, KEY_BUF_LEN);
293 293
 
294 294
 	/* Add the connection to the WebSocket connection table */
295
-	wsconn_add(msg->rcv.proto_reserved1);
295
+	wsconn_add(msg->rcv);
296 296
 
297 297
 	/* Make sure Kamailio core sends future messages on this connection
298 298
 	   directly to this module */
... ...
@@ -321,10 +321,14 @@ int ws_handle_handshake(struct sip_msg *msg)
321 321
 	msg->rpl_send_flags.f &= ~SND_F_CON_CLOSE;
322 322
 	if (ws_send_reply(msg, 101,
323 323
 				&str_status_switching_protocols, &headers) < 0)
324
+	{
324 325
 		if ((wsc = wsconn_get(msg->rcv.proto_reserved1)) != NULL)
325
-			wsconn_rm(wsc);
326
+			wsconn_rm(wsc, WSCONN_EVENTROUTE_NO);
326 327
 
327
-	return 0;
328
+		return 0;
329
+	}
330
+
331
+	return 1;
328 332
 }
329 333
 
330 334
 struct mi_root *ws_mi_disable(struct mi_root *cmd, void *param)