Home » Eclipse Projects » Eclipse Titan » Chat with STOMP over WebSocket in Titan part 1(The naive implementation )
Chat with STOMP over WebSocket in Titan part 1 [message #1771837] |
Thu, 31 August 2017 08:32 |
|
Dear all,
we have discussed earlier websocket support in Titan ( https://www.eclipse.org/forums/index.php/t/1087423/)
and we have also looked into a simple messaging application, STOMP ( https://www.eclipse.org/forums/index.php/t/1088082/).
Now, often messaging applications will take advantage of the bidirectional communication channel offered by websockets, so there's an entire family of protocols that are registered
as doing that, see https://www.iana.org/assignments/websocket/websocket.xml ; they are called websocket subprotocols, although strictly speaking they are sitting atop websockets layerwise.
To demonstrate such an application, we will use again the apache apollo message broker as detailed in the STOMP post;
when starting the message broker, the following is displayed:
_____ .__ .__
/ _ \ ______ ____ | | | | ____
/ /_\ \\____ \ / _ \| | | | / _ \
/ | \ |_> > <_> ) |_| |_( <_> )
\____|__ / __/ \____/|____/____/\____/
\/|__| Apache Apollo (1.7.1)
Loading configuration file '/var/lib/mybroker/etc/apollo.xml'.
INFO | OS : Linux 4.4.0-83-generic (Ubuntu 16.04.2 LTS)
INFO | JVM : OpenJDK 64-Bit Server VM 1.8.0_131 (Oracle Corporation)
INFO | Apollo : 1.7.1 (at: /home/ntaf/apache-apollo-1.7.1)
INFO | OS is restricting the open file limit to: 100000
INFO | Accepting connections at: tcp://0.0.0.0:61613
INFO | Accepting connections at: tls://0.0.0.0:61614
INFO | Starting store: leveldb store at /var/lib/mybroker/data
INFO | Accepting connections at: ws://0.0.0.0:61623/
INFO | Accepting connections at: wss://0.0.0.0:61624/
INFO | virtual host startup is waiting on store startup
INFO | virtual host startup is no longer waiting. It waited a total of 1 seconds.
INFO | Administration interface available at: https://127.0.0.1:61681/
INFO | Administration interface available at: http://127.0.0.1:61680/
We can see that a websocket application is listening on port 61623, and the secure websocket companion on port 61624.
Although from this alone it is not apparent that STOMP will be supported here, but the message broker documentation tells us that this is the case.
So let's design some code that will permit us to talk with the STOMP application hidden behind the websocket.
First, we need to request a connection upgrade on HTTP 1.1 to websocket. In the upgrade request, we can specify the websocket sub-protocol to be used.
If the broker does not support that particular subprotocol, it will reject the connection upgrade.
Once the connection upgrade is granted, we can start messaging using websocket frames with STOMP payload.
Here's the message flow on websocket level:
WEBSOCKET MESSAGE FLOW
+ HTTP 1.1 Upgrade +
+-------------------------->+
| |
| HTTP 1.1 Switching protocols
+<--------------------------+
| |
| Websocket binary frame|
+-------------------------->+
| |
| : |
| : |
| |
| Websocket binary frame |
+<--------------------------+
| Websocket conn close |
+-------------------------->+
and on STOMP level:
STOMP MESSAGE FLOW
+ CONNECT +
+-------------------------->+
| |
| CONNECTED |
+<--------------------------+
| |
| SUBSCRIBE |
+-------------------------->+
| |
| SEND |
+-------------------------->+
| |
| MESSAGE |
+<--------------------------+
| |
+ +
As in the pure websocket example, we will use an IPL4 test port in TCP mode to connect to the message broker.
To make this demo more spectacular, let's use a chat demo supplied together with the message broker.
Start a browser with websocket support and navigate to:
file:///home/userxxx/Apache_Apollo/examples/stomp/websocket/index.html
or wherever the chat example you have downloaded is deposited on your machine,
and login with the following parameters:
Websocket URL : ws://localhost:61623
User, Password as configured (default login id and password is "admin" and "password"- don't forget to change this in aproduction environment).
Destination: /topic/chat.general
Now the code executed in the browser will connect to the message broker as a chat client; in the Debug Log window in the right the message exchange can be monitored.
All connected and subscribed clients will be dispatched the messages sent to this topic.The TTCN-3 code will connect and subscribe in a similar manner.
Here's the code that has been used:
module StompWS {
modulepar {
charstring tsp_hostname:="localhost"
charstring tsp_url:="/"
boolean tsp_use_ssl:=false
}
import from IPL4asp_Types all;
import from IPL4asp_PortType all;
import from HTTP_Types all;
import from HTTP_MessageLen all;
import from WebSocket_Types all;
import from STOMP_Types all;
//*************************************************************************
function f_HTTPMessage_len( in octetstring stream, inout ro_integer args) return integer
//*************************************************************************
{
return ef_HTTPMessage_len(stream) ;
}
//*************************************************************************
function f_HTTPMessage_len_ws( in octetstring stream, inout ro_integer args) return integer
//*************************************************************************
{
return f_WebSocket_calc_length(stream) ;
}
type component GeneralComp
{
port IPL4asp_PT p;
timer t;
var IPL4asp_Types.Result c_res:= { errorCode := omit, connId := omit, os_error_code:=omit, os_error_text:= omit };
}
type component SystemComp
{
port IPL4asp_PT p;
}
//-----------------------------------STOMP Templates---------------------------------------------------------
template STOMPFrame t_connect:=
{
command:=CONNECT,
headers:={ {header_name:="accept-version", header_value:="1.2"}, {header_name:="login", header_value:="admin"},
{header_name:="passcode", header_value:="password"},{header_name:="heart-beat", header_value:="10000,10000"} },
payload:=omit
}
template STOMPFrame t_connected:=
{
command:=CONNECTED,
headers:=?,
payload:=omit
}
template STOMPFrame t_subscribe:=
{
command:=SUBSCRIBE,
headers:={ {header_name:="id", header_value:="sub-0"},{header_name:="destination", header_value:="/topic/chat.general"}},
payload:=omit
}
template STOMPFrame t_send(in octetstring payload):=
{
command:=SEND,
headers:={ {header_name:="destination", header_value:="/topic/chat.general"} },//, {header_name:="content-length", header_value:=int2unichar(lengthof(payload))}},
payload:=payload
}
template STOMPFrame t_message:=
{
command:=MESSAGE,
headers:={ {header_name:="subscription", header_value:="sub-0"}, {header_name:="message-id", header_value:=?},{header_name:="destination", header_value:="/topic/chat.general"}, {header_name:="content-length", header_value:=?}},
payload:=?
}
template STOMPFrame t_disconnect:=
{
command:=DISCONNECT,
headers:={},
payload:=omit
}
//-----------------------------------WebSocket Templates---------------------------------------------------------
template WebSocket_PDU t_websocket_bin (in charstring payload) := {
fin_bit :='1'B,
rsv1_bit :='0'B,
rsv2_bit :='0'B,
rsv3_bit :='0'B,
opcode:= Binary_frame,
mask_bit := '1'B,
payload_len:=lengthof(char2oct(payload)),
masking_key :='00000000'O,
payload_data := {data := char2oct(payload)}
}
template WebSocket_PDU t_websocket_ping := {
fin_bit :='1'B,
rsv1_bit :='0'B,
rsv2_bit :='0'B,
rsv3_bit :='0'B,
opcode:= Binary_frame,
mask_bit := '1'B,
payload_len:=1,
masking_key :='00000000'O,
payload_data := {data := '0A'O}
}
template WebSocket_PDU t_websocket_pong := {
fin_bit :='1'B,
rsv1_bit :='0'B,
rsv2_bit :='0'B,
rsv3_bit :='0'B,
opcode:= Binary_frame,
mask_bit := '0'B,
payload_len:=1,
masking_key :=omit,
payload_data := {data := '0A'O}
}
template WebSocket_PDU t_websocket_conn_close_s := {
fin_bit :='1'B,
rsv1_bit :='0'B,
rsv2_bit :='0'B,
rsv3_bit :='0'B,
opcode:= Connection_Close,
mask_bit := '1'B,
payload_len:=2,
masking_key :='00000000'O,
payload_data := {close_data := {
status_code:=1000,
data:=omit
}}
}
template WebSocket_PDU t_websocket_conn_close_r := {
fin_bit :='1'B,
rsv1_bit :='0'B,
rsv2_bit :='0'B,
rsv3_bit :='0'B,
opcode:= Connection_Close,
mask_bit := '0'B,
payload_len:=0,
masking_key :=omit,
payload_data := omit
}
template ASP_Send t_data1(in integer p_id ) :={
connId:=p_id,
proto:=omit,
msg:=ef_HTTP_Encode(valueof(t_http_message("")))
}
template ASP_Send t_data2(in integer p_id, in octetstring pdu ) :={
connId:=p_id,
proto:=omit,
msg:=pdu
}
template HTTP_Message t_http_message (in charstring payload) := {
msg := {{request_line :={GET,tsp_url,1,1}},{
host := tsp_hostname&":61623", //FIXME!!
connection := {{"Upgrade"}},
upgrade := {{"websocket"}},
origin := "null",
sec_websocket_key := "dGhlIHNhbXBsZSBub25jZQ==",
sec_websocket_version := "13",
sec_websocket_protocol := "v12.stomp",
sec_websocket_extensions:= "permessage-deflate"
},omit}
}with { optional "implicit omit" }
testcase TC_websocket() runs on GeneralComp system SystemComp {
var ASP_RecvFrom v_ASP_RecvFrom
var HTTP_Message v_HTTP_Message
var WebSocket_PDU v_WebSocket_PDU
var STOMPFrame v_stomp
var octetstring v_ws_pdu:=''O, v_stomp_pdu;
var IPL4asp_Types.Result vl_result;
var integer v_cid, v_ret,v_ret_s;
map(self:p, system:p);
vl_result := c_res;
if(tsp_use_ssl) {
vl_result :=f_IPL4_connect(p, tsp_hostname, 61624,"",0, -1, {ssl := {} }, {} ); //61624-wss
}
else {
vl_result :=f_IPL4_connect( p, tsp_hostname, 61623,"",0, -1, {tcp := {} }, {} ); //61623-ws
}
log("connect result",vl_result);
if (not(ispresent(vl_result.connId)))
{
log("Could not connect TCP/TLS");
stop;
}
v_cid:=vl_result.connId;
//register message length function of HTTP with IPL4:
//*************************************************************************
var f_IPL4_getMsgLen getMsg_Func := refers(f_HTTPMessage_len);
f_IPL4_setGetMsgLen(p,v_cid, getMsg_Func, {});
//*************************************************************************
p.send(t_data1(v_cid));
t.start(1.0)
alt
{
[] p.receive(ASP_RecvFrom:?) -> value v_ASP_RecvFrom {
v_HTTP_Message:= ef_HTTP_Decode(v_ASP_RecvFrom.msg);
log(v_HTTP_Message);
}
[] t.timeout{log("Bye")}
}
//*************************************************************************
//From here on, we move to websocket
//*************************************************************************
//register message length function of websocket with IPL4:
//*************************************************************************
getMsg_Func := refers(f_HTTPMessage_len_ws);
f_IPL4_setGetMsgLen(p,v_cid, getMsg_Func, {});
//*************************************************************************
//send CONNECT
v_ret_s:=f_STOMP_enc(valueof(t_connect),v_stomp_pdu)
f_WebSocket_Encode(valueof(t_websocket_bin(oct2char(v_stomp_pdu))), v_ws_pdu)
p.send(t_data2(v_cid, v_ws_pdu));
log("STOMP >>>>>>>>>", t_connect)
t.start(1.0)
alt
{
[] p.receive(ASP_RecvFrom:?) -> value v_ASP_RecvFrom {
v_ret:= f_WebSocket_Decode(v_ASP_RecvFrom.msg,v_WebSocket_PDU )
log("Decode return result : ",v_ret)
log("Websocket response received : ",v_WebSocket_PDU)
v_ret_s:=f_STOMP_dec(v_WebSocket_PDU.payload_data.data, v_stomp)
log("STOMP response received : ",v_stomp);
if(v_stomp.command!=CONNECTED) { log("Could not connect to STOMP"); stop; }
}
[] t.timeout{log("Bye")}
}
//send SUBSCRIBE
v_ret_s:=f_STOMP_enc(valueof(t_subscribe),v_stomp_pdu)
f_WebSocket_Encode(valueof(t_websocket_bin(oct2char(v_stomp_pdu))), v_ws_pdu)
p.send(t_data2(v_cid, v_ws_pdu));
//send SEND
v_ret_s:=f_STOMP_enc(valueof(t_send(char2oct("Haha, charade you are"))),v_stomp_pdu)
f_WebSocket_Encode(valueof(t_websocket_bin(oct2char(v_stomp_pdu))), v_ws_pdu)
p.send(t_data2(v_cid, v_ws_pdu));
t.start(1.0)
alt
{
[] p.receive(ASP_RecvFrom:?) -> value v_ASP_RecvFrom {
v_ret:= f_WebSocket_Decode(v_ASP_RecvFrom.msg,v_WebSocket_PDU )
log("Decode return result : ",v_ret)
log("Websocket response received : ",v_WebSocket_PDU)
if(match(v_WebSocket_PDU,t_websocket_pong ))
{
f_WebSocket_Encode(valueof(t_websocket_ping), v_ws_pdu)
p.send(t_data2(v_cid, v_ws_pdu)); //send PING
}
else{
v_ret_s:=f_STOMP_dec(v_WebSocket_PDU.payload_data.data,v_stomp)
log("STOMP response received : ",v_stomp);
}
}
[] t.timeout{log("Bye")}
}
//send conn_close
f_WebSocket_Encode(valueof(t_websocket_conn_close_s), v_ws_pdu);
p.send(t_data2(v_cid, v_ws_pdu));
t.start(1.0)
alt
{
[] p.receive(ASP_RecvFrom:?) -> value v_ASP_RecvFrom {
v_ret:= f_WebSocket_Decode(v_ASP_RecvFrom.msg,v_WebSocket_PDU )
log("Decode return result : ",v_ret)
log("Websocket response received : ",v_WebSocket_PDU)
}
[] t.timeout{log("Bye")}
}
//close TCP/TLS connection
vl_result := f_IPL4_close(p, v_cid)
log("close result",vl_result)
setverdict(pass);
}
control{
execute(TC_websocket());
}
}//endmodule
and the execution log:
08:23:38.305958 - TTCN-3 Main Test Component started on esekilxxen1842. Version: CRL 113 200/6 R2A.
08:23:38.306135 - TTCN Logger v2.2 options: TimeStampFormat:=Time; LogEntityName:=No; LogEventTypes:=No; SourceInfoFormat:=Single; *.FileMask:=LOG_ALL; *.ConsoleMask:=ERROR | USER; LogFileSize:=0; LogFileNumber:=1; DiskFullAction:=Error
08:23:38.306213 - Connected to MC.
08:23:38.313513 - Executing control part of module StompWS.
08:23:38.313565 StompWS.ttcn:381 Execution of control part in module StompWS started.
08:23:38.313616 StompWS.ttcn:229 Test case TC_websocket started.
08:23:38.313652 StompWS.ttcn:229 Initializing variables, timers and ports of component type StompWS.GeneralComp inside testcase TC_websocket.
08:23:38.313782 StompWS.ttcn:229 Port p was started.
08:23:38.313812 StompWS.ttcn:229 Component type StompWS.GeneralComp was initialized.
08:23:38.313870 StompWS.ttcn:242 Mapping port mtc:p to system:p.
08:23:38.313975 StompWS.ttcn:242 Port p was mapped to system:p.
08:23:38.314067 StompWS.ttcn:242 Map operation of mtc:p to system:p finished.
08:23:38.314119 StompWS.ttcn:247 entering f__IPL4__PROVIDER__connect: :0 -> localhost:61624 / SSL
08:23:38.403822 StompWS.ttcn:254 connect result{ errorCode := omit, connId := 1, os_error_code := omit, os_error_text := omit }
08:23:38.404343 StompWS.ttcn:270 Sent on p to system @IPL4asp_Types.ASP_Send : { connId := 1, proto := omit, msg := '474554202F20485454502F312E310D0A436F6E6E656374696F6E3A20557067726164650D0A486F73743A206C6F63616C686F73743A36313632330D0A4F726967696E3A206E756C6C0D0A5365632D576562736F636B65742D457874656E73696F6E733A207065726D6573736167652D6465666C6174650D0A5365632D576562736F636B65742D4B65793A206447686C49484E68625842735A5342756232356A5A513D3D0D0A5365632D576562736F636B65742D50726F746F636F6C3A207631322E73746F6D700D0A5365632D576562736F636B65742D56657273696F6E3A2031330D0A557067726164653A20776562736F636B65740D0A0D0A'O ("GET / HTTP/1.1\r\nConnection: Upgrade\r\nHost: localhost:61623\r\nOrigin: null\r\nSec-Websocket-Extensions: permessage-deflate\r\nSec-Websocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-Websocket-Protocol: v12.stomp\r\nSec-Websocket-Version: 13\r\nUpgrade: websocket\r\n\r\n") }
08:23:38.404525 StompWS.ttcn:271 Start timer t: 1 s
08:23:38.405946 StompWS.ttcn:273 Message enqueued on p from system @IPL4asp_Types.ASP_RecvFrom : { connId := 1, remName := "127.0.0.1", remPort := 61624, locName := "127.0.0.1", locPort := 39265, proto := { ssl := { } }, userData := 0, msg := '485454502F312E312031303120537769746368696E672050726F746F636F6C730D0A557067726164653A20576562536F636B65740D0A436F6E6E656374696F6E3A20557067726164650D0A5365632D576562536F636B65742D4163636570743A20733370504C4D426954786151396B59477A7A685A52624B2B784F6F3D0D0A5365632D576562536F636B65742D50726F746F636F6C3A207631322E73746F6D700D0A0D0A'O ("HTTP/1.1 101 Switching Protocols\r\nUpgrade: WebSocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\nSec-WebSocket-Protocol: v12.stomp\r\n\r\n") } id 1
08:23:38.406009 StompWS.ttcn:275 Receive operation on port p succeeded, message from system(): @IPL4asp_Types.ASP_RecvFrom : { connId := 1, remName := "127.0.0.1", remPort := 61624, locName := "127.0.0.1", locPort := 39265, proto := { ssl := { } }, userData := 0, msg := '485454502F312E312031303120537769746368696E672050726F746F636F6C730D0A557067726164653A20576562536F636B65740D0A436F6E6E656374696F6E3A20557067726164650D0A5365632D576562536F636B65742D4163636570743A20733370504C4D426954786151396B59477A7A685A52624B2B784F6F3D0D0A5365632D576562536F636B65742D50726F746F636F6C3A207631322E73746F6D700D0A0D0A'O ("HTTP/1.1 101 Switching Protocols\r\nUpgrade: WebSocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\nSec-WebSocket-Protocol: v12.stomp\r\n\r\n") } id 1
08:23:38.406033 StompWS.ttcn:275 Message with id 1 was extracted from the queue of p.
08:23:38.406180 StompWS.ttcn:277 { msg := { start_line := { status_line := { version_major := 1, version_minor := 1, status_code := 101, reason_phrase := "Switching Protocols" } }, headers := { accept := omit, accept_charset := omit, accept_encoding := omit, accept_language := omit, accept_ranges := omit, age := omit, allow := omit, authorization := omit, cache_control := omit, connection := { { "Upgrade" } }, content_disposition := omit, content_encoding := omit, content_language := omit, content_length := omit, content_location := omit, content_range := omit, content_type := omit, cookie := omit, dasl := omit, dav := omit, date := omit, depth := omit, destination := omit, etag := omit, expect := omit, expires := omit, forwarded := omit, fRom := omit, host := omit, http2_settings := omit, iF := omit, if_match := omit, if_modified_since := omit, if_none_match := omit, if_range := omit, if_schedule_tag_match := omit, if_unmodified_since := omit, last_modified := omit, location := omit, lock_token := omit, max_forwards := omit, mime_version := omit, ordering_type := omit, origin := omit, overwrite := omit, position := omit, pragma := omit, prefer := omit, preference_applied := omit, proxy_authenticate := omit, proxy_authorization := omit, range := omit, referer := omit, retry_after := omit, schedule_reply := omit, schedule_tag := omit, sec_websocket_accept := "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", sec_websocket_extensions := omit, sec_websocket_key := omit, sec_websocket_protocol := "v12.stomp", sec_websocket_version := omit, server := omit, set_cookie := omit, slug := omit, strict_transport_security := omit, tE := omit, tImeout := omit, trailer := omit, transfer_encoding := omit, upgrade := { { "WebSocket" } }, user_agent := omit, vary := omit, via := omit, www_authenticate := omit, warning := omit, undefined_header_list := omit }, body := omit } }
08:23:38.406530 StompWS.ttcn:300 Sent on p to system @IPL4asp_Types.ASP_Send : { connId := 1, proto := omit, msg := '82D2FE9AD8EDBDD596A3BBD98CE79FF9BB888EEEF59B9BE8AB8491F4E2DCD0A8D28191FDB183C4FBBC8097F4D29D9FE9AB8E91FEBDD78EFBAB9E89F5AA89F4F2BD8C8CEEF58F9BFBACD7CFAAE8DDCEB6E9DDCEAAE8E7F49A'O }
08:23:38.406715 StompWS.ttcn:302 STOMP >>>>>>>>>{ command := CONNECT (10), headers := { { header_name := "accept-version", header_value := "1.2" }, { header_name := "login", header_value := "admin" }, { header_name := "passcode", header_value := "password" }, { header_name := "heart-beat", header_value := "10000,10000" } }, payload := omit }
08:23:38.406815 StompWS.ttcn:303 Warning: Re-starting timer t, which is already active (running or expired).
08:23:38.406840 StompWS.ttcn:303 Start timer t: 1 s
08:23:38.409904 StompWS.ttcn:305 Message enqueued on p from system @IPL4asp_Types.ASP_RecvFrom : { connId := 1, remName := "127.0.0.1", remPort := 61624, locName := "127.0.0.1", locPort := 39265, proto := { ssl := { } }, userData := 0, msg := '827C434F4E4E45435445440A76657273696F6E3A312E320A7365727665723A6170616368652D61706F6C6C6F2F312E372E310A686F73742D69643A6D7962726F6B65720A73657373696F6E3A6D7962726F6B65722D38620A68656172742D626561743A3130302C31303030300A757365722D69643A61646D696E0A0A000A'O } id 2
08:23:38.409945 StompWS.ttcn:307 Receive operation on port p succeeded, message from system(): @IPL4asp_Types.ASP_RecvFrom : { connId := 1, remName := "127.0.0.1", remPort := 61624, locName := "127.0.0.1", locPort := 39265, proto := { ssl := { } }, userData := 0, msg := '827C434F4E4E45435445440A76657273696F6E3A312E320A7365727665723A6170616368652D61706F6C6C6F2F312E372E310A686F73742D69643A6D7962726F6B65720A73657373696F6E3A6D7962726F6B65722D38620A68656172742D626561743A3130302C31303030300A757365722D69643A61646D696E0A0A000A'O } id 2
08:23:38.409966 StompWS.ttcn:307 Message with id 2 was extracted from the queue of p.
08:23:38.409983 StompWS.ttcn:309 Decode return result : 0
08:23:38.410031 StompWS.ttcn:310 Websocket response received : { fin_bit := '1'B, rsv1_bit := '0'B, rsv2_bit := '0'B, rsv3_bit := '0'B, opcode := Binary_frame (2), mask_bit := '0'B, payload_len := 124, masking_key := omit, payload_data := { data := '434F4E4E45435445440A76657273696F6E3A312E320A7365727665723A6170616368652D61706F6C6C6F2F312E372E310A686F73742D69643A6D7962726F6B65720A73657373696F6E3A6D7962726F6B65722D38620A68656172742D626561743A3130302C31303030300A757365722D69643A61646D696E0A0A000A'O } }
08:23:38.410229 StompWS.ttcn:312 STOMP response received : { command := CONNECTED (9), headers := { { header_name := "version", header_value := "1.2" }, { header_name := "server", header_value := "apache-apollo/1.7.1" }, { header_name := "host-id", header_value := "mybroker" }, { header_name := "session", header_value := "mybroker-8b" }, { header_name := "heart-beat", header_value := "100,10000" }, { header_name := "user-id", header_value := "admin" } }, payload := omit }
08:23:38.410340 StompWS.ttcn:323 Sent on p to system @IPL4asp_Types.ASP_Send : { connId := 1, proto := omit, msg := '82B5FB0531E2A85073B1B85778A0BE0F5886C1764480D6353B869E76458B9564458B946B0BCD8F6A418B982A528A9A711F859E6B54909A693BE8FB'O }
08:23:38.410500 StompWS.ttcn:328 Sent on p to system @IPL4asp_Types.ASP_Send : { connId := 1, proto := omit, msg := '82CE072A99CF546FD78B0D4EFCBC7343F7AE7343F6A13D05EDA07743FAE06442F8BB294DFCA16258F8A30D49F6A1734FF7BB2A46FCA1605EF1F5351B93C54F4BF1AE2B0AFAA76658F8AB620AE0A0720AF8BD622A'O }
08:23:38.410573 StompWS.ttcn:330 Warning: Re-starting timer t, which is already active (running or expired).
08:23:38.410590 StompWS.ttcn:330 Start timer t: 1 s
08:23:38.451950 StompWS.ttcn:332 Message enqueued on p from system @IPL4asp_Types.ASP_RecvFrom : { connId := 1, remName := "127.0.0.1", remPort := 61624, locName := "127.0.0.1", locPort := 39265, proto := { ssl := { } }, userData := 0, msg := '827E00834D4553534147450A737562736372697074696F6E3A7375622D300A61636B3A320A6D6573736167652D69643A6D7962726F6B65722D3862310A64657374696E6174696F6E3A2F746F7069632F636861742E67656E6572616C0A636F6E74656E742D6C656E6774683A32310A0A486168612C206368617261646520796F7520617265000A'O } id 3
08:23:38.452043 StompWS.ttcn:334 Receive operation on port p succeeded, message from system(): @IPL4asp_Types.ASP_RecvFrom : { connId := 1, remName := "127.0.0.1", remPort := 61624, locName := "127.0.0.1", locPort := 39265, proto := { ssl := { } }, userData := 0, msg := '827E00834D4553534147450A737562736372697074696F6E3A7375622D300A61636B3A320A6D6573736167652D69643A6D7962726F6B65722D3862310A64657374696E6174696F6E3A2F746F7069632F636861742E67656E6572616C0A636F6E74656E742D6C656E6774683A32310A0A486168612C206368617261646520796F7520617265000A'O } id 3
08:23:38.452066 StompWS.ttcn:334 Message with id 3 was extracted from the queue of p.
08:23:38.452087 StompWS.ttcn:336 Decode return result : 0
08:23:38.452115 StompWS.ttcn:337 Websocket response received : { fin_bit := '1'B, rsv1_bit := '0'B, rsv2_bit := '0'B, rsv3_bit := '0'B, opcode := Binary_frame (2), mask_bit := '0'B, payload_len := 131, masking_key := omit, payload_data := { data := '4D4553534147450A737562736372697074696F6E3A7375622D300A61636B3A320A6D6573736167652D69643A6D7962726F6B65722D3862310A64657374696E6174696F6E3A2F746F7069632F636861742E67656E6572616C0A636F6E74656E742D6C656E6774683A32310A0A486168612C206368617261646520796F7520617265000A'O } }
08:23:38.452341 StompWS.ttcn:347 STOMP response received : { command := MESSAGE (12), headers := { { header_name := "subscription", header_value := "sub-0" }, { header_name := "ack", header_value := "2" }, { header_name := "message-id", header_value := "mybroker-8b1" }, { header_name := "destination", header_value := "/topic/chat.general" }, { header_name := "content-length", header_value := "21" } }, payload := '486168612C206368617261646520796F7520617265'O ("Haha, charade you are") }
08:23:38.452445 StompWS.ttcn:359 Sent on p to system @IPL4asp_Types.ASP_Send : { connId := 1, proto := omit, msg := '88822C87CDC72F6F'O }
08:23:38.452524 StompWS.ttcn:360 Warning: Re-starting timer t, which is already active (running or expired).
08:23:38.452544 StompWS.ttcn:360 Start timer t: 1 s
08:23:38.453291 StompWS.ttcn:362 Message enqueued on p from system @IPL4asp_Types.ASP_RecvFrom : { connId := 1, remName := "127.0.0.1", remPort := 61624, locName := "127.0.0.1", locPort := 39265, proto := { ssl := { } }, userData := 0, msg := '880203E8'O } id 4
08:23:38.453363 StompWS.ttcn:362 Message enqueued on p from system @Socket_API_Definitions.PortEvent : { connClosed := { connId := 1, remName := "127.0.0.1", remPort := 61624, locName := "127.0.0.1", locPort := 39265, proto := { ssl := { } }, userData := 0 } } id 5
08:23:38.453471 StompWS.ttcn:364 Receive operation on port p succeeded, message from system(): @IPL4asp_Types.ASP_RecvFrom : { connId := 1, remName := "127.0.0.1", remPort := 61624, locName := "127.0.0.1", locPort := 39265, proto := { ssl := { } }, userData := 0, msg := '880203E8'O } id 4
08:23:38.453493 StompWS.ttcn:364 Message with id 4 was extracted from the queue of p.
08:23:38.453510 StompWS.ttcn:366 Decode return result : 0
08:23:38.453533 StompWS.ttcn:367 Websocket response received : { fin_bit := '1'B, rsv1_bit := '0'B, rsv2_bit := '0'B, rsv3_bit := '0'B, opcode := Connection_Close (8), mask_bit := '0'B, payload_len := 2, masking_key := omit, payload_data := { close_data := { status_code := 1000, data := omit } } }
08:23:38.453575 StompWS.ttcn:375 p: f__IPL4__close: proto { unspecified := { } } connId 1
08:23:38.453602 StompWS.ttcn:376 close result{ errorCode := ERROR_INVALID_INPUT_PARAMETER (2), connId := omit, os_error_code := omit, os_error_text := omit }
08:23:38.453633 StompWS.ttcn:378 setverdict(pass): none -> pass
08:23:38.453665 StompWS.ttcn:378 Terminating component type StompWS.GeneralComp.
08:23:38.453683 StompWS.ttcn:378 Stop timer t: 1 s
08:23:38.453699 StompWS.ttcn:378 Removing unterminated mapping between port p and system:p.
08:23:38.453739 StompWS.ttcn:378 Port p was unmapped from system:p.
08:23:38.453765 StompWS.ttcn:378 Message with id 5 was extracted from the queue of p.
08:23:38.453780 StompWS.ttcn:378 Port p was stopped.
08:23:38.453807 StompWS.ttcn:378 Component type StompWS.GeneralComp was shut down inside testcase TC_websocket.
08:23:38.453823 StompWS.ttcn:378 Waiting for PTCs to finish.
08:23:38.454063 StompWS.ttcn:378 Setting final verdict of the test case.
08:23:38.454091 StompWS.ttcn:378 Local verdict of MTC: pass
08:23:38.454111 StompWS.ttcn:378 No PTCs were created.
08:23:38.454126 StompWS.ttcn:378 Test case TC_websocket finished. Verdict: pass
08:23:38.454144 StompWS.ttcn:382 Execution of control part in module StompWS finished.
08:23:38.461415 - Verdict statistics: 0 none (0.00 %), 1 pass (100.00 %), 0 inconc (0.00 %), 0 fail (0.00 %), 0 error (0.00 %).
08:23:38.461463 - Test execution summary: 1 test case was executed. Overall verdict: pass
08:23:38.461477 - Exit was requested from MC. Terminating MTC.
The message sent by the TTCN-3 code appears in the browser chat.
Note1. As seen from the log, closing the TCP(TLS) connection results in an error:
close result{ errorCode := ERROR_INVALID_INPUT_PARAMETER (2), connId := omit, os_error_code := omit, os_error_text := omit }
The TCP close is preceeded by a Websocket close , which is acknowledged by the other end and also causes the TCP conection to be closed
from the broker side.
Note2. If someone uses a Titan compiled from the latest source, should use the new compatibility switch -e
compiler -h
compiler: invalid option -- 'h'
compiler: error: No input TTCN-3 or ASN.1 module was given.
usage: compiler [-abcdDeEfgijlLMnNOpqrRsStuwxXyY] [-J file] [-K file] [-z file] [-V verb_level]
[-o dir] [-U none|type|'number'] [-P modulename.top_level_pdu_name] [-Q number] ...
[-T] module.ttcn [-A] module.asn ...
or compiler -v
or compiler --ttcn2json [-jf] ... [-T] module.ttcn [-A] module.asn ... [- schema.json]
OPTIONS:
-a: force XER in ASN.1 files
-b: disable BER encoder/decoder functions
-B: allow selected union field to be unbound (legacy behavior)
-c: write out checksums in case of error
-d: treat default fields as omit
-D: disable user and time information generation in the generated files
-e: enforce legacy handling of 'encode' and 'variant' attributes
-E: display only warnings for unrecognized encoding variants
-f: force overwriting of output files
-g: emulate GCC error/warning message format
-i: use only line numbers in error/warning messages
-j: disable JSON encoder/decoder functions
-J file: read input files from file
-K file: enable selective code coverage
-l: include source line info in C++ code
-L: add source line info for logging
-M: allow 'omit' in template value lists (legacy behavior)
-n: activate debugger (generates extra code for debugging)
-N: ignore UNTAGGED encoding instruction on top level unions (legacy behaviour)
-o dir: output files will be placed into dir
-p: parse only (no semantic check or code generation)
-P pduname: define top-level pdu
-q: suppress all messages (quiet mode)
-Qn: quit after n errors
-r: disable RAW encoder/decoder functions
-R: use function test runtime (TITAN_RUNTIME_2)
-s: parse and semantic check only (no code generation)
-S: suppress context information
-t: generate Test Port skeleton
-u: duplicate underscores in file names
-U none|type|'number': select code splitting mode for the generated C++ code
-V verb_level: set verbosity level bitmask (decimal)
-w: suppress warnings
-x: disable TEXT encoder/decoder functions
-X: disable XER encoder/decoder functions
-y: disable subtype checking
-Y: enforce legacy behaviour for "out" function parameters (see refguide)
-z file: enable profiling and code coverage for the TTCN-3 files in the argument
-T file: force interpretation of file as TTCN-3 module
-A file: force interpretation of file as ASN.1 module
-v: show version
--ttcn2json: generate JSON schema from input modules
JSON schema generator options:
-j: only include types with JSON encoding
-f: only generate references to types with JSON encoding/decoding functions
in the Makefile.
# Flags for the TTCN-3 and ASN.1 compiler:
COMPILER_FLAGS = -L -e
For earlier releases, this switch is not understood and should be removed.
Very important note 3.
Usually the code I post here is not of industrial robustness (e.g. not all negative situations are treated etc.), but an attempt to demonstrate various new features,
so I try to keep the code at a minimum.
However the code posted here is uglier than usual, and for a reason: the user has to face and deal with three protocol layers mixed in the same code : TCP/TLS, Websocket and STOMP;
It would be desirable that the user should deal with only one protoocl layer that is of interest, and all the lower layers are hidden.
Dealing with TCP and Websocket are brought from the necessity of using the IPL4 test port in lack of a dedicated Websocket test port; if we would have a dedicated Websocket test port then
we could focus on STOMP or whatever protocol layer sits on top of Websocket.
This s a classical architectural problem with a classical solution: we could use the dual-faced test port concept , or (recommended)
a translation port that appears as a Websocket tets port from above , but uses the infrastructure offered by the IPL4 port code.
And this is what we will do next, in the second part of this article.
Code attached as usual.
Best regards
Elemer
[Updated on: Thu, 31 August 2017 12:02] Report message to a moderator
|
|
| | |
Goto Forum:
Current Time: Mon Oct 14 09:37:06 GMT 2024
Powered by FUDForum. Page generated in 0.05617 seconds
|