Browse code

modules/websockets: started adding WebSocket connection management to WebSocket module

Peter Dunkley authored on 21/06/2012 15:28:57
Showing 5 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,187 @@
1
+/*
2
+ * $Id$
3
+ *
4
+ * Copyright (C) 2012 Crocodile RCS Ltd
5
+ *
6
+ * This file is part of Kamailio, a free SIP server.
7
+ *
8
+ * Kamailio is free software; you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation; either version 2 of the License, or
11
+ * (at your option) any later version
12
+ *
13
+ * Kamailio is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License 
19
+ * along with this program; if not, write to the Free Software 
20
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21
+ *
22
+ */
23
+
24
+#include "../../locking.h"
25
+#include "../../tcp_conn.h"
26
+#include "../../mem/mem.h"
27
+#include "ws_conn.h"
28
+
29
+struct ws_connection **wsconn_hash = NULL;
30
+gen_lock_t *wsconn_lock = NULL;
31
+
32
+char *wsconn_state_str[] =
33
+{
34
+	"CONNECTING",
35
+	"OPEN",
36
+	"CLOSING",
37
+	"CLOSED"
38
+};
39
+
40
+static inline void _wsconn_rm(ws_connection_t *wsc);
41
+
42
+int wsconn_init(void)
43
+{
44
+	wsconn_lock = lock_alloc();
45
+	if (wsconn_lock == NULL)
46
+	{
47
+		LM_ERR("allocating lock\n");
48
+		return -1;
49
+	}
50
+	if (lock_init(wsconn_lock) == 0)
51
+	{
52
+		LM_ERR("initialising lock\n");
53
+		lock_dealloc((void *) wsconn_lock);
54
+		wsconn_lock = NULL;
55
+		return -1;
56
+	}
57
+
58
+	wsconn_hash =
59
+		(ws_connection_t **) shm_malloc(TCP_ID_HASH_SIZE *
60
+						sizeof(ws_connection_t));
61
+	if (wsconn_hash == NULL)
62
+	{
63
+		LM_ERR("allocating WebSocket hash-table\n");
64
+		lock_dealloc((void *) wsconn_lock);
65
+		wsconn_lock = NULL;
66
+		return -1;
67
+	}
68
+	memset((void *) wsconn_hash, 0,
69
+		TCP_ID_HASH_SIZE * sizeof(ws_connection_t *));
70
+
71
+	return 0;
72
+}
73
+
74
+void wsconn_destroy(void)
75
+{
76
+	int h;
77
+
78
+	if (wsconn_hash)
79
+	{
80
+		WSCONN_UNLOCK;
81
+		WSCONN_LOCK;
82
+		for (h = 0; h < TCP_ID_HASH_SIZE; h++)
83
+		{
84
+			ws_connection_t *wsc = wsconn_hash[h];
85
+			while (wsc)
86
+			{
87
+				ws_connection_t *next = wsc->next;
88
+				_wsconn_rm(wsc);
89
+				wsc = next;
90
+			}
91
+		}
92
+		WSCONN_UNLOCK;
93
+
94
+		shm_free(wsconn_hash);
95
+		wsconn_hash = NULL;
96
+	}
97
+
98
+	if (wsconn_lock)
99
+	{
100
+		lock_destroy(wsconn_lock);
101
+		lock_dealloc((void *) wsconn_lock);
102
+		wsconn_lock = NULL;
103
+	}
104
+}
105
+
106
+int wsconn_add(struct tcp_connection *con)
107
+{
108
+	ws_connection_t *wsc;
109
+
110
+	if (!con)
111
+	{
112
+		LM_ERR("wsconn_add: null pointer\n");
113
+		return -1;
114
+	}
115
+
116
+	wsc = shm_malloc(sizeof(ws_connection_t));
117
+	if (wsc == NULL)
118
+	{
119
+		LM_ERR("allocating shared memory\n");
120
+		return -1;
121
+	}
122
+	memset(wsc, 0, sizeof(ws_connection_t));
123
+
124
+	wsc->con = con;
125
+	wsc->id_hash = con->id_hash;
126
+	wsc->last_used = (int)time(NULL);
127
+	wsc->state = WS_S_OPEN;
128
+
129
+	/* Make sure Kamailio core sends future messages on this connection
130
+	   directly to this module */
131
+	con->flags |= F_CONN_WS;
132
+
133
+	WSCONN_LOCK;
134
+	wsc->next = wsconn_hash[wsc->id_hash];
135
+	wsc->prev = NULL;
136
+	if (wsconn_hash[wsc->id_hash]) wsconn_hash[wsc->id_hash]->prev = wsc;
137
+	wsconn_hash[wsc->id_hash] = wsc;
138
+	WSCONN_UNLOCK;
139
+
140
+	return 0;
141
+}
142
+
143
+static inline void _wsconn_rm(ws_connection_t *wsc)
144
+{
145
+	if (wsconn_hash[wsc->id_hash] == wsc)
146
+		wsconn_hash[wsc->id_hash] = wsc->next;
147
+	if (wsc->next) wsc->next->prev = wsc->prev;
148
+	if (wsc->prev) wsc->prev->next = wsc->next;
149
+	shm_free(wsc);
150
+	wsc = NULL;
151
+}
152
+
153
+int wsconn_rm(ws_connection_t *wsc)
154
+{
155
+	if (!wsc)
156
+	{
157
+		LM_ERR("wsconn_rm: null pointer\n");
158
+		return -1;
159
+	}
160
+
161
+	WSCONN_LOCK;
162
+	_wsconn_rm(wsc);
163
+	WSCONN_UNLOCK;
164
+
165
+	return 0;
166
+}
167
+
168
+ws_connection_t *wsconn_find(struct tcp_connection *con)
169
+{
170
+	ws_connection_t *wsc;
171
+
172
+	if (!con)
173
+	{
174
+		LM_ERR("wsconn_find: null pointer\n");
175
+		return NULL;
176
+	}
177
+
178
+	WSCONN_LOCK;
179
+	for (wsc = wsconn_hash[con->id_hash]; wsc; wsc = wsc->next)
180
+	{
181
+		if (wsc->id_hash == con->id_hash)
182
+			return wsc;
183
+	}
184
+	WSCONN_UNLOCK;
185
+
186
+	return NULL;
187
+}
0 188
new file mode 100644
... ...
@@ -0,0 +1,63 @@
1
+/*
2
+ * $Id$
3
+ *
4
+ * Copyright (C) 2012 Crocodile RCS Ltd
5
+ *
6
+ * This file is part of Kamailio, a free SIP server.
7
+ *
8
+ * Kamailio is free software; you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation; either version 2 of the License, or
11
+ * (at your option) any later version
12
+ *
13
+ * Kamailio is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License 
19
+ * along with this program; if not, write to the Free Software 
20
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21
+ *
22
+ */
23
+
24
+#ifndef _WS_CONN_H
25
+#define _WS_CONN_H
26
+
27
+#include "../../locking.h"
28
+#include "../../tcp_conn.h"
29
+
30
+typedef enum
31
+{
32
+	WS_S_CONNECTING	= 0,
33
+	WS_S_OPEN,
34
+	WS_S_CLOSING,
35
+	WS_S_CLOSED
36
+} ws_conn_state_t;
37
+
38
+typedef struct ws_connection
39
+{
40
+	struct tcp_connection *con;
41
+
42
+	ws_conn_state_t state;
43
+	unsigned int id_hash;
44
+	unsigned int last_used;
45
+
46
+	struct ws_connection *prev;
47
+	struct ws_connection *next;
48
+} ws_connection_t;
49
+
50
+extern ws_connection_t **wsconn_hash;
51
+extern gen_lock_t *wsconn_lock;
52
+extern char *wsconn_state_str[];
53
+
54
+int wsconn_init(void);
55
+void wsconn_destroy(void);
56
+int wsconn_add(struct tcp_connection *con);
57
+int wsconn_rm(ws_connection_t *wsc);
58
+ws_connection_t *wsconn_find(struct tcp_connection *con);
59
+
60
+#define WSCONN_LOCK	lock_get(wsconn_lock);
61
+#define WSCONN_UNLOCK	lock_release(wsconn_lock);
62
+
63
+#endif /* _WS_CONN_H */
... ...
@@ -26,6 +26,8 @@
26 26
 #include "../../tcp_server.h"
