Browse code

modules/websocket: Added support for MSRP WebSocket sub-protocol

- Prototype of draft-pd-msrp-websocket
- Enables session based chat and group chat from HTML5 clients

Peter Dunkley authored on 11/10/2012 11:57:26
Showing 9 changed files
... ...
@@ -16,6 +16,7 @@ Peter Dunkley
16 16
 
17 17
               2.1. Initiating a connection
18 18
               2.2. SIP message routing
19
+              2.3. MSRP message routing
19 20
 
20 21
         3. Dependencies
21 22
 
... ...
@@ -29,6 +30,7 @@ Peter Dunkley
29 30
               4.3. keepalive_processes (integer)
30 31
               4.4. keepalive_interval (integer)
31 32
               4.5. ping_application_data (string)
33
+              4.6. sub_protocols (integer)
32 34
 
33 35
         5. Functions
34 36
 
... ...
@@ -56,7 +58,8 @@ Peter Dunkley
56 58
    1.5. Set keepalive_processes parameter
57 59
    1.6. Set keepalive_interval parameter
58 60
    1.7. Set ping_application_data parameter
59
-   1.8. ws_handle_handshake usage
61
+   1.8. Set sub_protocols parameter
62
+   1.9. ws_handle_handshake usage
60 63
 
61 64
 Chapter 1. Admin Guide
62 65
 
... ...
@@ -67,6 +70,7 @@ Chapter 1. Admin Guide
67 70
 
68 71
         2.1. Initiating a connection
69 72
         2.2. SIP message routing
73
+        2.3. MSRP message routing
70 74
 
71 75
    3. Dependencies
72 76
 
... ...
@@ -80,6 +84,7 @@ Chapter 1. Admin Guide
80 84
         4.3. keepalive_processes (integer)
81 85
         4.4. keepalive_interval (integer)
82 86
         4.5. ping_application_data (string)
87
+        4.6. sub_protocols (integer)
83 88
 
84 89
    5. Functions
85 90
 
... ...
@@ -102,8 +107,9 @@ Chapter 1. Admin Guide
102 107
 
103 108
    This module implements a WebSocket (RFC 6455) server and provides
104 109
    connection establishment (handshaking), management (including
105
-   connection keep-alive), and framing for the SIP WebSocket sub-protocol
106
-   (ietf-draft-sipcore-sip-websocket).
110
+   connection keep-alive), and framing for the SIP and MSRP WebSocket
111
+   sub-protocols (draft-ietf-sipcore-sip-websocket and
112
+   draft-pd-msrp-websocket).
107 113
 
108 114
    The module supports WebSockets (ws) and secure WebSockets (wss)
109 115
 
... ...
@@ -111,6 +117,7 @@ Chapter 1. Admin Guide
111 117
 
112 118
    2.1. Initiating a connection
113 119
    2.2. SIP message routing
120
+   2.3. MSRP message routing
114 121
 
115 122
 2.1. Initiating a connection
116 123
 
... ...
@@ -122,13 +129,14 @@ Chapter 1. Admin Guide
122 129
    headers before calling Section 5.1, “ ws_handle_handshake() ”. The
123 130
    event_route can also be used to make sure the HTTP GET has the correct
124 131
    URI, perform HTTP authentication on the WebSocket connection, and check
125
-   the Origin header (RFC 6454) to ensure a browser-based SIP UA has been
126
-   downloaded from the correct location.
132
+   the Origin header (RFC 6454) to ensure a browser-based SIP UA or MSRP
133
+   client has been downloaded from the correct location.
127 134
 
128 135
    Example 1.1. event_route[xhttp:request]
129 136
 ...
130 137
 loadmodule "sl.so"
131 138
 loadmodule "xhttp.so"
139
+loadmodule "msrp.so"  # Only required if using MSRP over WebSockets
132 140
 loadmodule "websocket.so"
133 141
 ...
