Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » Eclipse Titan » JSON in Titan IV, usage of IPL4 test port and extracting messages from a TCP stream(Using JSON as an abstract interface to connect Titan to an external language environment)
JSON in Titan IV, usage of IPL4 test port and extracting messages from a TCP stream [message #1709257] Fri, 25 September 2015 11:48
Elemer Lelik is currently offline Elemer LelikFriend
Messages: 1120
Registered: January 2015
Senior Member

This is a lengthy article - also the last one in the JSON series ( I promise) - but it covers several subjects: first, an RPC-like usage of JSON as an abstract API; then , the usage of the IPL4 test port , which can be configured for UDP, TCP, and SCTP transport; and lastly, its' usage peculiarities for TCP (valid for the other, "standard" TCP port as well).


JSON, due to its' lightweight nature and ubiquity can be also used as an abstract interface between TTCN-3/Titan and any other JSON-friendly language environment.

The attached code contain an example of a web server where the transport (test port and adaptation layer ) is implemented with Titan components, while the "business logic" , for demonstration's sake, in Python ( although JAVA or any other language can be used). The two parts are connected using JSON messages sent back and forth over a TCP channel.

As the test port expects a set of messages as specified in HTTPmsg_Types.ttcn, which is part of the test port, we need to somehow publish the set of messages ( let's call them an API for the sake of simplicity); The easiest way to publish this API is to generate a JSON schema from the TTCN-3 module and distribute it to parties with whom we want to communicate with, telling them that we understand the messages described here. This schema can be conveniently generated from the TTCN-3 type descriptions using the --ttcn2json option.

To trigger the generation of JSON codecs by Titan, we need to decorate the standard HTTPmsg_Types.ttcn module that is coming with the HTTP test port with the

with {
encode "JSON";
}

instruction.

This architecture can be used whenever TTCN-3/Titan is being used in a hybrid environment. The simplest example is one of a customized, application-specific GUI implemented say in JAVA or Python that connects to Titan.


I think the usage of the TCP test port is worth a few extra words here. First of all, the IPL4 port is being used in TCP server (listener) mode :



  map(self:TCP_PCO, system:TCP_PCO);



  var IPL4asp_Types.Result  vl_result; 

  vl_result := { errorCode := omit, connId  := omit, os_error_code:=omit, os_error_text:= omit };
  vl_result :=f_IPL4_listen(
    TCP_PCO,
    "",
    tsp_ServerListenerPort,
    {tcp:={}},
    {}
  )


  log(vl_result)


  if (ispresent(vl_result.connId))  {   log("ready");   } 
  else   {   log("Server could not bind to listener port"); stop; } 



where tsp_ServerListenerPort is declared as a module parameter:

 integer tsp_ServerListenerPort:=6222;



after this code executed , the TCP server starts to listen on port 6222.

When the TCP client (in our case the business logic written in python) opens the connection, the getMsg_Func function is activated , and a "ready" prompt is sent to the client.

 while(v_run)
  {
    alt{
:
:
:

      []TCP_PCO.receive(t_ASP_Event) -> value vl_rcvdASP_Event;//connection opened by a client
      {
        v_tcp_state:=active;
        vl_tcp_client_id:=vl_rcvdASP_Event.connOpened.connId;

        var f_IPL4_getMsgLen getMsg_Func := refers(f_GetMsgLengthJSON);
        f_IPL4_setGetMsgLen(TCP_PCO,vl_tcp_client_id, getMsg_Func, {});

        f_send_char_TCP("{\"ready\" : \"ready\"}\r");

      }

 
    }//endalt
  }//endwhile
 



The TCP protocol is stream oriented, meaning that the API gives us the ability to send or receive a byte stream. There is no preservation of message boundaries. TCP can bundle up data from many send() calls into one segment, or it could break down data from one send() call into many segments - but that's transparent to applications sitting on top of TCP,
and recv() just gives you back data, with no relation to how many send() calls produced the data you get back. To be able to extract messages in general , and JSON messages in particular, a TTCN-3 or external C++ function has to be used to find the message boundary in streams;
This is implemented as a function reference registration in the test port. The function is declared for the test port, but the user has to implement it depending on what session protocol is used.

In our case , the getMsg_Func plays this role; it refers the function f_GetMsgLengthJSON declared in JSON_common.ttcn , which calls
the external function f_calc_JSON_length implemented in JSON_slicer.cc.


On the Python side, exactly the same function is implemented with json_split in json_split_mod.py.


The code works as described below:

Unpack it to a directory JSON_RPC

cd JSON_RPCc
cd bin
../src/install.script
make


(The Makefile was generated with makefilegen -s -e JSON_RPC *.ttcn *.cc *.hh)

Start the Titan test port:

./JSON_RPC JSON.cfg


This maps the HTTP port to the south , opens a TCP listener port to the north and starts a loop which handles messages both from north or south.

python port.py


This opens a TCP socket to the same port where the Titan part listens , then starts the python web server (serverloop0() )
The first thing the loop does is to send a listen message in JSON form :

message = "{ \"listen\" :   { \"local_hostname\" :  \"localhost\" , \"portnumber\" : 6666 ,  \"use_ssl\" : false  } }"



this is received by Titan on the TCP channel (mind that TCP is a stream so you need json_split_mod.py to extract JSON messages from the stream based on delimiters in the Python part and JSON_slicer.cc in the Titan part)

and converted to the corresponding TTCN-3 form:

1:00:08.263144 JSON_RPC_Test.ttcn:835 Message with id 2 was extracted from the queue of TCP_PCO.
11:00:08.263183 JSON_RPC_Test.ttcn:843 "{ \"listen\" :   { \"local_hostname\" :  \"localhost\" , \"portnumber\" : 6666 ,  \"use_ssl\" : false  } }"     ----------This is JSON
11:00:08.263494 JSON_RPC_Test.ttcn:854 Sent on HTTP_PCO to system @HTTPmsg_Types.Listen : { local_hostname := "localhost", portnumber := 6666, use_ssl := false }  -------------------This is TTCN-3


And sent to the HTTP port as a port message.
So the port will start to listen on the indicated port(6666).


Now if you direct a curl GET to http://127.0.0.1:6666/ ,
the web server will respond as below:

curl -v localhost:6666/
* About to connect() to localhost port 6666 (#0)
*   Trying 127.0.0.1... connected
* Connected to localhost (127.0.0.1) port 6666 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.19.0 (x86_64-suse-linux-gnu) libcurl/7.19.0 OpenSSL/0.9.8h zlib/1.2.3 libidn/1.10
> Host: localhost:6666
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: Python content served on Titan HTTP
< X-Powered-By: Titan
< Content-Type: text/html
* no chunk, no close, no size. Assume close to signal end
<
<!DOCTYPE html><html>
<body>

<h1>Python web content</h1>

<p>served on Titan HTTP</p>

</body>
* Closing connection #0



In the Python console:

{
        "client_connected" : {
                "hostname" : "127.0.0.1",
                "portnumber" : 37546,
                "client_id" : 12
        }
}
{
        "hTTPMessage" : {
                "request" : {
                        "client_id" : 12,
                        "method" : "GET",
                        "uri" : "/",
                        "version_major" : 1,
                        "version_minor" : 1,
                        "header" : [
                                {
                                        "header_name" : "User-Agent",
                                        "header_value" : "curl/7.19.0 (x86_64-suse-linux-gnu) libcurl/7.19.0 OpenSSL/0.9.8h zlib/1.2.3 libidn/1.10"
                                },
                                {
                                        "header_name" : "Host",
                                        "header_value" : "localhost:6666"
                                },
                                {
                                        "header_name" : "Accept",
                                        "header_value" : "*/*"
                                }
                        ],
                        "body" : ""
                }
        }
}
http response sent

close sent

)


Similarly , if you direct a browser(for me, it works with Firefox, not with Chrome for some reason) to http://127.0.0.1:6666/ , the browser will send a GET message to the port,

the dialogue can be followed in the Titan log:


Message enqueued on HTTP_PCO from system @HTTPmsg_Types.HTTPMessage : 
{ request := { client_id := 8, method := "GET", uri := "/", version_major := 1, version_minor := 1, header := { { header_name := "Host", header_value := "127.0.0.1:6666" }, { header_name := "User-Agent", header_value := "Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12" }, { header_name := "Accept", header_value := "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" }, { header_name := "Accept-Language", header_value := "en-us,en;q=0.5" }, { header_name := "Accept-Encoding", header_value := "gzip,deflate" }, { header_name := "Accept-Charset", header_value := "ISO-8859-1,utf-8;q=0.7,*;q=0.7" }, { header_name := "Keep-Alive", header_value := "115" }, { header_name := "Connection", header_value := "keep-alive" } }, body := "" } } id 3



This is converted to JSON and sent to the Python web server:


Sent on TCP_PCO to system @IPL4asp_Types.ASP_Send : { connId := 2, proto := { tcp := { } }, msg := '7B0A0922685454504D696422......097D0A097D0A7D'O 
("{
  \"hTTPMessage\" : {
    \"request\" : {
      \"client_id\" : 8,
      \"method\" : \"GET\",
      \"uri\" : \"/\",
      \"version_major\" : 1,
      \"version_minor\" : 1,
      \"header\" : 
[
        {
          \"header_name\" : \"Host\",
          \"header_value\" : \"127.0.0.1:6666\"
        },
        {
          \"header_name\" : \"User-Agent\",
          \"header_value\" : \"Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12\"
        },
        {
          \"header_name\" : \"Accept\",
          \"header_value\" : \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\"
        },
        {
          \"header_name\" : \"Accept-Language\",
          \"header_value\" : \"en-us,en;q=0.5\"
        },
        {
          \"header_name\" : \"Accept-Encoding\",
          \"header_value\" : \"gzip,deflate\"
        },
        {
          \"header_name\" : \"Accept-Charset\",
          \"header_value\" : \"ISO-8859-1,utf-8;q=0.7,*;q=0.7\"
        },
        {
          \"header_name\" : \"Keep-Alive\",
          \"header_value\" : \"115\"
        },
        {
          \"header_name\" : \"Connection\",
          \"header_value\" : \"keep-alive\"
        }
      ],
      \"body\" : \"\"
    }
  }
}") }


The web server will respond in JSON format , which is sent on TCP, recoded to TTCN-3 , sent to the browser etc. etc.



On different URLs the web server will serve different content accordingly:

    if (uri =="/"):
            body =  "<!DOCTYPE html><html>
<body>

<h1>Python web content</h1>

<p>served on Titan HTTP</p>

</body>
</html>"
        elif  (uri == "/titan"):
            body =  "<!DOCTYPE html><html>
<body>

<h1>Titan interworking demo</h1>

<p>using JSON serialization</p>

</body>
</html>"
        elif (uri == "/secret"):
            body =  myhtml
        elif (uri == "/clock"):
            body =  clock            
        elif (uri == "/time"):      
            body = data.replace("'date-time' : ''","'date-time' : '" + datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S') +"'" )  
   
        else:       
            body =  "<!DOCTYPE html><html>
<body>

<h1>Nothing here</h1>

<p>Move away</p>

</body>
</html>"   







In conclusion, the TTCN-3/Titan part uses JSON to talk with the Python part; similarly, all TTCN-3 protocol modules ( or the upper layer of TTCN-3 protocol stack) can be simply encoded in JSON hence all can expose a JSON API to any external party which will send and receive JSON messages.





Best regards

Elemer



  • Attachment: JSON_RPC.tgz
    (Size: 902.20KB, Downloaded 175 times)
Previous Topic:Titan course material
Next Topic:The binary RAW encoder of Titan : part I
Goto Forum:
  


Current Time: Sat Apr 20 02:54:32 GMT 2024

Powered by FUDForum. Page generated in 0.03211 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top