27 27
 #include "../../lib/kcore/kstats_wrapper.h"
28 28
 #include "../../lib/kmi/tree.h"
29
+#include "../../mem/mem.h"
30
+#include "ws_conn.h"
29 31
 #include "ws_frame.h"
30 32
 #include "ws_mod.h"
31 33
 
... ...
@@ -31,9 +31,11 @@
31 31
 #include "../../lib/kcore/kstats_wrapper.h"
32 32
 #include "../../lib/kcore/cmpapi.h"
33 33
 #include "../../lib/kmi/tree.h"
34
+#include "../../mem/mem.h"
34 35
 #include "../../parser/msg_parser.h"
35 36
 #include "../sl/sl.h"
36 37
 #include "../tls/tls_cfg.h"
38
+#include "ws_conn.h"
37 39
 #include "ws_handshake.h"
38 40
 #include "ws_mod.h"
39 41
 
... ...
@@ -308,9 +310,8 @@ int ws_handle_handshake(struct sip_msg *msg)
308 310
 				&str_status_switching_protocols, &headers) < 0)
309 311
 		return 0;
310 312
 
311
-	/* Make sure Kamailio core sends future requests on this connection
312
-	   directly to this module */
313
-	con->flags |= F_CONN_WS;
313
+	/* Add the connection to the WebSocket connection table */
314
+	wsconn_add(con);
314 315
 