134 142
 event_route[xhttp:request] {
... ...
@@ -194,7 +202,7 @@ event_route[xhttp:request] {
194 202
    WebSocket connection is made. This means that the routing headers
195 203
    cannot be used for request or response routing in the normal manner.
196 204
 
197
-   ietf-draft-sipcore-sip-websocket states that SIP WebSocket Clients and
205
+   draft-ietf-sipcore-sip-websocket states that SIP WebSocket Clients and
198 206
    the SIP registrar should implement Outbound (RFC 5626) and Path (RFC
199 207
    3327) to enable requests and responses to be correctly routed. However,
200 208
    Kamailio does not currently support Outbound and it may not be possible
... ...
@@ -265,6 +273,16 @@ onreply_route[WS_REPLY] {
265 273
 }
266 274
 ...
267 275
 
276
+2.3. MSRP message routing
277
+
278
+   MSRP over WebSocket clients create invalid local URIs for use in Path
279
+   headers (From-Path: and To-Path:) because a JavaScript stack running in
280
+   a browser has no way to determine the local address from which the
281
+   WebSocket connection is made. This is OK because MSRP over WebSocket
282
+   clients MUST use an MSRP relay and it is the MSRP relay's
283
+   responsibility to select the correct connection to the client based on
284
+   the MSRP URIs that it has created (and maintains a mapping for).
285
+
268 286
 3. Dependencies
269 287
 
270 288
    3.1. Kamailio Modules
... ...
@@ -284,6 +302,9 @@ onreply_route[WS_REPLY] {
284 302
    scheme:
285 303
      * tls.
286 304
 
305
+   The following module is required to support MSRP over WebSockets:
306
+     * msrp.
307
+
287 308
 3.2. External Libraries or Applications
288 309
 
289 310
    The following libraries must be installed before running Kamailio with
... ...
@@ -298,6 +319,7 @@ onreply_route[WS_REPLY] {
298 319
    4.3. keepalive_processes (integer)
299 320
    4.4. keepalive_interval (integer)
300 321
    4.5. ping_application_data (string)
322
+   4.6. sub_protocols (integer)
301 323
 
302 324
 4.1. keepalive_mechanism (integer)
303 325
 
... ...
@@ -367,6 +389,21 @@ modparam("websocket", "keepalive_interval", 2)
367 389
 modparam("websocket", "ping_application_data", "WebSockets rock")
368 390
 ...
369 391
 
392
+4.6. sub_protocols (integer)
393
+
394
+   A bitmap that allows you to control the sub-protocols supported by the
395
+   WebSocket server.
396
+     * 1 - sip (draft-ietf-sipcore-sip-websocket)
397
+     * 2 - msrp (draft-pd-msrp-websocket) - msrp.so must be loaded before
398
+       websocket.so
399
+
400
+   Default value is 1 when msrp.so is not loaded 3 when msrp.so is loaded.
401
+
402
+   Example 1.8. Set sub_protocols parameter
403
+...
404
+modparam("websocket", "sub_protocols", 2)
405
+...
406
+
370 407
 5. Functions
371 408
 
372 409
    5.1. ws_handle_handshake()
... ...
@@ -385,7 +422,7 @@ Note
385 422
    This function returns 0, stopping all further processing of the
386 423
    request, when there is a problem.
387 424
 
388
-   Example 1.8. ws_handle_handshake usage
425
+   Example 1.9. ws_handle_handshake usage
389 426
 ...
390 427
 ws_handle_handshake();
391 428
 ...
... ...
@@ -17,8 +17,9 @@
17 17
 	<title>Overview</title>
18 18
 	<para>This module implements a WebSocket (RFC 6455) server and provides
19 19
 	connection establishment (handshaking), management (including
20
-	connection keep-alive), and framing for the SIP WebSocket sub-protocol
21
-	(ietf-draft-sipcore-sip-websocket).</para>
20
+	connection keep-alive), and framing for the SIP and MSRP WebSocket
21
+	sub-protocols (draft-ietf-sipcore-sip-websocket and
22
+	draft-pd-msrp-websocket).</para>
22 23
 	<para>The module supports WebSockets (ws) and secure WebSockets (wss)
23 24
 	</para>
24 25
 	</section>
... ...
@@ -37,14 +38,15 @@
37 38
 	used to make sure the HTTP GET has the correct URI, perform HTTP
38 39
 	authentication on the WebSocket connection, and check the
39 40
 	<emphasis>Origin</emphasis> header (RFC 6454) to ensure a
40
-	browser-based SIP UA has been downloaded from the correct location.
41
-	</para>
41
+	browser-based SIP UA or MSRP client has been downloaded from the
42
+	correct location.</para>
42 43
 	<example>
43 44
 	<title>event_route[xhttp:request]</title>
44 45
 	<programlisting><![CDATA[
45 46
 ...
46 47
 loadmodule "sl.so"
47 48
 loadmodule "xhttp.so"
49
+loadmodule "msrp.so"  # Only required if using MSRP over WebSockets
48 50
 loadmodule "websocket.so"
49 51
 ...
50 52
 event_route[xhttp:request] {
... ...
@@ -107,13 +109,13 @@ event_route[xhttp:request] {
107 109
 
108 110
 	<section>
109 111
 	<title>SIP message routing</title>
110
-	<para> SIP over WebSockets uses invalid URIs in routing headers
112
+	<para>SIP over WebSockets uses invalid URIs in routing headers
111 113
 	(Contact:, Record-Route:, and Via:) because a JavaScript stack running
112 114
 	in a browser has no way to determine the local address from which the
113 115
 	WebSocket connection is made.  This means that the routing headers
114 116
 	cannot be used for request or response routing in the normal manner.
115 117
 	</para>
116
-	<para>ietf-draft-sipcore-sip-websocket states that SIP WebSocket
118
+	<para>draft-ietf-sipcore-sip-websocket states that SIP WebSocket
117 119
 	Clients and the SIP registrar should implement Outbound (RFC 5626) and
118 120
 	Path (RFC 3327) to enable requests and responses to be correctly
119 121
 	routed.  However, &kamailio; does not currently support Outbound and
... ...
@@ -192,6 +194,18 @@ onreply_route[WS_REPLY] {
192 194
 	</example>
193 195
 
194 196
 	</section>
197
+
198
+	<section>
199
+	<title>MSRP message routing</title>
200
+	<para>MSRP over WebSocket clients create invalid local URIs for use in
201
+	Path headers (From-Path: and To-Path:) because a JavaScript stack
202
+	running in a browser has no way to determine the local address from
203
+	which the WebSocket connection is made.  This is OK because MSRP over
204
+	WebSocket clients MUST use an MSRP relay and it is the MSRP relay's
205
+	responsibility to select the correct connection to the client based on
206
+	the MSRP URIs that it has created (and maintains a mapping for).</para>
207
+	</section>
208
+
195 209
 	</section>
196 210
 
197 211
 	<section>
... ...
@@ -230,6 +244,15 @@ onreply_route[WS_REPLY] {
230 244
 		</listitem>
231 245
 		</itemizedlist>
232 246
 		</para>
247
+		<para>
248
+		The following module is required to support MSRP over
249
+		WebSockets:
250
+		<itemizedlist>
251
+		<listitem>
252
+		<para><emphasis>msrp</emphasis>.</para>
253
+		</listitem>
254
+		</itemizedlist>
255
+		</para>
233 256
 	</section>
234 257
 
235 258
 	<section>
... ...
@@ -348,6 +371,32 @@ modparam("websocket", "ping_application_data", "WebSockets rock")
348 371
 		</example>
349 372
 	</section>
350 373
 
374
+	<section>
375
+		<title><varname>sub_protocols</varname> (integer)</title>
376
+		<para>A bitmap that allows you to control the sub-protocols
377
+		supported by the WebSocket server.</para>
378
+		<itemizedlist>
379
+		<listitem><para>
380
+		<emphasis>1</emphasis> - sip (draft-ietf-sipcore-sip-websocket)
381
+		</para></listitem>
382
+		<listitem><para>
383
+		<emphasis>2</emphasis> - msrp (draft-pd-msrp-websocket) -
384
+		msrp.so must be loaded before websocket.so
385
+		</para></listitem>
386
+		</itemizedlist>
387
+		<para><emphasis>Default value is 1 when msrp.so is not loaded
388
+		 3 when msrp.so is loaded.</emphasis></para>
389
+		<example>
390
+		<title>Set <varname>sub_protocols</varname>
391
+		parameter</title>
392
+		<programlisting format="linespecific">
393
+...
394
+modparam("websocket", "sub_protocols", 2)
395
+...
396
+</programlisting>
397
+		</example>
398
+	</section>
399
+
351 400
 	</section>
352 401
 
353 402
 	<section>
... ...
@@ -8,12 +8,15 @@
8 8
 #!substdef "!MY_DOMAIN!example.com!g"
9 9
 #!substdef "!MY_WS_PORT!80!g"
10 10
 #!substdef "!MY_WSS_PORT!443!g"
11
+#!substdef "!MY_MSRP_PORT!9000!g"
11 12
 #!substdef "!MY_WS_ADDR!tcp:MY_IP_ADDR:MY_WS_PORT!g"
12 13
 #!substdef "!MY_WSS_ADDR!tls:MY_IP_ADDR:MY_WSS_PORT!g"
14
+#!substdef "!MY_MSRP_ADDR!tls:MY_IP_ADDR:MY_MSRP_PORT!g"
13 15
 
14 16
 ##!define LOCAL_TEST_RUN
15 17
 #!define WITH_TLS
16 18
 #!define WITH_WEBSOCKETS
19
+#!define WITH_MSRP
17 20
 
18 21
 
19 22
 ####### Global Parameters #########
... ...
@@ -32,6 +35,9 @@ listen=MY_WS_ADDR
32 35
 listen=MY_WSS_ADDR
33 36
 #!endif
34 37
 #!endif
38
+#!ifdef WITH_MSRP
39
+listen=MY_MSRP_ADDR
40
+#!endif
35 41
 
36 42
 tcp_connection_lifetime=3604
37 43
 tcp_accept_no_cl=yes
... ...
@@ -68,6 +74,11 @@ loadmodule "corex.so"
68 74
 #!ifdef WITH_TLS
69 75
 loadmodule "tls.so"
70 76
 #!endif
77
+#!ifdef WITH_MSRP
78
+loadmodule "msrp.so"
79
+loadmodule "htable.so"
80
+loadmodule "cfgutils.so"
81
+#!endif
71 82
 #!ifdef WITH_WEBSOCKETS
72 83
 loadmodule "xhttp.so"
73 84
 loadmodule "websocket.so"
... ...
@@ -124,6 +135,11 @@ modparam("nathelper|registrar", "received_avp", "$avp(RECEIVED)")
124 135
 #       their own keep-alives.
125 136
 #!endif
126 137
 
138
+#!ifdef WITH_MSRP
139
+# ----- htable params -----
140
+modparam("htable", "htable", "msrp=>size=8;autoexpire=3600;")
141
+#!endif
142
+
127 143
 
128 144
 ####### Routing Logic ########
129 145
 
... ...
@@ -202,7 +218,7 @@ route[RELAY] {
202 218
 # Per SIP request initial checks
203 219
 route[REQINIT] {
204 220
 	if (!mf_process_maxfwd_header("10")) {
205
-		sl_send_reply("483","Too Many Hops");
221
+		sl_send_reply("483", "Too Many Hops");
206 222
 		exit;
207 223
 	}
208 224
 
... ...
@@ -368,3 +384,86 @@ event_route[websocket:closed] {
368 384
 	xlog("L_INFO", "WebSocket connection from $si:$sp has closed\n");
369 385
 }
370 386
 #!endif
387
+
388
+#!ifdef WITH_MSRP
389
+event_route[msrp:frame-in] {
390
+	xdbg("============#[[$msrp(method)]]===========\n");
391
+	xdbg("============*[[$si:$sp]]\n");
392
+	xdbg("============ crthop:   [$msrp(crthop)]\n");
393
+	xdbg("============ prevhop:  [$msrp(prevhop)]\n");
394
+	xdbg("============ nexthop:  [$msrp(nexthop)]\n");
395
+	xdbg("============ firsthop: [$msrp(firsthop)]\n");
396
+	xdbg("============ lasthop:  [$msrp(lasthop)]\n");
397
+	xdbg("============ prevhops: [$msrp(prevhops)]\n");
398
+	xdbg("============ nexthops: [$msrp(nexthops)]\n");
399
+	xdbg("============ srcaddr:  [$msrp(srcaddr)]\n");
400
+	xdbg("============ srcsock:  [$msrp(srcsock)]\n");
401
+	xdbg("============ sessid:   [$msrp(sessid)]\n");
402
+
403
+	msrp_reply_flags("1");
404
+
405
+	if(msrp_is_reply())
406
+	{
407
+		msrp_relay();
408
+		exit;
409
+	}
410
+
411
+	# handle AUTH MSRP requests
412
+	if($msrp(method)=="AUTH")
413
+	{
414
+		if($msrp(nexthops)>0)
415
+		{
416
+			msrp_relay();
417
+			exit;
418
+		}
419
+		# frame for local server - send Use-Path
420
+		# -- passwd can be loaded from DB based on $au
421
+		$var(passwd) = "xyz123";
422
+		if(!pv_www_authenticate("myrealm", "$var(passwd)", "0"))
423
+		{
424
+			if(auth_get_www_authenticate("myrealm", "0",
425
+						"$var(wauth)"))
426
+			{
427
+				msrp_reply("401", "Authorization Required",
428
+						"$var(wauth)");
429
+			} else {
430
+				msrp_reply("500", "Server Error");
431
+			}
432
+			exit;
433
+		}
434
+		$var(cnt) = $var(cnt) + 1;
435
+		pv_printf("$var(sessid)", "s.$(pp).$(var(cnt)).$(RANDOM)");
436
+		$sht(msrp=>$var(sessid)::srcaddr) = $msrp(srcaddr);
437
+		$sht(msrp=>$var(sessid)::srcsock) = $msrp(srcsock);
438
+		# - Use-Path: the MSRP address for server + session id
439
+		$var(UsePath) = "Use-Path: msrp://MY_IP_ADDR:MY_MSRP_PORT/"
440
+				+ $var(sessid) + ";tcp\r\n";
441
+		msrp_reply("200", "OK", "$var(UsePath)");
442
+		exit;
443
+	}
444
+
445
+	if($msrp(method)=="SEND")
446
+	{
447
+		if($msrp(nexthops)>1)
448
+		{
449
+			msrp_reply("200", "Received");
450
+			msrp_relay();
451
+			exit;
452
+		}
453
+		$var(sessid) = $msrp(sessid);
454
+		if($sht(msrp=>$var(sessid)::srcaddr) == $null)
455
+		{
456
+			# one more hop, but we don't have address in htable
457
+			msrp_reply("481", "No Such Session");
458
+			exit;
459
+		}
460
+		msrp_relay_flags("1");
461
+		msrp_set_dst("$sht(msrp=>$var(sessid)::srcaddr)",
462
+				"$sht(msrp=>$var(sessid)::srcsock)");
463
+		msrp_relay();
464
+		exit;
465
+	}
466
+
467
+	msrp_relay();
468
+}
469
+#!endif
... ...
@@ -176,7 +176,7 @@ void wsconn_destroy(void)
176 176
 	}
177 177
 }
178 178
 
179
-int wsconn_add(struct receive_info rcv)
179
+int wsconn_add(struct receive_info rcv, unsigned int sub_protocol)
180 180
 {
181 181
 	int cur_cons, max_cons;
182 182
 	int id = rcv.proto_reserved1;
... ...
@@ -195,6 +195,7 @@ int wsconn_add(struct receive_info rcv)
195 195
 	wsc->id_hash = id_hash;
196 196
 	wsc->state = WS_S_OPEN;
197 197
 	wsc->rcv = rcv;
198
+	wsc->sub_protocol = sub_protocol;
198 199
 
199 200
 	WSCONN_LOCK;
200 201
 	/* Add to WebSocket connection table */
... ...
@@ -50,6 +50,8 @@ typedef struct ws_connection
50 50
 	struct ws_connection *id_next;
51 51
 
52 52
 	struct receive_info rcv;
53
+
54
+	unsigned int sub_protocol;
53 55
 } ws_connection_t;
54 56
 
55 57
 typedef struct
... ...
@@ -73,7 +75,7 @@ extern stat_var *ws_max_concurrent_connections;
73 75
 
74 76
 int wsconn_init(void);
75 77
 void wsconn_destroy(void);
76
-int wsconn_add(struct receive_info rcv);
78
+int wsconn_add(struct receive_info rcv, unsigned int sub_protocol);
77 79
 int wsconn_rm(ws_connection_t *wsc, ws_conn_eventroute_t run_event_route);
78 80
 int wsconn_update(ws_connection_t *wsc);
79 81
 void wsconn_close_now(ws_connection_t *wsc);
... ...
@@ -23,10 +23,12 @@
23 23
 
24 24
 #include <limits.h>
25 25
 #include <unistr.h>
26
+#include "../../events.h"
26 27
 #include "../../receive.h"
27 28
 #include "../../stats.h"
28 29
 #include "../../str.h"
29 30
 #include "../../tcp_conn.h"
31
+#include "../../tcp_read.h"
30 32
 #include "../../tcp_server.h"
31 33
 #include "../../lib/kcore/kstats_wrapper.h"
32 34
 #include "../../lib/kmi/tree.h"
... ...
@@ -34,6 +36,7 @@
34 36
 #include "ws_conn.h"
35 37
 #include "ws_frame.h"
36 38
 #include "ws_mod.h"
39
+#include "ws_handshake.h"
37 40
 
38 41
 /*    0                   1                   2                   3
39 42
       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
... ...
@@ -571,10 +574,36 @@ int ws_frame_receive(void *data)
571 574
 	{
572 575
 	case OPCODE_TEXT_FRAME:
573 576
 	case OPCODE_BINARY_FRAME:
574
-		LM_DBG("Rx SIP message:\n%.*s\n", frame.payload_len,
577
+		if (likely(frame.wsc->sub_protocol == SUB_PROTOCOL_SIP))
578
+		{
579
+			LM_DBG("Rx SIP message:\n%.*s\n", frame.payload_len,
575 580
 				frame.payload_data);
576
-		return receive_msg(frame.payload_data, frame.payload_len,
577
-				tcpinfo->rcv);
581
+			return receive_msg(frame.payload_data,
582
+						frame.payload_len,
583
+						tcpinfo->rcv);
584
+		}
585
+		else if (frame.wsc->sub_protocol == SUB_PROTOCOL_MSRP)
586
+		{
587
+			LM_DBG("Rx MSRP frame:\n%.*s\n", frame.payload_len,
588
+				frame.payload_data);
589
+			if (likely(sr_event_enabled(SREV_TCP_MSRP_FRAME)))
590
+			{
591
+				tcp_event_info_t tev;
592
+				memset(&tev, 0, sizeof(tcp_event_info_t));
593
+				tev.type = SREV_TCP_MSRP_FRAME;
594
+				tev.buf = frame.payload_data;
595
+				tev.len = frame.payload_len;
596
+				tev.rcv = tcpinfo->rcv;
597
+				tev.con = tcpinfo->con;
598
+				return sr_event_exec(SREV_TCP_MSRP_FRAME,
599
+							(void *) &tev);
600
+			}
601
+			else
602
+			{
603
+				LM_ERR("no callback registerd for MSRP\n");
604
+				return -1;
605
+			}
606
+		}
578 607
 
579 608
 	case OPCODE_CLOSE:
580 609
 		return handle_close(&frame);
... ...
@@ -608,12 +637,12 @@ int ws_frame_transmit(void *data)
608 637
 	frame.payload_data = wsev->buf;
609 638
 	frame.wsc = wsconn_get(wsev->id);
610 639
 
611
-	LM_DBG("Tx SIP message:\n%.*s\n", frame.payload_len,
640
+	LM_DBG("Tx message:\n%.*s\n", frame.payload_len,
612 641
 			frame.payload_data);
613 642
 
614 643
 	if (encode_and_send_ws_frame(&frame, CONN_CLOSE_DONT) < 0)
615 644
 	{	
616
-		LM_ERR("sending SIP message\n");
645
+		LM_ERR("sending message\n");
617 646
 		return -1;
618 647
 	}
619 648
 
... ...
@@ -42,10 +42,13 @@
42 42
 
43 43
 #define WS_VERSION		(13)
44 44
 
45
+int ws_sub_protocols = DEFAULT_SUB_PROTOCOLS;
46
+
45 47
 stat_var *ws_failed_handshakes;
46 48
 stat_var *ws_successful_handshakes;
47 49
 
48 50
 static str str_sip = str_init("sip");
51
+static str str_msrp = str_init("msrp");
49 52
 static str str_upgrade = str_init("upgrade");
50 53
 static str str_websocket = str_init("websocket");
51 54
 static str str_ws_guid = str_init("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
... ...
@@ -110,7 +113,7 @@ int ws_handle_handshake(struct sip_msg *msg)
110 113
 {
111 114
 	str key = {0, 0}, headers = {0, 0}, reply_key = {0, 0};
112 115
 	unsigned char sha1[SHA_DIGEST_LENGTH];
113
-	unsigned int hdr_flags = 0;
116
+	unsigned int hdr_flags = 0, sub_protocol = 0;
114 117
 	int version;
115 118
 	struct hdr_field *hdr = msg->headers;
116 119
 	struct tcp_connection *con;
... ...
@@ -188,7 +191,8 @@ int ws_handle_handshake(struct sip_msg *msg)
188 191
 				LM_WARN("%.*s found multiple times\n",
189 192
 					hdr->name.len, hdr->name.s);
190 193
 				ws_send_reply(msg, 400,
191
-						&str_status_bad_request, NULL);
194
+						&str_status_bad_request,
195
+						NULL);
192 196
 				return 0;
193 197
 			}
194 198
 
... ...
@@ -210,6 +214,15 @@ int ws_handle_handshake(struct sip_msg *msg)
210 214
 					hdr->name.len, hdr->name.s,
211 215
 					hdr->body.len, hdr->body.s);
212 216
 				hdr_flags |= SEC_WEBSOCKET_PROTOCOL;
217
+				sub_protocol |= SUB_PROTOCOL_SIP;
218
+			}
219
+			if (str_search(&hdr->body, &str_msrp) != NULL)
220
+			{
221
+				LM_DBG("found %.*s: %.*s\n",
222
+					hdr->name.len, hdr->name.s,
223
+					hdr->body.len, hdr->body.s);
224
+				hdr_flags |= SEC_WEBSOCKET_PROTOCOL;
225
+				sub_protocol |= SUB_PROTOCOL_MSRP;
213 226
 			}
214 227
 		}
215 228
 		/* Decode and validate Sec-WebSocket-Version */
... ...
@@ -222,7 +235,8 @@ int ws_handle_handshake(struct sip_msg *msg)
222 235
 				LM_WARN("%.*s found multiple times\n",
223 236
 					hdr->name.len, hdr->name.s);
224 237
 				ws_send_reply(msg, 400,
225
-						&str_status_bad_request, NULL);
238
+						&str_status_bad_request,
239
+						NULL);
226 240
 				return 0;
227 241
 			}
228 242
 
... ...
@@ -254,20 +268,37 @@ int ws_handle_handshake(struct sip_msg *msg)
254 268
 	}
255 269
 
256 270
 	/* Final check that all required headers/values were found */
257
-	if (hdr_flags != REQUIRED_HEADERS)
271
+	sub_protocol &= ws_sub_protocols;
272
+	if (hdr_flags != REQUIRED_HEADERS || sub_protocol == 0)
258 273
 	{
274
+
259 275
 		LM_WARN("required headers not present\n");
260 276
 		headers.s = headers_buf;
261
-		headers.len = snprintf(headers.s, HDR_BUF_LEN,
262
-					"%.*s: %.*s\r\n"
263
-					"%.*s: %d\r\n",
277
+		headers.len = 0;
278
+
279
+		if (ws_sub_protocols & SUB_PROTOCOL_SIP)
280
+			headers.len += snprintf(headers.s + headers.len,
281
+						HDR_BUF_LEN - headers.len,
282
+						"%.*s: %.*s\r\n",
264 283
 					str_hdr_sec_websocket_protocol.len,
265 284
 					str_hdr_sec_websocket_protocol.s,
266
-					str_sip.len, str_sip.s,
285
+					str_sip.len, str_sip.s);
286
+
287
+		if (ws_sub_protocols & SUB_PROTOCOL_MSRP)
288
+			headers.len += snprintf(headers.s + headers.len,
289
+						HDR_BUF_LEN - headers.len,
290
+						"%.*s: %.*s\r\n",
291
+					str_hdr_sec_websocket_protocol.len,
292
+					str_hdr_sec_websocket_protocol.s,
293
+					str_msrp.len, str_msrp.s);
294
+
295
+		headers.len += snprintf(headers.s + headers.len,
296
+					HDR_BUF_LEN - headers.len,
297
+					"%.*s: %d\r\n",
267 298
 					str_hdr_sec_websocket_version.len,
268 299
 					str_hdr_sec_websocket_version.s,
269 300
 					WS_VERSION);
270
-		ws_send_reply(msg, 400, &str_status_bad_request, NULL);
301
+		ws_send_reply(msg, 400, &str_status_bad_request, &headers);
271 302
 		return 0;
272 303
 	}
273 304
 
... ...
@@ -292,7 +323,7 @@ int ws_handle_handshake(struct sip_msg *msg)
292 323
 				base64_enc_len(SHA_DIGEST_LENGTH));
293 324
 
294 325
 	/* Add the connection to the WebSocket connection table */
295
-	wsconn_add(msg->rcv);
326
+	wsconn_add(msg->rcv, sub_protocol);
296 327
 
297 328
 	/* Make sure Kamailio core sends future messages on this connection
298 329
 	   directly to this module */
... ...
@@ -304,23 +335,38 @@ int ws_handle_handshake(struct sip_msg *msg)
304 335
 	/* Now Kamailio is ready to receive WebSocket frames build and send a
305 336
 	   101 reply */
306 337
 	headers.s = headers_buf;
307
-	headers.len = snprintf(headers.s, HDR_BUF_LEN,
308
-			"%.*s: %.*s\r\n"
309
-			"%.*s: %.*s\r\n"
310
-			"%.*s: %.*s\r\n"
311
-			"%.*s: %.*s\r\n",
312
-			str_hdr_upgrade.len, str_hdr_upgrade.s,
313
-			str_websocket.len, str_websocket.s,
314
-			str_hdr_connection.len, str_hdr_connection.s,
315
-			str_upgrade.len, str_upgrade.s,
316
-			str_hdr_sec_websocket_accept.len,
317
-			str_hdr_sec_websocket_accept.s, reply_key.len,
318
-			reply_key.s, str_hdr_sec_websocket_protocol.len,
319
-			str_hdr_sec_websocket_protocol.s, str_sip.len,
320
-			str_sip.s);
338
+	headers.len = 0;
339
+
340
+	if (sub_protocol & SUB_PROTOCOL_SIP)
341
+		headers.len += snprintf(headers.s + headers.len,
342
+					HDR_BUF_LEN - headers.len,
343
+					"%.*s: %.*s\r\n",
344
+					str_hdr_sec_websocket_protocol.len,
345
+					str_hdr_sec_websocket_protocol.s,
346
+					str_sip.len, str_sip.s);
347
+	else if (sub_protocol & SUB_PROTOCOL_MSRP)
348
+		headers.len += snprintf(headers.s + headers.len,
349
+					HDR_BUF_LEN - headers.len,
350
+					"%.*s: %.*s\r\n",
351
+					str_hdr_sec_websocket_protocol.len,
352
+					str_hdr_sec_websocket_protocol.s,
353
+					str_msrp.len, str_msrp.s);
354
+
355
+	headers.len += snprintf(headers.s + headers.len,
356
+				HDR_BUF_LEN - headers.len,
357
+				"%.*s: %.*s\r\n"
358
+				"%.*s: %.*s\r\n"
359
+				"%.*s: %.*s\r\n",
360
+				str_hdr_upgrade.len, str_hdr_upgrade.s,
361
+				str_websocket.len, str_websocket.s,
362
+				str_hdr_connection.len, str_hdr_connection.s,
363
+				str_upgrade.len, str_upgrade.s,
364
+				str_hdr_sec_websocket_accept.len,
365
+				str_hdr_sec_websocket_accept.s, reply_key.len,
366
+				reply_key.s);
321 367
 	msg->rpl_send_flags.f &= ~SND_F_CON_CLOSE;
322
-	if (ws_send_reply(msg, 101,
323
-				&str_status_switching_protocols, &headers) < 0)
368
+	if (ws_send_reply(msg, 101, &str_status_switching_protocols,
369
+				&headers) < 0)
324 370
 	{
325 371
 		if ((wsc = wsconn_get(msg->rcv.proto_reserved1)) != NULL)
326 372
 			wsconn_rm(wsc, WSCONN_EVENTROUTE_NO);
... ...
@@ -27,8 +27,17 @@
27 27
 #include "../../sr_module.h"
28 28
 #include "../../parser/msg_parser.h"
29 29
 
30
-stat_var *ws_failed_handshakes;
31
-stat_var *ws_successful_handshakes;
30
+enum
31
+{
32
+	SUB_PROTOCOL_SIP  = (1 << 0),
33
+	SUB_PROTOCOL_MSRP = (1 << 1)
34
+};
35
+#define DEFAULT_SUB_PROTOCOLS	(SUB_PROTOCOL_SIP | SUB_PROTOCOL_MSRP)
36
+#define SUB_PROTOCOL_ALL	(SUB_PROTOCOL_SIP | SUB_PROTOCOL_MSRP)
37
+extern int ws_sub_protocols;
38
+
39
+extern stat_var *ws_failed_handshakes;
40
+extern stat_var *ws_successful_handshakes;
32 41
 
33 42
 int ws_handle_handshake(struct sip_msg *msg);
34 43
 struct mi_root *ws_mi_disable(struct mi_root *cmd, void *param);
... ...
@@ -72,6 +72,9 @@ static param_export_t params[]=
72 72
 	{ "keepalive_timeout",		INT_PARAM, &ws_keepalive_timeout },
73 73
 	{ "ping_application_data",	STR_PARAM, &ws_ping_application_data.s},
74 74
 
75
+	/* ws_handshake.c */
76
+	{ "sub_protocols",		INT_PARAM, &ws_sub_protocols},
77
+
75 78
 	/* ws_mod.c */
76 79
 	{ "keepalive_interval",		INT_PARAM, &ws_keepalive_interval },
77 80
 	{ "keepalive_processes",	INT_PARAM, &ws_keepalive_processes },
... ...
@@ -214,6 +217,22 @@ static int mod_init(void)
214 217
 		register_sync_timers(ws_keepalive_processes);
215 218
 	}
216 219
 
220
+	if (ws_sub_protocols & SUB_PROTOCOL_MSRP
221
+		&& !sr_event_enabled(SREV_TCP_MSRP_FRAME))
222
+		ws_sub_protocols &= ~SUB_PROTOCOL_MSRP;
223
+
224
+	if ((ws_sub_protocols & SUB_PROTOCOL_ALL) == 0)
225
+	{
226
+		LM_ERR("no sub-protocols enabled\n");
227
+		goto error;
228
+	}
229
+
230
+	if ((ws_sub_protocols | SUB_PROTOCOL_ALL) != SUB_PROTOCOL_ALL)
231
+	{
232
+		LM_ERR("unrecognised sub-protocols enabled\n");
233
+		goto error;
234
+	}
235
+
217 236
 	return 0;
218 237
 
219 238
 error: