Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » Eclipse Titan » gRPC/ProtoBuff support in Titan
gRPC/ProtoBuff support in Titan [message #1783714] Fri, 16 March 2018 08:19
Elemer Lelik is currently offline Elemer LelikFriend
Messages: 1120
Registered: January 2015
Senior Member
Dear all,

This post is a synthesis between a number of previously published posts:

ProtoBuff support (https://www.eclipse.org/forums/index.php/t/1069134/),
HTTP/2 support (https://www.eclipse.org/forums/index.php/t/1089118/)
and a number of posts about translation ports.


gRPC is a high performance, open-source universal RPC framework, see https://grpc.io.
As such, it presents many analogies with REST , as both gRPC and REST use the request/response
HTTP protocol to send and receive data. REST as an architectural style implements HTTP protocol between a client and a
server through a set of constraints, typically a method (GET) and a resource or endpoint.
RPC implements client and server stubs that essentially make it possible to make calls to
procedures over a network address as if the procedure was local.
gRPC is built on HTTP/2 hence benefits of features such
as bidirectional streaming, flow control, header compression and multiplexing requests.
gRPC's default serialization protocol, Protocol Buffer, also transmits data in binary format
which is smaller and faster as compared to JSON or XML.
Protocol buffer's latest version proto3 makes it easy to
define services and automatically generate client libraries.

gRPC over HTTP/2 is described in detail in:
https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md

As a starting point I will use this article:

https://scotch.io/tutorials/implementing-remote-procedure-calls-with-grpc-and-protocol-buffers#when-and-why-to-use-rpc
which also contains a good explanation of gRPC.

Let's start with installing the Node.js server and client as detailed in the article.
When done, let's start the server

node server
grpc server running on port: 0.0.0.0:50050

and run the client against it:
node client/node
{ granted: true, accrued_leave_days: 6, granted_leave_days: 4 }


At this point maybe it's a good idea to take a Wireshark trace of the client-server communication;
You will need at least Wireshark 2.5.0 to be able to dissect gRPC; at the end of this post
https://www.eclipse.org/forums/index.php/t/1089118/
there's an entry by Gustavo Gonnet explaining how to compile and install Wireshark 2.5.0 on Ubuntu.


OK, now that we understand what's going on on a network level, let's try to replicate the client functionality in Titan.

To start with, we need to generate the ProtoBuff TTCN-3 files from the .proto file in the above article
(the protocol module generator has been recently updated for proto3):

./PBPMG.sh work_leave.proto

where work_leave.proto is:
syntax = "proto3"; //Specify proto3 version.

package work_leave; //Optional: unique package name.

//Service. define the methods that the grpc server can expose to the client.
service EmployeeLeaveDaysService {
  rpc EligibleForLeave (Employee) returns (LeaveEligibility);
  rpc grantLeave (Employee) returns (LeaveFeedback);
}

// Message Type fefinition for an Employee.
message Employee {
  int32 employee_id = 1;
  string name = 2;
  float accrued_leave_days = 3;
  float requested_leave_days = 4;
}

// Message Type definition for LeaveEligibility response.
message LeaveEligibility {
  bool eligible = 1;
}

// Message Type definition for LeaveFeedback response.
message LeaveFeedback  {
  bool granted = 1;
  float accrued_leave_days = 2;
  float granted_leave_days = 3;
}


As a result, work_leave.ttcn , and the codecs, work_leave_EncDec.cc,
work_leave_EncDec.hh will be generated; work_leave.ttcn has the below content:

// TTCN-3 module generated from work_leave.proto                                         
module work_leave {                                                                      

// imports
  import from ProtoBuff_Types all;

// public imports

// encoder/decoder function declaration

  external function f_encode_work_leave_LeaveEligibility(in work_leave_LeaveEligibility pdu) return octetstring
  external function f_decode_work_leave_LeaveEligibility(in octetstring pdu) return work_leave_LeaveEligibility

  external function f_encode_work_leave_Employee(in work_leave_Employee pdu) return octetstring
  external function f_decode_work_leave_Employee(in octetstring pdu) return work_leave_Employee

  external function f_encode_work_leave_LeaveFeedback(in work_leave_LeaveFeedback pdu) return octetstring
  external function f_decode_work_leave_LeaveFeedback(in octetstring pdu) return work_leave_LeaveFeedback


// definitions for enums


// definitions for messages

  type record work_leave_LeaveEligibility{ // LeaveEligibility
    bool   eligible optional
  }
  type record work_leave_Employee{ // Employee
    int32   employee_id optional,
    string   name optional,
    float   accrued_leave_days optional,
    float   requested_leave_days optional
  }
  type record work_leave_LeaveFeedback{ // LeaveFeedback
    bool   granted optional,
    float   accrued_leave_days optional,
    float   granted_leave_days optional
  }

}

The ProtoBuf messages declared here will be used, after serialization, as binary payloads for HTTP/2 messages,
with message headers set appropriately as described in https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md


And here's the TTCN-3 code we will be using:



module HTTP2   {


modulepar {

  charstring  tsp_hostname:="localhost"; 
  charstring  tsp_url1:="/work_leave.EmployeeLeaveDaysService/EligibleForLeave" ;
  charstring  tsp_url2:="/work_leave.EmployeeLeaveDaysService/grantLeave" ;
  charstring  tsp_port:="50050";
}

import from IPL4asp_Types all;
import from IPL4asp_PortType all;
import from HTTP2_Types all;
import from work_leave  all;
import from IPL4asp_User_CtrlFunct all;

//*************************************************************************
function f_HTTP2_Message_len(  in octetstring stream,   inout ro_integer args) return integer
//*************************************************************************
{    
  return f_HTTP2_msglen(stream, args) ;                 
}

//*************************************************************************
type port HTTP2Port  message map to IPL4asp_PT {
//*************************************************************************
  out 	        HTTP2_Frames	to	ASP_Send     with f_enc_HTTP2_Frames_to_ASPSend(),
  		octetstring	to	ASP_Send     with f_enc_octetstring_to_ASPSend(),
  		integer       	to      ASP_Send     with f_set_connId() 

  in 	        HTTP2_Frame    from 	ASP_RecvFrom with f_dec_ASPRecvFrom_to_HTTP2_Frame(),
  		ASP_Event      from	ASP_Event    with  f_discardASPEvent()


  var HTTP2_Frame v_HTTP2_Frame;	
  var integer v_cid;
  var HTTP2_decoder_error_descr v_err;

} with {extension "internal" }



//*************************************************************************
function f_enc_HTTP2_Frames_to_ASPSend(in HTTP2_Frames p_HTTP2_Frames, out ASP_Send p_asp_Send)   port HTTP2Port
{
//*************************************************************************
  p_asp_Send.msg:=''O;
  p_asp_Send.connId:=v_cid; 
  p_asp_Send.proto:=omit;
  for (var integer i:=0;i<sizeof(p_HTTP2_Frames);i:=i+1 )
  {
    p_asp_Send.msg:=p_asp_Send.msg&f_HTTP2_encode_frame(p_HTTP2_Frames[i]);
  }
  port.setstate(0); 
}with {extension "prototype(fast)"}
//*************************************************************************
function f_enc_octetstring_to_ASPSend(in octetstring p_os, out ASP_Send p_asp_Send)   port HTTP2Port
//*************************************************************************
{


  p_asp_Send.connId:=v_cid;
  p_asp_Send.proto:=omit;
  p_asp_Send.msg:=p_os;

  port.setstate(0); 
}with {extension "prototype(fast)"}



//*************************************************************************
function f_dec_ASPRecvFrom_to_HTTP2_Frame(in ASP_RecvFrom p_aspRecvFrom, out HTTP2_Frame p_HTTP2_Frame) port HTTP2Port
//*************************************************************************
{

  f_HTTP2_decode_frame(p_aspRecvFrom.msg, v_HTTP2_Frame, v_err) ;
  //log("v_HTTP2_Frame: ",v_HTTP2_Frame)
  //log("v_err: ",v_err)
  p_HTTP2_Frame:=v_HTTP2_Frame;

  port.setstate(0); 
}with {extension "prototype(fast)"}

//*************************************************************************
function f_discardASPEvent(in ASP_Event p_aspEventIn, out ASP_Event p_aspEventOut) port HTTP2Port
//*************************************************************************
{

  log("ASP_Event received: ", p_aspEventIn)
  port.setstate(4);
}with {extension "prototype(fast)"}

//*************************************************************************
function f_set_connId(in integer p_in, out ASP_Send  p_out) port HTTP2Port
//*************************************************************************
{
  v_cid:=p_in; //save connection Id
  port.setstate(4);
}with {extension "prototype(fast)"}


type component GeneralComp
{
  
  
  var IPL4asp_Types.Result  vl_result;
  var octetstring v_data:=''O, v_msg:=''O, v_os, v_os0;
  var ASP_RecvFrom v_ASP_RecvFrom;
  var HTTP2_Frame v_HTTP2_Frame;	
  var integer v_cid, v_ret;
  var HTTP2_decoder_error_descr v_err;
  var HTTP2_comp_context v_HTTP2_comp_context;

  port HTTP2Port  http2_port;
  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 ipl4_port;
}


type record of HTTP2_Frame  HTTP2_Frames;



template  HTTP2_Frame t_HTTP2_init_settings_frame:= {
  settings_frame :={
    ack_flag :=false,
    settings := {
      {
        setting_id:=2, //SETTINGS_ENABLE_PUSH
        setting_value:=0
      },
      {
        setting_id:=3, //SETTINGS_MAX_CONCURRENT_STREAMS
        setting_value:=0
      },
      {
        setting_id:=4,//SETTINGS_INITIAL_WINDOW_SIZE
        setting_value:=4194304
      },
      {
        setting_id:=5,//SETTINGS_MAX_FRAME_SIZE
        setting_value:=4194304
      },
      {
        setting_id:=6,//SETTINGS_MAX_HEADER_LIST_SIZE
        setting_value:=8192
      },
      {
        setting_id:=65027,//Unknown
        setting_value:=1
      }
    }

  }
}




template  HTTP2_Frame t_HTTP2_window_update_frame( in  template integer   p_stream, in template integer  p_incr)  := {
  window_update_frame :={
    stream_id :=p_stream,
    window_size_increment := p_incr
  }
}

template  HTTP2_Frame t_HTTP2_ping_frame(in template boolean p_ack ):= {
  ping_frame :={
    ack_flag :=p_ack,//false,
    opaque_data := '0000000000000000'O
  }
}


//---------------------------------------------------------------


template  HTTP2_Frame t_HTTP2_init_settings_frame_r(in template boolean p_ack_flag, in template HTTP2_Setting_list p_set  ):= {
  settings_frame :={
    ack_flag :=p_ack_flag,
    settings := p_set
  }
}



template  HTTP2_header_block t_HTTP2_header_block(in charstring p_path):= {
  pseudo_headers :={
    method:="POST",
    scheme:="http",
    authority:=tsp_hostname&":"&tsp_port,
    path:=p_path,
    status:=omit
  },
  headers :={
    {
      header_name:="te",
      header_value:="trailers"
    },
    {
      header_name:="content-type",
      header_value:="application/grpc"
    },
    {
      header_name:="user-agent",
      header_value:="titan"
    },
    {
      header_name:="grpc-accept-encoding",
      header_value:="identity,deflate,gzip"
    },
    {
      header_name:="accept-encoding",
      header_value:="identity,gzip"
    }
  }
}


template  HTTP2_Frame t_HTTP2_data_frame(in integer p_stream, in template octetstring pdu):= {
  data_frame :={
    stream_id:=p_stream,
    end_stream_flag :=true,
    data:=pdu,
    padding:=omit
  }
}


template  HTTP2_Frame t_HTTP2_data_frame_r(in boolean p_esf, in template octetstring pdu):= {
  data_frame :={
    stream_id:=?,
    end_stream_flag :=p_esf,
    data:=pdu,
    padding:=omit
  }
}

template  HTTP2_Frame t_HTTP2_header_frame(in integer p_stream, in octetstring pdu):= {
  header_frame:=
  {
    stream_id:=p_stream,
    end_stream_flag :=false,
    end_header_flag :=true,
    priority_data:=omit,
    header_block_fragment:=pdu,
    padding:=omit
  }

}


template  HTTP2_Frame t_HTTP2_header_frame_r(in boolean p_esf, in boolean p_ehf, in template octetstring pdu):= {
  header_frame:=
  {
    stream_id:=?,
    end_stream_flag :=p_esf,
    end_header_flag :=p_ehf,
    priority_data:=*,
    header_block_fragment:=pdu,
    padding:=*
  }

}


template work_leave_Employee t_work_leave_Employee:={

  employee_id:=1,
  name:="Master Jones",
  accrued_leave_days:=10.0,
  requested_leave_days:=4.0

} 


//--------------------------------------------------------------------------
altstep as_receive() runs on GeneralComp system SystemComp {
  //--------------------------------------------------------------------------

  []http2_port.receive(t_HTTP2_ping_frame(?)) { repeat;}


}//endaltstep




//--------------------------------------------------------------------------
testcase TC_grpc()  runs on GeneralComp system SystemComp {
//--------------------------------------------------------------------------



  map(self:http2_port, system:ipl4_port); 



  var default v_myDefVar := activate(as_receive()); 



  vl_result :=IPL4asp_User_CtrlFunct.f_IPL4_connect( http2_port, tsp_hostname, 50050,"",0, -1, {tcp := {} }, {} );

  log("connect result",vl_result)

  if (not(ispresent(vl_result.connId)))  {  log("Could not connect");   stop;  } 
  v_cid:=vl_result.connId ;


  http2_port.send(v_cid);// send connId to the port

  //--------------------------------------------------------------------------

  //register message length function of HTTP/2 with IPL4:

  //--------------------------------------------------------------------------
  var f_IPL4_getMsgLen getMsg_Func := refers(f_HTTP2_Message_len);
  IPL4asp_User_CtrlFunct.f_IPL4_setGetMsgLen(http2_port,v_cid, getMsg_Func, {});
  //--------------------------------------------------------------------------


  //init compression context
  v_HTTP2_comp_context:=HTTP2_comp_context_init();




  interleave
  { 
    [] http2_port.receive(t_HTTP2_init_settings_frame_r(false, *)) -> value v_HTTP2_Frame { }

    [] http2_port.receive(t_HTTP2_window_update_frame(?,?)) -> value v_HTTP2_Frame { }

    [] http2_port.receive(t_HTTP2_ping_frame(false)) -> value v_HTTP2_Frame {}	

  }   


  // send connection preface "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
  http2_port.send(HTTP2_connection_preface);

  // send settings, window update , ping

  var HTTP2_Frames v_frames:={ 
    valueof(t_HTTP2_init_settings_frame), 
    valueof(t_HTTP2_window_update_frame(0,4128769)), 
    valueof(t_HTTP2_ping_frame(false))
  }

  http2_port.send(v_frames) ;


  // send HEADER frame
  HTTP2_comp_context_encode(v_HTTP2_comp_context,valueof(t_HTTP2_header_block(tsp_url1)), v_data )
  //send DATA frame (encoded protobuf) 
  v_os:=f_encode_work_leave_Employee(valueof(t_work_leave_Employee))
  v_os:='00'O&int2oct(lengthof(v_os),4)&v_os //add grpc not-compressed + length

  log("protobuf message to be sent:  ", t_work_leave_Employee)


  v_frames:={ 
    valueof(t_HTTP2_header_frame(1,v_data)), 
    valueof(t_HTTP2_window_update_frame(1,5)), 
    valueof(t_HTTP2_data_frame(1,v_os)),
    valueof(t_HTTP2_window_update_frame(0,5))
  }


  http2_port.send(v_frames) ;



  // send ping
  // send settings ?
  v_frames:={ 
    valueof(t_HTTP2_ping_frame(true)),
    valueof(t_HTTP2_init_settings_frame_r(true, omit)) 
  }

  http2_port.send(v_frames) ;


  interleave


  { 

    [] http2_port.receive(t_HTTP2_ping_frame(?)) { }

    [] http2_port.receive(t_HTTP2_init_settings_frame_r(true, *)) -> value v_HTTP2_Frame { }

    [] http2_port.receive(t_HTTP2_header_frame_r(false,true,?)) -> value v_HTTP2_Frame { }

    [] http2_port.receive(t_HTTP2_header_frame_r(true,true,?)) -> value v_HTTP2_Frame { }

    [] http2_port.receive(t_HTTP2_window_update_frame(0,?)) -> value v_HTTP2_Frame { }

    [] http2_port.receive(t_HTTP2_data_frame_r(false,?)) -> value v_HTTP2_Frame {


      //log("-------------------->",v_HTTP2_Frame.data_frame.data)

      v_os0:=substr(v_HTTP2_Frame.data_frame.data,5,oct2int(substr(v_HTTP2_Frame.data_frame.data,1,4 )))
      log("------------------------------------------------------------")
      log("protobuf reply received:   ",f_decode_work_leave_LeaveEligibility (v_os0))
      log("------------------------------------------------------------")
      //'00 00 00 00 02 0801'O

    }	

  }//end interleave   

  //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


  // send HEADER frame
  HTTP2_comp_context_encode(v_HTTP2_comp_context,valueof(t_HTTP2_header_block(tsp_url2)), v_data )
  //send DATA frame (encoded protobuf) 
  v_os:=f_encode_work_leave_Employee(valueof(t_work_leave_Employee))
  v_os:='00'O&int2oct(lengthof(v_os),4)&v_os //add grpc not-compressed + length

  log("protobuf message to be sent:  ", t_work_leave_Employee)


  v_frames:={ 
    valueof(t_HTTP2_header_frame(3,v_data)), 
    valueof(t_HTTP2_window_update_frame(3,5)), 
    valueof(t_HTTP2_data_frame(3,v_os)),
    valueof(t_HTTP2_window_update_frame(0,7))
  }

  http2_port.send(v_frames) ;

  interleave
  { 

    [] http2_port.receive(t_HTTP2_header_frame_r(false,true,?)) -> value v_HTTP2_Frame { }

    [] http2_port.receive(t_HTTP2_header_frame_r(true,true,?)) -> value v_HTTP2_Frame { }

    [] http2_port.receive(t_HTTP2_window_update_frame(0,?)) -> value v_HTTP2_Frame { }

    [] http2_port.receive(t_HTTP2_data_frame_r(false,?)) -> value v_HTTP2_Frame {


      //log("-------------------->",v_HTTP2_Frame.data_frame.data)
       

       v_os0:=substr(v_HTTP2_Frame.data_frame.data,5,oct2int(substr(v_HTTP2_Frame.data_frame.data,1,4 )))
      log("------------------------------------------------------------")
      log("protobuf reply received:   ",f_decode_work_leave_LeaveFeedback (v_os0))
      log("------------------------------------------------------------")
    }	

  }//end interleave 

  HTTP2_comp_context_free(v_HTTP2_comp_context);

  //----------------------------------------------------------------------------

  vl_result :=    IPL4asp_User_CtrlFunct.f_IPL4_close(http2_port, v_cid);
  log("close result",vl_result); 

  deactivate(v_myDefVar); 
  unmap(self:http2_port, system:ipl4_port); 
  setverdict(pass);

}//endtestcase

control{
  execute(TC_grpc());

}//endcontrol



}//endmodule  


Lets' go through the more interesting parts:


1. The translation port

Here's the declaration:
//*************************************************************************
type port HTTP2Port  message map to IPL4asp_PT {
//*************************************************************************
  out 	HTTP2_Frames	to	ASP_Send     with f_enc_HTTP2_Frames_to_ASPSend(),
  		octetstring	to	ASP_Send     with f_enc_octetstring_to_ASPSend(),
  		integer       	to      ASP_Send     with f_set_connId() 

  in 	HTTP2_Frame    from 	ASP_RecvFrom with f_dec_ASPRecvFrom_to_HTTP2_Frame(),
  		ASP_Event      from	ASP_Event    with  f_discardASPEvent()


  var HTTP2_Frame v_HTTP2_Frame;	
  var integer v_cid;
  var HTTP2_decoder_error_descr v_err;

} with {extension "internal" }


as it is declared internal, the port skeleton will not have to be generated and used.

Expectedly it translates between HTTP2 frames and TCP ASPs. However, there's more to it:

-on the northern interface it also accepts octetstrings which will then be made the payload of a TCP message

//*************************************************************************
function f_enc_octetstring_to_ASPSend(in octetstring p_os, out ASP_Send p_asp_Send)   port HTTP2Port
//*************************************************************************
{
  p_asp_Send.connId:=v_cid;
  p_asp_Send.proto:=omit;
  p_asp_Send.msg:=p_os;
  port.setstate(0); 
}with {extension "prototype(fast)"}

a specific reason for this is the Magic message which initiates the conversation
( the "previous-knowledge" method is being used as described in https://www.eclipse.org/forums/index.php/t/1089118/ );
this message is declared as an octetstring:
 
 const octetstring HTTP2_connection_preface:='505249202a20485454502f322e300d0a0d0a534d0d0a0d0a'O

-also, on the northern interface integers are accepted; the reason for this is the following:

the translation ports are executing in their own variable space; they can have variables, timers etc. scoped to the port only
 
//*************************************************************************
type port HTTP2Port  message map to IPL4asp_PT {
//*************************************************************************
 :

  var HTTP2_Frame v_HTTP2_Frame;	
  var integer v_cid;
  var HTTP2_decoder_error_descr v_err;

} with {extension "internal" }

but they typically do not read/write the component variables of the component they are running on.

A TCP connection is initiated by calling f_IPL4_connect:
  vl_result :=IPL4asp_User_CtrlFunct.f_IPL4_connect( http2_port, tsp_hostname, 50050,"",0, -1, {tcp := {} }, {} );
:
  v_cid:=vl_result.connId ; 

the result returned contains the connection Id.

This id has to be communicated to the port, then saved into a port variable and reused later for the same connection.
This is done here by sending the connection id to the port as an outgoing message and saved:

  http2_port.send(v_cid);// send connId to the port


//*************************************************************************
function f_set_connId(in integer p_in, out ASP_Send  p_out) port HTTP2Port
//*************************************************************************
{
  v_cid:=p_in; //save connection Id
  port.setstate(4);
}with {extension "prototype(fast)"}



No outwards message is produced, as indicated by setstate(4), which signifies "discarded".

-unexpected ASP_Events coming in from the southern interface are also discarded:

//*************************************************************************
function f_discardASPEvent(in ASP_Event p_aspEventIn, out ASP_Event p_aspEventOut) port HTTP2Port
//*************************************************************************
{

  log("ASP_Event received: ", p_aspEventIn)
  port.setstate(4);
}with {extension "prototype(fast)"}


-if someone took a trace of the HTTP2 exchange may have noticed that HTTP2 frames travel not alone but in groups;
this is the reason the port expects not a single frame but a record of frames which are encoded one by one then concatenated and
made into a TCP payload:

 
//*************************************************************************
function f_enc_HTTP2_Frames_to_ASPSend(in HTTP2_Frames p_HTTP2_Frames, out ASP_Send p_asp_Send)   port HTTP2Port
{
//*************************************************************************
  p_asp_Send.msg:=''O;
  p_asp_Send.connId:=v_cid; 
  p_asp_Send.proto:=omit;
  for (var integer i:=0;i<sizeof(p_HTTP2_Frames);i:=i+1 )
  {
    p_asp_Send.msg:=p_asp_Send.msg&f_HTTP2_encode_frame(p_HTTP2_Frames[i]);
  }
  port.setstate(0); 
}with {extension "prototype(fast)"}

In receiving direction, the port will receive a payload consisting a of a number of glued HTTP2 frames, but will
know to slice them as we register the message length function against the connection id:

 
  //register message length function of HTTP/2 with IPL4:

  //--------------------------------------------------------------------------
  var f_IPL4_getMsgLen getMsg_Func := refers(f_HTTP2_Message_len);
  IPL4asp_User_CtrlFunct.f_IPL4_setGetMsgLen(http2_port,v_cid, getMsg_Func, {});
  //--------------------------------------------------------------------------


Hence, HTTP2 frames will be received one by one sequentially.



2. Interleave

As the order of frames seems to vary, I have used interleave when receiving:

 
   interleave
  { 
    [] http2_port.receive(t_HTTP2_init_settings_frame_r(false, *)) -> value v_HTTP2_Frame { }

    [] http2_port.receive(t_HTTP2_window_update_frame(?,?)) -> value v_HTTP2_Frame { }

    [] http2_port.receive(t_HTTP2_ping_frame(false)) -> value v_HTTP2_Frame {}	

  }   

This means that we expect all of the above three frames, but they may come in arbitrary order.

3. HTTP2 payload

the data in a data frame is preceded by a compressed indicator and a length
(see https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md ) ;

this has been simply added in front of the serialized protobuff data:
 
  v_os:='00'O&int2oct(lengthof(v_os),4)&v_os //add grpc not-compressed + length

respectively removed from it:
  v_os0:=substr(v_HTTP2_Frame.data_frame.data,5,oct2int(substr(v_HTTP2_Frame.data_frame.data,1,4 )))



Although the ProtoBuff payload sent in both requests is the same, its' significance is changed by the different HTTP2 POST headers
associated with it: the path changes from
"/work_leave.EmployeeLeaveDaysService/EligibleForLeave" 

to
"/work_leave.EmployeeLeaveDaysService/grantLeave"    

hence the responses will be different.


After compiling ad executing the code, this can be viewed in the log displayed on the console:



	   
 ttcn3_start ./http2 HTTP2.cfg
ttcn3_start: Starting the test suite
spawn /home/james00/titan.core/Install/bin/mctr_cli HTTP2.cfg

*************************************************************************
* TTCN-3 Test Executor - Main Controller 2                              *
* Version: CRL 113 200/6 R3B                                            *
* Copyright (c) 2000-2017 Ericsson Telecom AB                           *
* All rights reserved. This program and the accompanying materials      *
* are made available under the terms of the Eclipse Public License v1.0 *
* which accompanies this distribution, and is available at              *
* http://www.eclipse.org/legal/epl-v10.html                             *
*************************************************************************

Using configuration file: HTTP2.cfg
MC@GlobalWarning1: Unix server socket created successfully.
MC@GlobalWarning1: Listening on TCP port 8035.
GlobalWarning1 is the default
MC2> spawn ././http2 GlobalWarning1 8035
TTCN-3 Host Controller (parallel mode), version CRL 113 200/6 R3B
MC@GlobalWarning1: New HC connected from localhost [127.0.0.1]. GlobalWarning1: Linux 4.4.0-116-generic on x86_64.
cmtc
MC@GlobalWarning1: Downloading configuration file to all HCs.
MC@GlobalWarning1: Configuration file was processed on all HCs.
MC@GlobalWarning1: Creating MTC on host localhost.
MC@GlobalWarning1: MTC is created.
MC2> smtc
Executing all items of [EXECUTE] section.
MC2> MTC@GlobalWarning1: connect result{ errorCode := omit, connId := 1, os_error_code := omit, os_error_text := omit }
MTC@GlobalWarning1: protobuf message to be sent:  { employee_id := 1, name := "Master Jones", accrued_leave_days := 10.000000, requested_leave_days := 4.000000 }
MTC@GlobalWarning1: ------------------------------------------------------------
MTC@GlobalWarning1: protobuf reply received:   { eligible := true }
MTC@GlobalWarning1: ------------------------------------------------------------
MTC@GlobalWarning1: protobuf message to be sent:  { employee_id := 1, name := "Master Jones", accrued_leave_days := 10.000000, requested_leave_days := 4.000000 }
MTC@GlobalWarning1: ------------------------------------------------------------
MTC@GlobalWarning1: protobuf reply received:   { granted := true, accrued_leave_days := 6.000000, granted_leave_days := 4.000000 }
MTC@GlobalWarning1: ------------------------------------------------------------
MTC@GlobalWarning1: close result{ errorCode := omit, connId := 1, os_error_code := omit, os_error_text := omit }
MC@GlobalWarning1: Test execution finished.
Execution of [EXECUTE] section finished.
emtc
MC@GlobalWarning1: Terminating MTC.
MC@GlobalWarning1: MTC terminated.
MC2> exit
MC@GlobalWarning1: Shutting down session.
MC@GlobalWarning1: Shutdown complete.



All the code, Node.js and TTCN-3, plus the logs and traces are attached.




One possibly relevant question one may ask here:
the Node.js client has about 45 lines of code, while the TTCN-3 code is roughly ten times longer. Is the TTCN-3 language
that much worse/difficult to work with/performing poorly etc.?

There are two pertinent answers to this: first of all, what we are comparing is only the tips of the icebergs, most of the code
at work here is under the surface ( a lot less visible for Node.js, somewhat less visible for TTCN-3).
But the second part of the answer is that by using Titan and TTCN-3 the user can control, modify, twist and turn every bit of the messaging
involved, as that's what it is meant for; and complexity is the price paid for this flexibility. Of course the same flexibility can be achieved with Node.js or any other language as well, but in that case the Node.js or whatever code will inflate too accordingly.



All right then, so have fun experimenting with gRPC.

Best regards
Elemer

  • Attachment: traces.tgz
    (Size: 3.17KB, Downloaded 114 times)
  • Attachment: GRPC_0310.tgz
    (Size: 463.71KB, Downloaded 142 times)
  • Attachment: node.tgz
    (Size: 1.29KB, Downloaded 124 times)
  • Attachment: logs.tgz
    (Size: 4.25KB, Downloaded 152 times)

[Updated on: Fri, 16 March 2018 08:39]

Report message to a moderator

Previous Topic:Porting the ETSI IPv6 test suite to Titan part III
Next Topic:xsd2ttcn issue
Goto Forum:
  


Current Time: Thu Apr 25 17:51:33 GMT 2024

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

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

Back to the top