315 316
 	return 0;
316 317
 }
... ...
@@ -30,7 +30,9 @@
30 30
 #include "../../lib/kcore/kstats_wrapper.h"
31 31
 #include "../../lib/kmi/mi.h"
32 32
 #include "../../lib/kmi/tree.h"
33
+#include "../../mem/mem.h"
33 34
 #include "../../parser/msg_parser.h"
35
+#include "ws_conn.h"
34 36
 #include "ws_handshake.h"
35 37
 #include "ws_frame.h"
36 38
 #include "ws_mod.h"
... ...
@@ -121,111 +123,133 @@ static int mod_init(void)
121 123
 	if (sl_load_api(&ws_slb) != 0)
122 124
 	{
123 125
 		LM_ERR("binding to SL\n");
124
-		return -1;
126
+		goto error;
125 127
 	}
126 128
 
127 129
 	if (sr_event_register_cb(SREV_TCP_WS_FRAME, ws_frame_received) != 0)
128 130
 	{
129 131
 		LM_ERR("registering WebSocket call-back\n");
130
-		return -1;
132
+		goto error;
131 133
 	}
132 134
 
133 135
 	if (register_module_stats(exports.name, stats) != 0)
134 136
 	{
135 137
 		LM_ERR("registering core statistics\n");
136
-		return -1;
138
+		goto error;
137 139
 	}
138 140
 
139 141
 	if (register_mi_mod(exports.name, mi_cmds) != 0)
140 142
 	{
141 143
 		LM_ERR("registering MI commands\n");
142
-		return -1;
144
+		goto error;
145
+	}
146
+
147
+	if (wsconn_init() < 0)
148
+	{
149
+		LM_ERR("initialising WebSocket connections table\n");
150
+		goto error;
143 151
 	}
144 152
 
145 153
 	if ((ws_enabled = (int *) shm_malloc(sizeof(int))) == NULL)
146 154
 	{
147 155
 		LM_ERR("allocating shared memory\n");
148
-		return -1;
156
+		goto error;
149 157
 	}
150 158
 	*ws_enabled = 1;
151 159
 
160
+	if (wsconn_init() < 0)
161
+	{
162
+		LM_ERR("initialising WebSocket connections table\n");
163
+		goto error;
164
+	}
165
+
152 166
 	if ((ws_stats_lock = lock_alloc()) == NULL)
153 167
 	{
154 168
 		LM_ERR("allocating lock\n");
155
-		return -1;
169
+		goto error;
156 170
 	}
157 171
 	if (lock_init(ws_stats_lock) == NULL)
158 172
 	{
159 173
 		LM_ERR("initialising lock\n");
160
-		lock_dealloc(ws_stats_lock);
161
-		return -1;
174
+		goto error;
162 175
 	}
163 176
 
164
-	/* TODO: register module with core to receive WS/WSS messages */
165
-
166 177
 	return 0;
178
+
179
+error:
180
+	wsconn_destroy();
181
+
182
+	if (ws_stats_lock)
183
+		lock_dealloc(ws_stats_lock);
184
+
185
+	shm_free(ws_enabled);
186
+
187
+	return -1;
167 188
 }
168 189
 
169 190
 static void destroy(void)
170 191
 {
192
+	wsconn_destroy();
171 193
 	shm_free(ws_enabled);
172 194
 	lock_destroy(ws_stats_lock);
173 195
 	lock_dealloc(ws_stats_lock);
174
-
175
-	/* TODO: close all connections */
176 196
 }
177 197
 
178 198
 static struct mi_root *mi_dump(struct mi_root *cmd, void *param)
179 199
 {
180
-	int h, connections = 0;
200
+	int h, connections = 0, interval;
181 201
 	char *src_proto, *dst_proto;
182 202
 	char src_ip[IP6_MAX_STR_SIZE + 1], dst_ip[IP6_MAX_STR_SIZE + 1];
183
-	struct tcp_connection *c;
203
+	ws_connection_t *wsc;
184 204
 	struct mi_root *rpl_tree = init_mi_tree(200, MI_OK_S, MI_OK_LEN);
185 205
 
186 206
 	if (!rpl_tree)
187 207
 		return 0;
188 208
 
189
-	TCPCONN_LOCK;
209
+	WSCONN_LOCK;
190 210
 	for (h = 0; h < TCP_ID_HASH_SIZE; h++)
191 211
 	{
192
-		c = tcpconn_id_hash[h];
193
-		while(c)
212
+		wsc = wsconn_hash[h];
213
+		while(wsc)
194 214
 		{
195
-			if (c->flags & F_CONN_WS)
215
+			if (wsc->con)
196 216
 			{
197
-				src_proto = (c->rcv.proto== PROTO_TCP)
217
+				src_proto = (wsc->con->rcv.proto== PROTO_TCP)
198 218
 						? "tcp" : "tls";
199 219
 				memset(src_ip, 0, IP6_MAX_STR_SIZE + 1);
200
-				ip_addr2sbuf(&c->rcv.src_ip, src_ip,
220
+				ip_addr2sbuf(&wsc->con->rcv.src_ip, src_ip,
201 221
 						IP6_MAX_STR_SIZE);
202 222
 
203
-				dst_proto = (c->rcv.proto == PROTO_TCP)
223
+				dst_proto = (wsc->con->rcv.proto == PROTO_TCP)
204 224
 						? "tcp" : "tls";
205 225
 				memset(dst_ip, 0, IP6_MAX_STR_SIZE + 1);
206
-				ip_addr2sbuf(&c->rcv.dst_ip, src_ip,
226
+				ip_addr2sbuf(&wsc->con->rcv.dst_ip, src_ip,
207 227
 						IP6_MAX_STR_SIZE);
208 228
 
229
+				interval = (int)time(NULL) - wsc->last_used;
230
+
209 231
 				if (addf_mi_node_child(&rpl_tree->node, 0, 0, 0,
210
-						"id - %d, "
211
-						"src - %s:%s:%hu, "
212
-						"dst - %s:%s:%hu",
213
-						c->id,
232
+						"%d: %s:%s:%hu -> %s:%s:%hu "
233
+						"(state: %s, "
234
+						"last used %ds ago)",
235
+						wsc->con->id,
214 236
 						src_proto,
215 237
 						strlen(src_ip) ? src_ip : "*",
216
-						c->rcv.src_port,
238
+						wsc->con->rcv.src_port,
217 239
 						dst_proto,
218 240
 						strlen(dst_ip) ? dst_ip : "*",
219
-						c->rcv.dst_port) == 0)
241
+						wsc->con->rcv.dst_port,
242
+						wsconn_state_str[wsc->state],
243
+						interval) == 0)
220 244
 					return 0;
221 245
 
222 246
 				connections++;
223 247
 			}
224 248
 
225
-			c = c->id_next;
249
+			wsc = wsc->next;
226 250
 		}
227 251
 	}
228
-	TCPCONN_UNLOCK;
252
+	WSCONN_UNLOCK;
229 253
 
230 254
 	if (addf_mi_node_child(&rpl_tree->node, 0, 0, 0,
231 255
 				"%d WebSocket connection%s found",